Browse Source

Merge pull request #79 from KMDLabs/mastertest

Merge MaterTest to master.
pull/27/head
blackjok3rtt 6 years ago
committed by GitHub
parent
commit
04f7f39d23
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      qa/pull-tester/cc-tests.sh
  2. 141
      qa/rpc-tests/cryptoconditions_channels.py
  3. 12
      qa/rpc-tests/cryptoconditions_oracles.py
  4. 51
      qa/rpc-tests/cryptoconditions_token.py
  5. 6
      src/Makefile.am
  6. 3
      src/bitcoind.cpp
  7. 2
      src/cc/CC made easy.md
  8. 18
      src/cc/CCHeir.h
  9. 19
      src/cc/CCMarmara.h
  10. 28
      src/cc/CCassets.h
  11. 502
      src/cc/CCassetsCore.cpp
  12. 608
      src/cc/CCassetstx.cpp
  13. 2
      src/cc/CCchannels.h
  14. 54
      src/cc/CCcustom.cpp
  15. 86
      src/cc/CCinclude.h
  16. 768
      src/cc/CCtokens.cpp
  17. 44
      src/cc/CCtokens.h
  18. 173
      src/cc/CCtx.cpp
  19. 242
      src/cc/CCutils.cpp
  20. 266
      src/cc/assets.cpp
  21. 2
      src/cc/auction.cpp
  22. 317
      src/cc/cclib.cpp
  23. 421
      src/cc/channels.cpp
  24. 20
      src/cc/eval.cpp
  25. 7
      src/cc/eval.h
  26. 2
      src/cc/fsm.cpp
  27. 153
      src/cc/gateways.cpp
  28. 1382
      src/cc/heir.cpp
  29. 667
      src/cc/heir_validate.h
  30. 2
      src/cc/lotto.cpp
  31. 1
      src/cc/makecclib
  32. 1079
      src/cc/marmara.cpp
  33. 2
      src/cc/payments.cpp
  34. 2
      src/cc/pegs.cpp
  35. 8
      src/cc/prices.cpp
  36. 20
      src/chainparams.cpp
  37. 8
      src/init.cpp
  38. 18
      src/komodo.h
  39. 497
      src/komodo_bitcoind.h
  40. 44
      src/komodo_defs.h
  41. 12
      src/komodo_gateway.h
  42. 27
      src/komodo_globals.h
  43. 10
      src/komodo_jumblr.h
  44. 108
      src/komodo_utils.h
  45. 16
      src/main.cpp
  46. 5
      src/metrics.cpp
  47. 207
      src/miner.cpp
  48. 2
      src/miner.h
  49. 2
      src/notaries_staked.cpp
  50. 7
      src/pow.cpp
  51. 14
      src/primitives/block.cpp
  52. 1
      src/primitives/block.h
  53. 2
      src/rpc/blockchain.cpp
  54. 13
      src/rpc/mining.cpp
  55. 6
      src/rpc/misc.cpp
  56. 32
      src/rpc/server.cpp
  57. 21
      src/rpc/server.h
  58. 327
      src/script/sign.cpp
  59. 26
      src/test/miner_tests.cpp
  60. 1
      src/wallet-utility.cpp
  61. 2
      src/wallet/db.h
  62. 566
      src/wallet/rpcwallet.cpp
  63. 8
      src/wallet/wallet.cpp
  64. 18
      src/wallet/walletdb.cpp
  65. 15
      zcutil/build-mac.sh
  66. 4
      zcutil/build.sh

3
qa/pull-tester/cc-tests.sh

@ -13,12 +13,13 @@ 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'
# TODO: cant reconnect nodes back in channels test because of crash (seems regtest only specific)
'cryptoconditions_channels.py'
);
extArg="-extended"

141
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]

12
qa/rpc-tests/cryptoconditions_oracles.py

@ -112,17 +112,15 @@ class CryptoconditionsOraclesTest(CryptoconditionsTestFramework):
# baton
oraclesdata_d = self.send_and_mine(result["hex"], rpc)
result = rpc.oraclessamples(globals()["oracle_{}".format("d")], oraclesdata_d, "1")
# TODO: working not correct now!
#assert_equal("[u'01']", str(result["samples"][0]), "Data match")
assert_equal("[u'01']", str(result["samples"][0]), "Data match")
# D type
result = rpc.oraclesdata(globals()["oracle_{}".format("D")], "0101")
result = rpc.oraclesdata(globals()["oracle_{}".format("D")], "010001")
assert_success(result)
# baton
oraclesdata_D = self.send_and_mine(result["hex"], rpc)
result = rpc.oraclessamples(globals()["oracle_{}".format("D")], oraclesdata_D, "1")
# TODO: working not correct now!
#assert_equal("[u'01']", str(result["samples"][0]), "Data match")
assert_equal("[u'01']", str(result["samples"][0]), "Data match")
# c type
result = rpc.oraclesdata(globals()["oracle_{}".format("c")], "ff")
@ -198,12 +196,12 @@ class CryptoconditionsOraclesTest(CryptoconditionsTestFramework):
assert_equal("[u'ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000']", str(result["samples"][0]), "Data match")
# Ihh type
result = rpc.oraclesdata(globals()["oracle_{}".format("Ihh")], "00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff")
result = rpc.oraclesdata(globals()["oracle_{}".format("Ihh")], "ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff")
assert_success(result)
# baton
oraclesdata_Ihh = self.send_and_mine(result["hex"], rpc)
result = rpc.oraclessamples(globals()["oracle_{}".format("Ihh")], oraclesdata_Ihh, "1")
assert_equal("[u'0']", str(result["samples"][0]), "Data match")
assert_equal("[u'4294967295', u'ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000', u'ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000']", str(result["samples"][0]), "Data match")
def run_test(self):

51
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()

6
src/Makefile.am

@ -292,6 +292,7 @@ libbitcoin_server_a_SOURCES = \
cc/CCcustom.cpp \
cc/CCtx.cpp \
cc/CCutils.cpp \
cc/CCtokens.cpp \
cc/assets.cpp \
cc/faucet.cpp \
cc/rewards.cpp \
@ -387,6 +388,8 @@ libbitcoin_wallet_a_SOURCES = \
transaction_builder.cpp \
wallet/rpcdisclosure.cpp \
wallet/rpcdump.cpp \
cc/CCtokens.cpp \
cc/CCassetsCore.cpp \
cc/CCassetstx.cpp \
cc/CCtx.cpp \
wallet/rpcwallet.cpp \
@ -572,7 +575,8 @@ komodod_LDADD += \
$(LIBBITCOIN_CRYPTO) \
$(LIBVERUS_CRYPTO) \
$(LIBVERUS_PORTABLE_CRYPTO) \
$(LIBZCASH_LIBS)
$(LIBZCASH_LIBS) \
cclib.so
if ENABLE_PROTON
komodod_LDADD += $(LIBBITCOIN_PROTON) $(PROTON_LIBS)

3
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();

2
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)

18
src/cc/CCHeir.h

@ -18,12 +18,24 @@
#define CC_HEIR_H
#include "CCinclude.h"
#include "CCtokens.h"
#define EVAL_HEIR 0xea
//#define EVAL_HEIR 0xea
bool HeirValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
// CCcustom
UniValue HeirInfo();
class CoinHelper;
class TokenHelper;
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();
//std::string Heir_MakeBadTx(uint256 fundingtxid, uint8_t funcId, int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTime, uint32_t errMask);
//bool HeirExactTokenAmounts(bool compareTotals, struct CCcontract_info *cpHeir, Eval* eval, uint256 assetid, const CTransaction &tx);
#endif

19
src/cc/CCMarmara.h

@ -18,6 +18,25 @@
#define CC_TRIGGERS_H
#include "CCinclude.h"
#include "../komodo_cJSON.h"
#define MARMARA_GROUPSIZE 60
#define MARMARA_MINLOCK (1440 * 3 * 30)
#define MARMARA_MAXLOCK (1440 * 24 * 30)
#define MARMARA_VINS 16
extern uint8_t ASSETCHAINS_MARMARA;
uint64_t komodo_block_prg(uint32_t nHeight);
int32_t MarmaraGetcreatetxid(uint256 &createtxid,uint256 txid);
int32_t MarmaraGetbatontxid(std::vector<uint256> &creditloop,uint256 &batontxid,uint256 txid);
UniValue MarmaraCreditloop(uint256 txid);
UniValue MarmaraSettlement(uint64_t txfee,uint256 batontxid);
UniValue MarmaraLock(uint64_t txfee,int64_t amount,int32_t height);
UniValue MarmaraPoolPayout(uint64_t txfee,int32_t firstheight,double perc,char *jsonstr); // [[pk0, shares0], [pk1, shares1], ...]
UniValue MarmaraReceive(uint64_t txfee,CPubKey senderpk,int64_t amount,std::string currency,int32_t matures,uint256 batontxid,bool automaticflag);
UniValue MarmaraIssue(uint64_t txfee,uint8_t funcid,CPubKey receiverpk,int64_t amount,std::string currency,int32_t matures,uint256 approvaltxid,uint256 batontxid);
UniValue MarmaraInfo(CPubKey refpk,int32_t firstheight,int32_t lastheight,int64_t minamount,int64_t maxamount,std::string currency);
bool MarmaraValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);

28
src/cc/CCassets.h

@ -30,12 +30,12 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
// CCassetsCore
//CTxOut MakeAssetsVout(CAmount nValue,CPubKey pk);
CScript EncodeAssetCreateOpRet(uint8_t funcid,std::vector<uint8_t> origpubkey,std::string name,std::string description);
CScript EncodeAssetOpRet(uint8_t funcid,uint256 assetid,uint256 assetid2,int64_t price,std::vector<uint8_t> origpubkey);
bool DecodeAssetCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description);
uint8_t DecodeAssetOpRet(const CScript &scriptPubKey,uint256 &assetid,uint256 &assetid2,int64_t &price,std::vector<uint8_t> &origpubkey);
//CScript EncodeAssetCreateOpRet(uint8_t funcid,std::vector<uint8_t> origpubkey,std::string name,std::string description);
//CScript EncodeAssetOpRet(uint8_t assetFuncId, uint256 tokenid, uint256 assetid2, int64_t price, std::vector<uint8_t> origpubkey);
//bool DecodeAssetCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description);
//uint8_t DecodeAssetOpRet(const CScript &scriptPubKey, uint8_t &evalCode, uint256 &assetid, uint256 &assetid2, int64_t &price, std::vector<uint8_t> &origpubkey);
bool SetAssetOrigpubkey(std::vector<uint8_t> &origpubkey,int64_t &price,const CTransaction &tx);
int64_t IsAssetvout(int32_t maxAssetExactAmountDepth, struct CCcontract_info *cp, Eval* eval, int64_t &price,std::vector<uint8_t> &origpubkey,const CTransaction& tx,int32_t v,uint256 refassetid);
int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &price, std::vector<uint8_t> &origpubkey, const CTransaction& tx, int32_t v, uint256 refassetid);
bool ValidateBidRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice);
bool ValidateAskRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice);
bool ValidateSwapRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice);
@ -44,18 +44,18 @@ bool SetAskFillamounts(int64_t &paid,int64_t &remaining_price,int64_t orig_nValu
bool SetSwapFillamounts(int64_t &paid,int64_t &remaining_price,int64_t orig_nValue,int64_t &received,int64_t totalprice);
int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector<uint8_t> &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 refassetid);
int64_t AssetValidateSellvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector<uint8_t> &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 assetid);
bool AssetExactAmounts(int32_t maxDepth, struct CCcontract_info *cp,int64_t &inputs,int32_t starti,int64_t &outputs,Eval* eval,const CTransaction &tx,uint256 assetid);
//bool AssetExactAmounts(bool doValidateTx, struct CCcontract_info *cp, int64_t &inputs, int32_t starti, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid, std::vector<CTransaction> &ccVinsTxs);
bool AssetCalcAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid);
// CCassetstx
int64_t GetAssetBalance(CPubKey pk,uint256 tokenid);
int64_t AddAssetInputs(CMutableTransaction &mtx,CPubKey pk,uint256 assetid,int64_t total,int32_t maxinputs);
//int64_t GetAssetBalance(CPubKey pk,uint256 tokenid); // --> GetTokenBalance()
int64_t AddAssetInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 assetid, int64_t total, int32_t maxinputs);
UniValue AssetOrders(uint256 tokenid);
UniValue AssetInfo(uint256 tokenid);
UniValue AssetList();
std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::string description);
std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total);
std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total,int32_t evalcode);
//UniValue AssetInfo(uint256 tokenid);
//UniValue AssetList();
//std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::string description);
//std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total);
//std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total,int32_t evalcode);
std::string CreateBuyOffer(int64_t txfee,int64_t bidamount,uint256 assetid,int64_t pricetotal);
std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid);

502
src/cc/CCassetsCore.cpp

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright © 2014-2019 The SuperNET Developers. *
* Copyright © 2014-2018 The SuperNET Developers. *
* *
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
* the top-level directory of this distribution for the individual copyright *
@ -33,7 +33,7 @@
Yes, this is quite confusing...
In ValudateAssetRemainder the naming convention is nValue is the coin/asset with the offer on the books and "units" is what it is being paid in. The high level check is to make sure we didnt lose any coins or assets, the harder to validate is the actual price paid as the "orderbook" is in terms of the combined nValue for the combined totalunits.
In ValidateAssetRemainder the naming convention is nValue is the coin/asset with the offer on the books and "units" is what it is being paid in. The high level check is to make sure we didnt lose any coins or assets, the harder to validate is the actual price paid as the "orderbook" is in terms of the combined nValue for the combined totalunits.
We assume that the effective unit cost in the orderbook is valid and that that amount was paid and also that any remainder will be close enough in effective unit cost to not matter. At the edge cases, this will probably be not true and maybe some orders wont be practically fillable when reduced to fractional state. However, the original pubkey that created the offer can always reclaim it.
*/
@ -230,38 +230,43 @@ bool ValidateSwapRemainder(int64_t remaining_price,int64_t remaining_nValue,int6
return(true);
}
/* use EncodeTokenCreateOpRet instead:
CScript EncodeAssetCreateOpRet(uint8_t funcid,std::vector<uint8_t> origpubkey,std::string name,std::string description)
{
CScript opret; uint8_t evalcode = EVAL_ASSETS;
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description);
return(opret);
}
*/
CScript EncodeAssetOpRet(uint8_t funcid,uint256 assetid,uint256 assetid2,int64_t price,std::vector<uint8_t> origpubkey)
CScript EncodeAssetOpRet(uint8_t assetFuncId, uint256 assetid2, int64_t price, std::vector<uint8_t> origpubkey)
{
CScript opret; uint8_t evalcode = EVAL_ASSETS;
assetid = revuint256(assetid);
switch ( funcid )
CScript opret;
uint8_t evalcode = EVAL_ASSETS;
switch ( assetFuncId )
{
case 't': case 'x': case 'o':
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << assetid);
//case 't': this cannot be here
case 'x': case 'o':
opret << OP_RETURN << E_MARSHAL(ss << evalcode << assetFuncId);
break;
case 's': case 'b': case 'S': case 'B':
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << assetid << price << origpubkey);
opret << OP_RETURN << E_MARSHAL(ss << evalcode << assetFuncId << price << origpubkey);
break;
case 'E': case 'e':
assetid2 = revuint256(assetid2);
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << assetid << assetid2 << price << origpubkey);
opret << OP_RETURN << E_MARSHAL(ss << evalcode << assetFuncId << assetid2 << price << origpubkey);
break;
default:
fprintf(stderr,"EncodeOpRet: illegal funcid.%02x\n",funcid);
fprintf(stderr,"EncodeAssetOpRet: illegal funcid.%02x\n", assetFuncId);
opret << OP_RETURN;
break;
}
return(opret);
}
bool DecodeAssetCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description)
/* it is for compatibility, do not use this for new contracts (use DecodeTokenCreateOpRet)
bool DecodeAssetCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description)
{
std::vector<uint8_t> vopret; uint8_t evalcode,funcid,*script;
GetOpReturnData(scriptPubKey, vopret);
@ -272,173 +277,187 @@ bool DecodeAssetCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &or
return(true);
}
return(0);
}
} */
uint8_t DecodeAssetOpRet(const CScript &scriptPubKey,uint256 &assetid,uint256 &assetid2,int64_t &price,std::vector<uint8_t> &origpubkey)
uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &evalCodeInOpret, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector<uint8_t> &origpubkey)
{
std::vector<uint8_t> vopret; uint8_t funcid=0,*script,e,f;
GetOpReturnData(scriptPubKey, vopret);
std::vector<uint8_t> vopretExtra, vopretStripped;
uint8_t *script, funcId = 0, assetFuncId = 0, dummyEvalCode, dummyAssetFuncId;
uint256 dummyTokenid;
std::vector<CPubKey> voutPubkeysDummy;
tokenid = zeroid;
assetid2 = zeroid;
price = 0;
// First - decode token opret:
funcId = DecodeTokenOpRet(scriptPubKey, evalCodeInOpret, tokenid, voutPubkeysDummy, vopretExtra);
/*GetOpReturnData(scriptPubKey, vopret);
script = (uint8_t *)vopret.data();
memset(&assetid,0,sizeof(assetid));
memset(&assetid2,0,sizeof(assetid2));
price = 0;
if ( script != 0 && script[0] == EVAL_ASSETS )
{
funcid = script[1];
//fprintf(stderr,"decode.[%c]\n",funcid);
switch ( funcid )
if (script == 0) {
std::cerr << "DecodeAssetOpRet() script is empty" << std::endl;
return (uint8_t)0;
}*/
//bool isEof = true; // NOTE: if parse error occures, parse might not be able to set error. It is safer to treat that it was eof if it is not set!
//bool result = E_UNMARSHAL(vopret, ss >> evalCodeInOpret; ss >> funcId; ss >> tokenid; ss >> assetFuncId; isEof = ss.eof());
if (funcId == 0 || vopretExtra.size() < 2) {
std::cerr << "DecodeAssetOpRet() incorrect opret or no asset's payload" << " funcId=" << (int)funcId << " vopretExtra.size()=" << vopretExtra.size() << std::endl;
return (uint8_t)0;
}
if (!E_UNMARSHAL(vopretExtra, { ss >> vopretStripped; })) { //strip string size
std::cerr << "DecodeAssetTokenOpRet() could not unmarshal vopretStripped" << std::endl;
return (uint8_t)0;
}
////tokenid = revuint256(tokenid); already done in DecodeToken!
evalCodeInOpret = vopretStripped.begin()[0];
assetFuncId = vopretStripped.begin()[1];
//std::cerr << "DecodeAssetOpRet() evalCodeInOpret=" << (int)evalCodeInOpret << " funcId=" << (char)(funcId ? funcId : ' ') << " assetFuncId=" << (char)(assetFuncId ? assetFuncId : ' ') << std::endl;
if(evalCodeInOpret == EVAL_ASSETS)
{
//fprintf(stderr,"decode.[%c] assetFuncId.[%c]\n", funcId, assetFuncId);
switch( assetFuncId )
{
case 'c': return(funcid);
break;
case 't': case 'x': case 'o':
if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> assetid) != 0 )
case 'x': case 'o':
if (vopretStripped.size() == 2) // no data after 'evalcode assetFuncId' allowed
{
assetid = revuint256(assetid);
return(funcid);
return(assetFuncId);
}
break;
case 's': case 'b': case 'S': case 'B':
if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> assetid; ss >> price; ss >> origpubkey) != 0 )
if (E_UNMARSHAL(vopretStripped, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> price; ss >> origpubkey) != 0)
{
assetid = revuint256(assetid);
//fprintf(stderr,"got price %llu\n",(long long)price);
return(funcid);
//fprintf(stderr,"DecodeAssetTokenOpRet got price %llu\n",(long long)price);
return(assetFuncId);
}
break;
case 'E': case 'e':
if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> assetid; ss >> assetid2; ss >> price; ss >> origpubkey) != 0 )
if ( E_UNMARSHAL(vopretStripped, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> assetid2; ss >> price; ss >> origpubkey) != 0 )
{
//fprintf(stderr,"got price %llu\n",(long long)price);
assetid = revuint256(assetid);
//fprintf(stderr,"DecodeAssetTokenOpRet got price %llu\n",(long long)price);
assetid2 = revuint256(assetid2);
return(funcid);
return(assetFuncId);
}
break;
default:
fprintf(stderr,"DecodeAssetOpRet: illegal funcid.%02x\n",funcid);
funcid = 0;
fprintf(stderr,"DecodeAssetTokenOpRet: illegal assetFuncId.%02x\n", assetFuncId);
//funcId = 0;
break;
}
}
return(funcid);
return (uint8_t)0;
}
// extract sell/buy owner's pubkey from the opret
bool SetAssetOrigpubkey(std::vector<uint8_t> &origpubkey,int64_t &price,const CTransaction &tx)
{
uint256 assetid,assetid2;
if ( tx.vout.size() > 0 && DecodeAssetOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,assetid,assetid2,price,origpubkey) != 0 )
uint8_t evalCode;
if ( tx.vout.size() > 0 && DecodeAssetTokenOpRet(tx.vout[tx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey) != 0 )
return(true);
else return(false);
else
return(false);
}
bool GetAssetorigaddrs(struct CCcontract_info *cp,char *CCaddr,char *destaddr,const CTransaction& tx)
// Calculate sell/buy owner's source token/asset address from ask/bid tx
bool GetAssetorigaddrs(struct CCcontract_info *cp, char *userCCaddr, char *destaddr, const CTransaction& tx)
{
uint256 assetid,assetid2; int64_t price,nValue=0; int32_t n; uint8_t funcid; std::vector<uint8_t> origpubkey; CScript script;
uint256 assetid,assetid2; int64_t price,nValue=0; int32_t n; uint8_t funcid;
std::vector<uint8_t> origpubkey;
CScript script;
uint8_t evalCode;
n = tx.vout.size();
if ( n == 0 || (funcid= DecodeAssetOpRet(tx.vout[n-1].scriptPubKey,assetid,assetid2,price,origpubkey)) == 0 )
if( n == 0 || (funcid = DecodeAssetTokenOpRet(tx.vout[n-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) == 0 )
return(false);
if ( GetCCaddress(cp,CCaddr,pubkey2pk(origpubkey)) != 0 && Getscriptaddress(destaddr,CScript() << origpubkey << OP_CHECKSIG) != 0 )
bool bGetCCaddr = false;
if (funcid == 's' || funcid == 'S') {
struct CCcontract_info *cpTokens, tokensC;
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
bGetCCaddr = GetCCaddress(cpTokens, userCCaddr, pubkey2pk(origpubkey));
//bGetCCaddr = GetTokensCCaddress(cp, CCaddr, pubkey2pk(origpubkey));
}
else if (funcid == 'b' || funcid == 'B') {
struct CCcontract_info *cpTokens, tokensC;
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
bGetCCaddr = GetCCaddress(cpTokens, userCCaddr, pubkey2pk(origpubkey));
}
else {
std::cerr << "GetAssetorigaddrs incorrect funcid=" << (char)(funcid?funcid:' ') << std::endl;
return false;
}
if( bGetCCaddr && Getscriptaddress(destaddr, CScript() << origpubkey << OP_CHECKSIG))
return(true);
else return(false);
else
return(false);
}
// Checks if the vout is a really Asset CC vout
// if maxAssetExactAmountDepth > 0, it also validates the vin transaction itself:
// it should be either sum(cc vins) == sum(cc vouts) or the transaction is the 'tokenbase' ('c') tx
int64_t IsAssetvout(int32_t maxAssetExactAmountDepth, struct CCcontract_info *cp, Eval* eval, int64_t &price,std::vector<uint8_t> &origpubkey,const CTransaction& tx,int32_t v,uint256 refassetid)
int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *CCaddr,char *origaddr,const CTransaction &tx,int32_t vini,CTransaction &vinTx)
{
uint256 assetid,assetid2; int64_t nValue=0; int32_t n; uint8_t funcid;
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) // maybe check address too?
{
uint256 hashBlock;
uint256 assetid, assetid2;
int64_t tmpprice;
std::vector<uint8_t> tmporigpubkey;
uint8_t evalCode;
if (maxAssetExactAmountDepth > 0) {
//validate all tx
int64_t myCCVinsAmount = 0, myCCVoutsAmount = 0;
std::vector<CTransaction> ccVinsTxs;
//std::cerr << "IsAssetvout() validate=yes" << std::endl;
const bool validateVinTxs = false;
bool isEqualAmounts = AssetExactAmounts(maxAssetExactAmountDepth, cp, myCCVinsAmount, 0, myCCVoutsAmount, eval, tx, refassetid);
// if ccInputs != ccOutputs and it is not the tokenbase tx means it is possibly fake tx (dimxy):
if (!isEqualAmounts && refassetid != tx.GetHash()) { // checking that this is the true tokenbase tx, by verifying that funcid=c, is done further in this function (dimxy)
std::cerr << "IsAssetvout() detected bad tx=" << tx.GetHash().GetHex() << ": cc inputs != cc outputs and not the 'tokenbase' tx" << std::endl;
return 0;
}
}
char destaddr[64], unspendableAddr[64];
origaddr[0] = destaddr[0] = CCaddr[0] = 0;
n = tx.vout.size();
if (v >= n - 1) { // just moved this up (dimxy)
std::cerr << "isAssetVout() internal err: (v >= n - 1), returning 0" << std::endl;
return(0);
}
nValue = tx.vout[v].nValue;
// fprintf(stderr,"IsAssetvout() CC vout v.%d of n=%d amount=%.8f\n",v,n,(double)nValue/COIN);
uint8_t funcid = 0;
if (tx.vout.size() > 0) {
uint256 assetid, assetid2;
int64_t tmpprice;
std::vector<uint8_t> tmporigpubkey;
uint8_t evalCode;
funcid = DecodeAssetTokenOpRet(tx.vout[tx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey);
}
if ( (funcid= DecodeAssetOpRet(tx.vout[n-1].scriptPubKey,assetid,assetid2,price,origpubkey)) == 0 )
{
fprintf(stderr,"IsAssetvout() null decodeopret v.%d\n",v);
return(0);
}
else if ( funcid == 'c' )
{
if (refassetid == tx.GetHash() && v == 0) {
std::cerr << "isAssetVout() this is the tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " returning nValue=" << nValue << std::endl;
return(nValue);
}
}
else if ( (funcid == 'b' || funcid == 'B') && v == 0 ) // critical! 'b'/'B' vout0 is NOT asset
return(0);
else if ( funcid != 'E' )
{
if ( assetid == refassetid )
{
fprintf(stderr,"IsAssetvout() returning %.8f\n",(double)nValue/COIN);
return(nValue);
}
}
else if ( funcid == 'E' )
{
if ( v < 2 && assetid == refassetid )
return(nValue);
else if ( v == 2 && assetid2 == refassetid )
return(nValue);
}
}
//fprintf(stderr,"Isassetvout: normal output v.%d %.8f\n",v,(double)tx.vout[v].nValue/COIN);
return(0);
}
int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *CCaddr,char *origaddr,const CTransaction &tx,int32_t vini,CTransaction &vinTx)
{
uint256 hashBlock; char destaddr[64];
origaddr[0] = destaddr[0] = CCaddr[0] = 0;
if ( tx.vin.size() < 2 )
if( tx.vin.size() < 2 )
return eval->Invalid("not enough for CC vins");
else if ( tx.vin[vini].prevout.n != 0 )
else if( tx.vin[vini].prevout.n != 0 )
return eval->Invalid("vin1 needs to be buyvin.vout[0]");
else if ( eval->GetTxUnconfirmed(tx.vin[vini].prevout.hash,vinTx,hashBlock) == 0 )
else if( eval->GetTxUnconfirmed(tx.vin[vini].prevout.hash, vinTx,hashBlock) == 0 )
{
int32_t z;
/* int32_t z;
for (z=31; z>=0; z--)
fprintf(stderr,"%02x",((uint8_t *)&tx.vin[vini].prevout.hash)[z]);
fprintf(stderr," vini.%d\n",vini);
fprintf(stderr," vini.%d\n",vini); */
std::cerr << "AssetValidateCCvin cannot load vintx for vin=" << vini << " vintx id=" << tx.vin[vini].prevout.hash.GetHex() << std::endl;
return eval->Invalid("always should find CCvin, but didnt");
}
else if ( Getscriptaddress(destaddr,vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 || strcmp(destaddr,(char *)cp->unspendableCCaddr) != 0 )
// if fillSell or cancelSell --> to spend tokens from dual-eval token-assets unspendable addr
else if( (funcid == 'S' || funcid == 'x') &&
(Getscriptaddress(destaddr, vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 ||
!GetTokensCCaddress(cp, unspendableAddr, GetUnspendable(cp, NULL)) ||
strcmp(destaddr, unspendableAddr) != 0))
{
fprintf(stderr,"%s vs %s\n",destaddr,(char *)cp->unspendableCCaddr);
fprintf(stderr,"AssetValidateCCvin cc addr %s is not dual token-evalcode=0x%02x asset unspendable addr %s\n", destaddr, (int)cp->evalcode, unspendableAddr);
return eval->Invalid("invalid vin AssetsCCaddr");
}
// if fillBuy or cancelBuy --> to spend coins from asset unspendable addr
else if ((funcid == 'B' || funcid == 'o') &&
(Getscriptaddress(destaddr, vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 ||
!GetCCaddress(cp, unspendableAddr, GetUnspendable(cp, NULL)) ||
strcmp(destaddr, unspendableAddr) != 0))
{
fprintf(stderr, "AssetValidateCCvin cc addr %s is not evalcode=0x%02x asset unspendable addr %s\n", destaddr, (int)cp->evalcode, unspendableAddr);
return eval->Invalid("invalid vin AssetsCCaddr");
}
//else if ( vinTx.vout[0].nValue < 10000 )
// return eval->Invalid("invalid dust for buyvin");
else if ( GetAssetorigaddrs(cp,CCaddr,origaddr,vinTx) == 0 )
else if( GetAssetorigaddrs(cp, CCaddr, origaddr, vinTx) == 0 )
return eval->Invalid("couldnt get origaddr for buyvin");
fprintf(stderr,"Got %.8f to origaddr.(%s)\n",(double)vinTx.vout[tx.vin[vini].prevout.n].nValue/COIN,origaddr);
fprintf(stderr,"AssetValidateCCvin got %.8f to origaddr.(%s)\n",(double)vinTx.vout[tx.vin[vini].prevout.n].nValue/COIN,origaddr);
if ( vinTx.vout[0].nValue == 0 )
return eval->Invalid("null value CCvin");
return(vinTx.vout[0].nValue);
@ -446,16 +465,21 @@ int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *CCaddr,ch
int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector<uint8_t> &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 refassetid)
{
CTransaction vinTx; int64_t nValue; uint256 assetid,assetid2; uint8_t funcid;
CTransaction vinTx; int64_t nValue; uint256 assetid,assetid2; uint8_t funcid, evalCode;
CCaddr[0] = origaddr[0] = 0;
if ( (nValue= AssetValidateCCvin(cp,eval,CCaddr,origaddr,tx,1,vinTx)) == 0 )
return(0);
// validate locked coins on Assets vin[1]
if ( (nValue= AssetValidateCCvin(cp, eval, CCaddr, origaddr, tx, 1, vinTx)) == 0 )
return(0);
else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
return eval->Invalid("invalid normal vout0 for buyvin");
else if ((funcid = DecodeAssetTokenOpRet(vinTx.vout[vinTx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey)) == 'b' &&
vinTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) // marker is only in 'b'?
return eval->Invalid("invalid normal vout1 for buyvin");
else
{
//fprintf(stderr,"have %.8f checking assetid origaddr.(%s)\n",(double)nValue/COIN,origaddr);
if ( vinTx.vout.size() > 0 && (funcid= DecodeAssetOpRet(vinTx.vout[vinTx.vout.size()-1].scriptPubKey,assetid,assetid2,tmpprice,tmporigpubkey)) != 'b' && funcid != 'B' )
if ( vinTx.vout.size() > 0 && funcid != 'b' && funcid != 'B' )
return eval->Invalid("invalid opreturn for buyvin");
else if ( refassetid != assetid )
return eval->Invalid("invalid assetid for buyvin");
@ -469,88 +493,162 @@ int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmppr
int64_t AssetValidateSellvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector<uint8_t> &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 assetid)
{
CTransaction vinTx; int64_t nValue,assetoshis;
fprintf(stderr,"AssetValidateSellvin\n");
if ( (nValue= AssetValidateCCvin(cp,eval,CCaddr,origaddr,tx,1,vinTx)) == 0 )
//fprintf(stderr,"AssetValidateSellvin\n");
if ( (nValue = AssetValidateCCvin(cp, eval, CCaddr, origaddr, tx, 1, vinTx)) == 0 )
return(0);
if ( (assetoshis= IsAssetvout(1, cp, NULL, tmpprice,tmporigpubkey,vinTx,0,assetid)) == 0 )
if ( (assetoshis= IsAssetvout(cp, tmpprice, tmporigpubkey, vinTx, 0, assetid)) == 0 )
return eval->Invalid("invalid missing CC vout0 for sellvin");
else return(assetoshis);
else
return(assetoshis);
}
// overload with additional params for deep tx validation (dimxy)
bool AssetExactAmounts(int maxDepth, struct CCcontract_info *cp, int64_t &inputs, int32_t starti, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid)
{
CTransaction vinTx; uint256 hashBlock,id,id2; int32_t i,flag,numvins,numvouts; int64_t assetoshis; std::vector<uint8_t> tmporigpubkey; int64_t tmpprice;
numvins = tx.vin.size();
numvouts = tx.vout.size();
inputs = outputs = 0;
// validates opret for asset tx:
bool ValidateAssetOpret(CTransaction tx, int32_t v, uint256 assetid, int64_t &price, std::vector<uint8_t> &origpubkey) {
maxDepth--;
uint256 assetidOpret, assetidOpret2;
uint8_t funcid, evalCode;
for (i=starti; i<numvins; i++)
{
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
{
//std::cerr << "AssetExactAmounts() eval is true=" << (eval != NULL) << " ismyvin=ok for_i=" << i << std::endl;
// we are really not inside validation! -- dimxy
if ( (eval && eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0) || (!eval && !myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock)) )
{
fprintf(stderr,"AssetExactAmounts() cannot read vintx i.%d starti.%d numvins.%d\n", i,starti,numvins);
return (!eval) ? false : eval->Invalid("always should find vin, but didnt");
} // false means 'don't go deeper' -- dimxy
else if ( (assetoshis= IsAssetvout( maxDepth, cp, eval, tmpprice,tmporigpubkey,vinTx,tx.vin[i].prevout.n,assetid)) != 0 )
{
fprintf(stderr,"AssetExactAmounts() vin%d %llu, ",i,(long long)assetoshis);
inputs += assetoshis;
}
else
{
if ( vinTx.vout[i].scriptPubKey.IsPayToCryptoCondition() != 0 && DecodeAssetOpRet(vinTx.vout[vinTx.vout.size()-1].scriptPubKey,id,id2,tmpprice,tmporigpubkey) == 't' && id == assetid )
{
assetoshis = vinTx.vout[i].nValue;
fprintf(stderr,"AssetExactAmounts() vin%d assetoshis=%llu special case, ",i,(long long)assetoshis);
inputs += assetoshis;
}
}
}
}
// this is just for log messages indentation fur debugging recursive calls:
int32_t n = tx.vout.size();
if ((funcid = DecodeAssetTokenOpRet(tx.vout[n - 1].scriptPubKey, evalCode, assetidOpret, assetidOpret2, price, origpubkey)) == 0)
{
std::cerr << "ValidateAssetOpret() DecodeAssetTokenOpRet returned null for the opret for txid=" << tx.GetHash().GetHex() << std::endl;
return(false);
}
/* it is now on token level:
else if (funcid == 'c')
{
if (assetid != zeroid && assetid == tx.GetHash() && v == 0) {
//std::cerr << "ValidateAssetOpret() this is the tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl;
return(true);
}
}
else if (funcid == 't') // TODO: check if this new block does not influence IsAssetVout
{
//std::cerr << "ValidateAssetOpret() assetid=" << assetid.GetHex() << " assetIdOpret=" << assetidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl;
if (assetid != zeroid && assetid == assetidOpret) {
//std::cerr << "ValidateAssetOpret() this is a transfer 't' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl;
return(true);
}
} */
//else if ((funcid == 'b' || funcid == 'B') && v == 0) // critical! 'b'/'B' vout0 is NOT asset
// return(false);
else if (funcid != 'E')
{
if (assetid != zeroid && assetidOpret == assetid)
{
//std::cerr << "ValidateAssetOpret() returns true for not 'E', funcid=" << (char)funcid << std::endl;
return(true);
}
}
else if (funcid == 'E') // NOTE: not implemented yet!
{
if (v < 2 && assetid != zeroid && assetidOpret == assetid)
return(true);
else if (v == 2 && assetid != zeroid && assetidOpret2 == assetid)
return(true);
}
//std::cerr << "ValidateAssetOpret() return false funcid=" << (char)funcid << " assetid=" << assetid.GetHex() << " assetIdOpret=" << assetidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl;
return false;
}
if ( DecodeAssetOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,id,id2,tmpprice,tmporigpubkey) == 't' && id == assetid )
flag = 1;
else flag = 0;
// Checks if the vout is a really Asset CC vout
// compareTotals == true, the func also validates the passed transaction itself:
// it should be either sum(cc vins) == sum(cc vouts) or the transaction is the 'tokenbase' ('c') tx
int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &price, std::vector<uint8_t> &origpubkey, const CTransaction& tx, int32_t v, uint256 refassetid)
{
for (i=0; i<numvouts; i++)
{ // 'false' means 'dont go deep' -- dimxy
if ( (assetoshis= IsAssetvout(maxDepth, cp, eval, tmpprice,tmporigpubkey,tx,i,assetid)) != 0 )
{
fprintf(stderr,"AssetExactAmounts() vout%d assetoshis=%llu, ",i,(long long)assetoshis);
outputs += assetoshis;
}
// Note: account it only if this is 'transfer' tx -- dimxy
else if ( flag != 0 && tx.vout[i].scriptPubKey.IsPayToCryptoCondition() != 0 )
{
assetoshis = tx.vout[i].nValue;
fprintf(stderr,"AssetExactAmounts() vout%d assetoshis=%llu special case, ",i,(long long)assetoshis);
outputs += assetoshis;
}
}
//std::cerr << "IsAssetvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for assetid=" << refassetid.GetHex() << std::endl;
//std::cerr << "AssetExactAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl;
if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0) // maybe check address too? dimxy: possibly no, because there are too many cases with different addresses here
{
int32_t n = tx.vout.size();
// just check boundaries:
if (v >= n - 1) { // just moved this up (dimxy)
std::cerr << "isAssetVout() internal err: (v >= n - 1), returning 0" << std::endl;
return(0);
}
if ( inputs != outputs )
{
std::cerr << "AssetExactAmounts() incorrect inputs=" << (double)inputs / COIN << " vs outputs=" << (double)outputs/COIN << " for txid=" << tx.GetHash().GetHex() << std::endl;
return(false);
}
else return(true);
}
// moved opret checking to this new reusable func (dimxy):
const bool valOpret = ValidateAssetOpret(tx, v, refassetid, price, origpubkey);
//std::cerr << "IsAssetvout() ValidateAssetOpret returned=" << std::boolalpha << valOpret << " for txid=" << tx.GetHash().GetHex() << " for assetid=" << refassetid.GetHex() << std::endl;
if (valOpret) {
//std::cerr << "IsAssetvout() ValidateAssetOpret returned true, returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for assetid=" << refassetid.GetHex() << std::endl;
return tx.vout[v].nValue;
}
// overload for existing calls of this function (dimxy)
/*bool AssetExactAmounts(struct CCcontract_info *cp, int64_t &inputs, int32_t starti, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid) {
std::vector<CTransaction> ccVinsTxs;
//fprintf(stderr,"IsAssetvout() CC vout v.%d of n=%d amount=%.8f txid=%s\n",v,n,(double)0/COIN, tx.GetHash().GetHex().c_str());
}
//fprintf(stderr,"IsAssetvout() normal output v.%d %.8f\n",v,(double)tx.vout[v].nValue/COIN);
return(0);
}
return AssetExactAmounts(true, cp, inputs, starti, outputs, eval, tx, assetid);
}*/
// sets cc inputs vs cc outputs and ensures they are equal:
bool AssetCalcAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid)
{
CTransaction vinTx; uint256 hashBlock, id, id2; int32_t flag; int64_t assetoshis; std::vector<uint8_t> tmporigpubkey; int64_t tmpprice;
int32_t numvins = tx.vin.size();
int32_t numvouts = tx.vout.size();
inputs = outputs = 0;
struct CCcontract_info *cpTokens, C;
cpTokens = CCinit(&C, EVAL_TOKENS);
for (int32_t i = 0; i<numvins; i++)
{ // only tokens are relevant!!
if (/*(*cpAssets->ismyvin)(tx.vin[i].scriptSig)*/ (*cpTokens->ismyvin)(tx.vin[i].scriptSig) ) // || IsVinAllowed(tx.vin[i].scriptSig) != 0)
{
//std::cerr << indentStr << "AssetExactAmounts() eval is true=" << (eval != NULL) << " ismyvin=ok for_i=" << i << std::endl;
// we are not inside the validation code -- dimxy
if ((eval && eval->GetTxUnconfirmed(tx.vin[i].prevout.hash, vinTx, hashBlock) == 0) || (!eval && !myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock)))
{
std::cerr << "AssetCalcAmounts() cannot read vintx for i." << i << " numvins." << numvins << std::endl;
return (!eval) ? false : eval->Invalid("always should find vin tx, but didnt");
}
else {
// validate vouts of vintx
//std::cerr << indentStr << "AssetExactAmounts() check vin i=" << i << " nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl;
//assetoshis = IsAssetvout(cpAssets, tmpprice, tmporigpubkey, vinTx, tx.vin[i].prevout.n, assetid);
std::vector<uint8_t> vopretExtra;
std::vector<CPubKey> vinPubkeysEmpty;
// TODO: maybe we do not need call to IsTokensVout here, cause we've already selected token vins
assetoshis = IsTokensvout(false, false, cpTokens, NULL, vinTx, tx.vin[i].prevout.n, assetid);
if (assetoshis != 0)
{
std::cerr << "AssetCalcAmounts() vin i=" << i << " assetoshis=" << assetoshis << std::endl;
inputs += assetoshis;
}
}
}
}
for (int32_t i = 0; i < numvouts; i++)
{
// Note: we pass in here 'false' because we don't need to call AssetExactAmounts() recursively from IsAssetvout
// indeed, in this case we'll be checking this tx again
assetoshis = IsAssetvout(cpAssets, tmpprice, tmporigpubkey, tx, i, assetid);
if (assetoshis != 0)
{
std::cerr << "AssetCalcAmounts() vout i=" << i << " assetoshis=" << assetoshis << std::endl;
outputs += assetoshis;
}
}
//std::cerr << "AssetCalcAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl;
/* we do not verify inputs == outputs here,
it's done in Tokens:
if (inputs != outputs) {
if (tx.GetHash() != assetid) {
std::cerr << "AssetCalcAmounts() unequal inputs=" << inputs << " vs outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl;
return (!eval) ? false : eval->Invalid("assets cc inputs != cc outputs");
}
} */
return(true);
}

608
src/cc/CCassetstx.cpp

@ -14,6 +14,8 @@
******************************************************************************/
#include "CCassets.h"
//#include "CCtokens.h"
int64_t AddAssetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 assetid,int64_t total,int32_t maxinputs)
{
@ -21,25 +23,31 @@ int64_t AddAssetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubK
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
GetCCaddress(cp,coinaddr,pk);
SetCCunspents(unspentOutputs,coinaddr);
threshold = total/(maxinputs!=0?maxinputs:64);
threshold = total/(maxinputs!=0?maxinputs:64); // TODO: is maxinputs really not over 64, what if i want to calc total balance?
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
{
txid = it->first.txhash;
vout = (int32_t)it->first.index;
if ( it->second.satoshis < threshold )
if (it->second.satoshis < threshold)
continue;
for (j=0; j<mtx.vin.size(); j++)
if ( txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n )
if (txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n)
break;
if ( j != mtx.vin.size() )
if( j != mtx.vin.size() )
continue;
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
if( GetTransaction(txid,vintx,hashBlock,false) != 0 )
{
Getscriptaddress(destaddr,vintx.vout[vout].scriptPubKey);
if ( strcmp(destaddr,coinaddr) != 0 && strcmp(destaddr,cp->unspendableCCaddr) != 0 && strcmp(destaddr,cp->unspendableaddr2) != 0 )
if( strcmp(destaddr,coinaddr) != 0 && strcmp(destaddr,cp->unspendableCCaddr) != 0 && strcmp(destaddr,cp->unspendableaddr2) != 0 )
continue;
fprintf(stderr,"AddAssetInputs() check destaddress=%s vout amount=%.8f\n",destaddr,(double)vintx.vout[vout].nValue/COIN);
if ( (nValue= IsAssetvout(1, cp, NULL, price,origpubkey,vintx,vout,assetid)) > 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
if( (nValue = IsAssetvout(cp, price, origpubkey, vintx, vout, assetid)) > 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
{
if ( total != 0 && maxinputs != 0 )
mtx.vin.push_back(CTxIn(txid,vout,CScript()));
@ -57,72 +65,37 @@ int64_t AddAssetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubK
return(totalinputs);
}
int64_t GetAssetBalance(CPubKey pk,uint256 tokenid)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
struct CCcontract_info *cp,C;
cp = CCinit(&C,EVAL_ASSETS);
return(AddAssetInputs(cp,mtx,pk,tokenid,0,0));
}
UniValue AssetInfo(uint256 assetid)
{
UniValue result(UniValue::VOBJ); uint256 hashBlock; CTransaction vintx; std::vector<uint8_t> origpubkey; std::string name,description; char str[67],numstr[65];
if ( GetTransaction(assetid,vintx,hashBlock,false) == 0 )
{
fprintf(stderr,"cant find assetid\n");
result.push_back(Pair("result","error"));
result.push_back(Pair("error","cant find assetid"));
return(result);
}
if ( vintx.vout.size() > 0 && DecodeAssetCreateOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,origpubkey,name,description) == 0 )
{
fprintf(stderr,"assetid isnt assetcreation txid\n");
result.push_back(Pair("result","error"));
result.push_back(Pair("error","assetid isnt assetcreation txid"));
}
result.push_back(Pair("result","success"));
result.push_back(Pair("tokenid",uint256_str(str,assetid)));
result.push_back(Pair("owner",pubkey33_str(str,origpubkey.data())));
result.push_back(Pair("name",name));
result.push_back(Pair("supply",vintx.vout[0].nValue));
result.push_back(Pair("description",description));
return(result);
}
UniValue AssetList()
{
UniValue result(UniValue::VARR); std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex; struct CCcontract_info *cp,C; uint256 txid,hashBlock; CTransaction vintx; std::vector<uint8_t> origpubkey; std::string name,description; char str[65];
cp = CCinit(&C,EVAL_ASSETS);
SetCCtxids(addressIndex,cp->normaladdr);
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
{
txid = it->first.txhash;
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
{
if ( vintx.vout.size() > 0 && DecodeAssetCreateOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,origpubkey,name,description) != 0 )
{
result.push_back(uint256_str(str,txid));
}
}
}
return(result);
}
UniValue AssetOrders(uint256 refassetid)
{
static uint256 zero;
int64_t price; uint256 txid,hashBlock,assetid,assetid2; std::vector<uint8_t> origpubkey; CTransaction vintx; UniValue result(UniValue::VARR); std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; uint8_t funcid; char numstr[32],funcidstr[16],origaddr[64],assetidstr[65]; struct CCcontract_info *cp,C;
cp = CCinit(&C,EVAL_ASSETS);
SetCCunspents(unspentOutputs,(char *)cp->unspendableCCaddr);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
{
UniValue result(UniValue::VARR);
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputsTokens, unspentOutputsAssets;
struct CCcontract_info *cpTokens, tokensC;
struct CCcontract_info *cpAssets, assetsC;
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
cpAssets = CCinit(&assetsC, EVAL_ASSETS);
auto addOrders = [&](struct CCcontract_info *cp, std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it)
{
uint256 txid, hashBlock, assetid, assetid2;
int64_t price;
std::vector<uint8_t> origpubkey;
CTransaction vintx;
uint8_t funcid, evalCode;
char numstr[32], funcidstr[16], origaddr[64], assetidstr[65];
txid = it->first.txhash;
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
//std::cerr << "addOrders() txid=" << txid.GetHex() << std::endl;
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
{
if ( vintx.vout.size() > 0 && (funcid= DecodeAssetOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,assetid,assetid2,price,origpubkey)) != 0 )
// for logging: funcid = DecodeAssetOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey);
//std::cerr << "addOrders() vintx.vout.size()=" << vintx.vout.size() << " funcid=" << (char)(funcid ? funcid : ' ') << " assetid=" << assetid.GetHex() << std::endl;
if (vintx.vout.size() > 0 && (funcid = DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) != 0)
{
if ( refassetid != zero && assetid != refassetid )
if (refassetid != zero && assetid != refassetid)
{
//int32_t z;
//for (z=31; z>=0; z--) fprintf(stderr,"%02x",((uint8_t *)&txid)[z]);
@ -131,11 +104,15 @@ UniValue AssetOrders(uint256 refassetid)
//fprintf(stderr," assetid\n");
//for (z=31; z>=0; z--) fprintf(stderr,"%02x",((uint8_t *)&refassetid)[z]);
//fprintf(stderr," refassetid\n");
continue;
return;
}
if ( vintx.vout[it->first.index].nValue == 0 )
continue;
//std::cerr << "addOrders() it->first.index=" << it->first.index << " vintx.vout[it->first.index].nValue=" << vintx.vout[it->first.index].nValue << std::endl;
if (vintx.vout[it->first.index].nValue == 0)
return;
UniValue item(UniValue::VOBJ);
funcidstr[0] = funcid;
funcidstr[1] = 0;
item.push_back(Pair("funcid", funcidstr));
@ -157,8 +134,8 @@ UniValue AssetOrders(uint256 refassetid)
}
if ( origpubkey.size() == 33 )
{
GetCCaddress(cp,origaddr,pubkey2pk(origpubkey));
item.push_back(Pair("origaddress",origaddr));
GetCCaddress(cp, origaddr, pubkey2pk(origpubkey)); // TODO: what is this? is it asset or token??
item.push_back(Pair("origaddress", origaddr));
}
if ( assetid != zeroid )
item.push_back(Pair("tokenid",uint256_str(assetidstr,assetid)));
@ -181,14 +158,35 @@ UniValue AssetOrders(uint256 refassetid)
}
}
result.push_back(item);
//fprintf(stderr,"func.(%c) %s/v%d %.8f\n",funcid,uint256_str(assetidstr,txid),(int32_t)it->first.index,(double)vintx.vout[it->first.index].nValue/COIN);
//fprintf(stderr,"addOrders() func.(%c) %s/v%d %.8f\n",funcid,uint256_str(assetidstr,txid),(int32_t)it->first.index,(double)vintx.vout[it->first.index].nValue/COIN);
}
}
}
};
char assetsUnspendableAddr[64];
GetCCaddress(cpAssets, assetsUnspendableAddr, GetUnspendable(cpAssets, NULL));
SetCCunspents(unspentOutputsAssets, assetsUnspendableAddr /*(char *)cpTokens->unspendableCCaddr*/);
char tokensUnspendableAddr[64];
GetTokensCCaddress(cpAssets, tokensUnspendableAddr, GetUnspendable(cpAssets, NULL));
SetCCunspents(unspentOutputsAssets, tokensUnspendableAddr /*(char *)cpAssets->unspendableCCaddr*/);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator itTokens = unspentOutputsTokens.begin();
itTokens != unspentOutputsTokens.end();
itTokens++)
addOrders(cpTokens, itTokens);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator itAssets = unspentOutputsAssets.begin();
itAssets != unspentOutputsAssets.end();
itAssets++)
addOrders(cpAssets, itAssets);
return(result);
}
std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::string description)
// not used (use TokenCreate instead)
/* std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::string description)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; struct CCcontract_info *cp,C;
@ -213,9 +211,10 @@ std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeAssetCreateOpRet('c',Mypubkey(),name,description)));
}
return("");
}
std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total)
} */
// not used (use TokenTransfer instead)
/* std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; uint64_t mask; int64_t CCchange=0,inputs=0; struct CCcontract_info *cp,C;
@ -230,11 +229,11 @@ std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector<uint8_t> des
mypk = pubkey2pk(Mypubkey());
if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 )
{
/*n = outputs.size();
if ( n == amounts.size() )
{
for (i=0; i<n; i++)
total += amounts[i];*/
//n = outputs.size();
//if ( n == amounts.size() )
//{
// for (i=0; i<n; i++)
// total += amounts[i];
mask = ~((1LL << mtx.vin.size()) - 1);
if ( (inputs= AddAssetInputs(cp,mtx,mypk,assetid,total,60)) > 0 )
{
@ -254,9 +253,10 @@ std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector<uint8_t> des
//} else fprintf(stderr,"numoutputs.%d != numamounts.%d\n",n,(int32_t)amounts.size());
}
return("");
}
} */
std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total,int32_t evalcode)
// deprecated
/* std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total,int32_t evalcode)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; int64_t CCchange=0,inputs=0; struct CCcontract_info *cp,C;
@ -281,76 +281,121 @@ std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector<uint8_t> dest
} else fprintf(stderr,"not enough CC asset inputs for %.8f\n",(double)total/COIN);
}
return("");
}
} */
std::string CreateBuyOffer(int64_t txfee,int64_t bidamount,uint256 assetid,int64_t pricetotal)
// rpc tokenbid implementation, locks 'bidamount' coins for the 'pricetotal' of tokens
std::string CreateBuyOffer(int64_t txfee, int64_t bidamount, uint256 assetid, int64_t pricetotal)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; struct CCcontract_info *cp,C; uint256 hashBlock; CTransaction vintx; std::vector<uint8_t> origpubkey; std::string name,description;
if ( bidamount < 0 || pricetotal < 0 )
CPubKey mypk;
struct CCcontract_info *cpAssets, C;
uint256 hashBlock;
CTransaction vintx;
std::vector<uint8_t> origpubkey;
std::string name,description;
int64_t inputs;
std::cerr << "CreateBuyOffer() bidamount=" << bidamount << " numtokens(pricetotal)=" << pricetotal << std::endl;
if (bidamount < 0 || pricetotal < 0)
{
fprintf(stderr,"negative bidamount %lld, pricetotal %lld\n",(long long)bidamount,(long long)pricetotal);
fprintf(stderr,"negative bidamount %lld, pricetotal %lld\n", (long long)bidamount, (long long)pricetotal);
return("");
}
if ( GetTransaction(assetid,vintx,hashBlock,false) == 0 )
if (GetTransaction(assetid, vintx, hashBlock, false) == 0)
{
fprintf(stderr,"cant find assetid\n");
return("");
}
if ( vintx.vout.size() > 0 && DecodeAssetCreateOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,origpubkey,name,description) == 0 )
if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey, origpubkey, name, description) == 0)
{
fprintf(stderr,"assetid isnt assetcreation txid\n");
return("");
}
cp = CCinit(&C,EVAL_ASSETS);
if ( txfee == 0 )
cpAssets = CCinit(&C,EVAL_ASSETS); // NOTE: assets here!
if (txfee == 0)
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
if ( AddNormalinputs(mtx,mypk,bidamount+txfee,64) > 0 )
if ((inputs = AddNormalinputs(mtx, mypk, bidamount+(2*txfee), 64)) > 0)
{
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,bidamount,GetUnspendable(cp,0)));
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeAssetOpRet('b',assetid,zeroid,pricetotal,Mypubkey())));
std::cerr << "CreateBuyOffer() inputs=" << inputs << std::endl;
if (inputs < bidamount+txfee) {
std::cerr << "CreateBuyOffer(): insufficient coins to make buy offer" << std::endl;
CCerror = strprintf("insufficient coins to make buy offer");
return ("");
}
CPubKey unspendablePubkey = GetUnspendable(cpAssets, 0);
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount, unspendablePubkey));
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, mypk));
std::vector<CPubKey> voutTokenPubkeys; // should be empty - no token vouts
return(FinalizeCCTx(0, cpAssets, mtx, mypk, txfee,
EncodeTokenOpRet(assetid, voutTokenPubkeys,
EncodeAssetOpRet('b', zeroid, pricetotal, Mypubkey()))));
}
CCerror = strprintf("no coins found to make buy offer");
return("");
}
// rpc tokenask implementation, locks 'askamount' tokens for the 'pricetotal'
std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t pricetotal)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; uint64_t mask; int64_t inputs,CCchange; CScript opret; struct CCcontract_info *cp,C;
CPubKey mypk;
uint64_t mask;
int64_t inputs, CCchange;
CScript opret;
struct CCcontract_info *cpAssets, assetsC;
struct CCcontract_info *cpTokens, tokensC;
//std::cerr << "CreateSell() askamount=" << askamount << " pricetotal=" << pricetotal << std::endl;
if ( askamount < 0 || pricetotal < 0 )
{
if (askamount < 0 || pricetotal < 0) {
fprintf(stderr,"negative askamount %lld, askamount %lld\n",(long long)pricetotal,(long long)askamount);
return("");
}
cp = CCinit(&C,EVAL_ASSETS);
if ( txfee == 0 )
cpAssets = CCinit(&assetsC, EVAL_ASSETS); // NOTE: this is for signing
if (txfee == 0)
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 ( (inputs= AddAssetInputs(cp,mtx,mypk,assetid,askamount,60)) > 0 )
// add single-eval tokens:
cpTokens = CCinit(&tokensC, EVAL_TOKENS); // NOTE: tokens is here
if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, askamount, 60)) > 0)
{
if (inputs < askamount) {
//askamount = inputs;
//was: askamount = inputs;
std::cerr << "CreateSell(): insufficient tokens for ask" << std::endl;
CCerror = strprintf("insufficient tokens for ask");
return ("");
}
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,askamount,GetUnspendable(cp,0)));
if ( inputs > askamount )
CPubKey unspendablePubkey = GetUnspendable(cpAssets, NULL);
mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, askamount, unspendablePubkey));
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, mypk)); //marker
if (inputs > askamount)
CCchange = (inputs - askamount);
if ( CCchange != 0 )
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk));
opret = EncodeAssetOpRet('s',assetid,zeroid,pricetotal,Mypubkey());
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,opret));
if (CCchange != 0)
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); // change to single-eval token vout
std::vector<CPubKey> voutTokenPubkeys;
voutTokenPubkeys.push_back(unspendablePubkey);
opret = EncodeTokenOpRet(assetid, voutTokenPubkeys,
EncodeAssetOpRet('s', zeroid, pricetotal, Mypubkey()));
return(FinalizeCCTx(mask,cpAssets, mtx, mypk, txfee, opret));
}
else {
fprintf(stderr, "need some assets to place ask\n");
fprintf(stderr, "need some tokens to place ask\n");
}
}
else { // dimxy added 'else', because it was misleading message before
@ -359,39 +404,62 @@ std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t p
return("");
}
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 assetid2,int64_t pricetotal)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; uint64_t mask; int64_t inputs,CCchange; CScript opret; struct CCcontract_info *cp,C;
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
fprintf(stderr,"asset swaps disabled\n");
return("");
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
if ( askamount < 0 || pricetotal < 0 )
{
fprintf(stderr,"negative askamount %lld, askamount %lld\n",(long long)pricetotal,(long long)askamount);
return("");
}
cp = CCinit(&C,EVAL_ASSETS);
cp = CCinit(&C, EVAL_ASSETS);
if ( txfee == 0 )
txfee = 10000;
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
mypk = pubkey2pk(Mypubkey());
if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 )
if (AddNormalinputs(mtx, mypk, txfee, 3) > 0)
{
mask = ~((1LL << mtx.vin.size()) - 1);
if ( (inputs= AddAssetInputs(cp,mtx,mypk,assetid,askamount,60)) > 0 )
if ((inputs = AddAssetInputs(cp, mtx, mypk, assetid, askamount, 60)) > 0)
{
if ( inputs < askamount )
askamount = inputs;
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,askamount,GetUnspendable(cp,0)));
if ( inputs > askamount )
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
if (inputs < askamount) {
//was: askamount = inputs;
std::cerr << "CreateSwap(): insufficient tokens for ask" << std::endl;
CCerror = strprintf("insufficient tokens for ask");
return ("");
}
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
CPubKey unspendablePubkey = GetUnspendable(cp, 0);
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, askamount, unspendablePubkey));
if (inputs > askamount)
CCchange = (inputs - askamount);
if ( CCchange != 0 )
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk));
if ( assetid2 == zeroid )
opret = EncodeAssetOpRet('s',assetid,zeroid,pricetotal,Mypubkey());
else
{
opret = EncodeAssetOpRet('e',assetid,assetid2,pricetotal,Mypubkey());
if (CCchange != 0)
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, CCchange, mypk));
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
std::vector<CPubKey> voutTokenPubkeys; // should be empty - no token vouts
if (assetid2 == zeroid) {
opret = EncodeTokenOpRet(assetid, voutTokenPubkeys,
EncodeAssetOpRet('s', zeroid, pricetotal, Mypubkey()));
}
else {
opret = EncodeTokenOpRet(assetid, voutTokenPubkeys,
EncodeAssetOpRet('e', assetid2, pricetotal, Mypubkey()));
}
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,opret));
}
else {
@ -403,148 +471,306 @@ std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 a
}
return("");
}
} ////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
// unlocks coins
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 *cp,C;
cp = CCinit(&C,EVAL_ASSETS);
if ( txfee == 0 )
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<uint8_t> dummyOrigpubkey;
cpAssets = CCinit(&C, EVAL_ASSETS);
if (txfee == 0)
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 )
if (AddNormalinputs(mtx, mypk, txfee, 3) > 0)
{
mask = ~((1LL << mtx.vin.size()) - 1);
if ( GetTransaction(bidtxid,vintx,hashBlock,false) != 0 )
if (GetTransaction(bidtxid, vintx, hashBlock, false) != 0)
{
bidamount = vintx.vout[0].nValue;
mtx.vin.push_back(CTxIn(bidtxid,0,CScript()));
mtx.vin.push_back(CTxIn(bidtxid, 0, CScript())); // coins in Assets
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));
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,EncodeAssetOpRet('o',assetid,zeroid,0,Mypubkey())));
mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
std::vector<CPubKey> voutTokenPubkeys; // should be empty, no token vouts
return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee,
EncodeTokenOpRet(assetid, voutTokenPubkeys,
EncodeAssetOpRet('o', zeroid, 0, Mypubkey()))));
}
}
return("");
}
//unlocks tokens
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 *cp,C;
cp = CCinit(&C,EVAL_ASSETS);
if ( txfee == 0 )
CTransaction vintx; uint64_t mask;
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<uint8_t> dummyOrigpubkey;
cpAssets = CCinit(&assetsC, EVAL_ASSETS);
if (txfee == 0)
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 )
if (AddNormalinputs(mtx, mypk, txfee, 3) > 0)
{
mask = ~((1LL << mtx.vin.size()) - 1);
if ( GetTransaction(asktxid,vintx,hashBlock,false) != 0 )
if (GetTransaction(asktxid, vintx, hashBlock, false) != 0)
{
askamount = vintx.vout[0].nValue;
mtx.vin.push_back(CTxIn(asktxid,0,CScript()));
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,askamount,mypk));
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,EncodeAssetOpRet('x',assetid,zeroid,0,Mypubkey())));
mtx.vin.push_back(CTxIn(asktxid, 0, CScript()));
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));
std::vector<CPubKey> voutTokenPubkeys;
voutTokenPubkeys.push_back(mypk);
/* char myCCaddr[65];
uint8_t myPrivkey[32];
Myprivkey(myPrivkey);
cpAssets = CCinit(&assetsC, EVAL_ASSETS);
GetCCaddress(cpAssets, myCCaddr, mypk); */
// this is only for unspendable addresses:
//CCaddr2set(cpTokens, EVAL_ASSETS, mypk, myPrivkey, myCCaddr); //do we need this? Seems FinalizeCCTx can attach to any evalcode cc addr by calling Getscriptaddress
uint8_t unspendableAssetsPrivkey[32];
char unspendableAssetsAddr[64];
// init assets 'unspendable' privkey and pubkey
CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey);
GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk);
// add additional eval-tokens unspendable assets privkey:
CCaddr2set(cpAssets, EVAL_TOKENS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr);
return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee,
EncodeTokenOpRet(assetid, voutTokenPubkeys,
EncodeAssetOpRet('x', zeroid, 0, Mypubkey()))));
}
}
return("");
}
//send tokens, receive coins:
std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t fillamount)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CTransaction vintx; uint256 hashBlock; CPubKey mypk; std::vector<uint8_t> origpubkey; int32_t bidvout=0; uint64_t mask; int64_t origprice,bidamount,paid_amount,remaining_required,inputs,CCchange=0; struct CCcontract_info *cp,C;
if ( fillamount < 0 )
CTransaction vintx;
uint256 hashBlock;
CPubKey mypk;
std::vector<uint8_t> origpubkey;
int32_t bidvout=0;
uint64_t mask;
int64_t origprice, bidamount, paid_amount, remaining_required, inputs, CCchange=0;
struct CCcontract_info *cpTokens, tokensC;
struct CCcontract_info *cpAssets, assetsC;
if (fillamount < 0)
{
fprintf(stderr,"negative fillamount %lld\n",(long long)fillamount);
fprintf(stderr,"negative fillamount %lld\n", (long long)fillamount);
return("");
}
cp = CCinit(&C,EVAL_ASSETS);
if ( txfee == 0 )
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
if (txfee == 0)
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(bidtxid,vintx,hashBlock,false) != 0 )
if (GetTransaction(bidtxid, vintx, hashBlock, false) != 0)
{
bidamount = vintx.vout[bidvout].nValue;
SetAssetOrigpubkey(origpubkey,origprice,vintx);
mtx.vin.push_back(CTxIn(bidtxid,bidvout,CScript()));
if ( (inputs= AddAssetInputs(cp,mtx,mypk,assetid,fillamount,60)) > 0 )
SetAssetOrigpubkey(origpubkey, origprice, vintx);
mtx.vin.push_back(CTxIn(bidtxid, bidvout, CScript())); // Coins on Assets unspendable
if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, fillamount, 60)) > 0)
{
if ( inputs < fillamount )
fillamount = inputs;
SetBidFillamounts(paid_amount,remaining_required,bidamount,fillamount,origprice);
if ( inputs > fillamount )
if (inputs < fillamount) {
std::cerr << "FillBuyOffer(): insufficient tokens to fill buy offer" << std::endl;
CCerror = strprintf("insufficient tokens to fill buy offer");
return ("");
}
SetBidFillamounts(paid_amount, remaining_required, bidamount, fillamount, origprice);
if (inputs > fillamount)
CCchange = (inputs - fillamount);
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,bidamount - paid_amount,GetUnspendable(cp,0)));
mtx.vout.push_back(CTxOut(paid_amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,fillamount,pubkey2pk(origpubkey)));
if ( CCchange != 0 )
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk));
fprintf(stderr,"remaining %llu -> origpubkey\n",(long long)remaining_required);
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,EncodeAssetOpRet('B',assetid,zeroid,remaining_required,origpubkey)));
} else return("dont have any assets to fill bid\n");
uint8_t unspendableAssetsPrivkey[32];
cpAssets = CCinit(&assetsC, EVAL_ASSETS);
CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey);
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)); // vout4 change in single-eval tokens
fprintf(stderr,"FillBuyOffer remaining %llu -> origpubkey\n", (long long)remaining_required);
char unspendableAssetsAddr[64];
cpAssets = CCinit(&assetsC, EVAL_ASSETS);
GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk);
// add additional unspendable addr from Assets:
CCaddr2set(cpTokens, EVAL_ASSETS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr);
// token vout verification pubkeys:
std::vector<CPubKey> voutTokenPubkeys;
voutTokenPubkeys.push_back(pubkey2pk(origpubkey));
return(FinalizeCCTx(mask,cpTokens,mtx,mypk,txfee,
EncodeTokenOpRet(assetid, voutTokenPubkeys,
EncodeAssetOpRet('B', zeroid, remaining_required, origpubkey))));
} else return("dont have any assets to fill bid");
}
}
return("no normal coins left");
}
std::string FillSell(int64_t txfee,uint256 assetid,uint256 assetid2,uint256 asktxid,int64_t fillunits)
// send coins, receive tokens
std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 asktxid, int64_t fillunits)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CTransaction vintx,filltx; uint256 hashBlock; CPubKey mypk; std::vector<uint8_t> origpubkey; double dprice; uint64_t mask; int32_t askvout=0; int64_t received_assetoshis,total_nValue,orig_assetoshis,paid_nValue,remaining_nValue,inputs,CCchange=0; struct CCcontract_info *cp,C;
if ( fillunits < 0 )
CTransaction vintx,filltx;
uint256 hashBlock;
CPubKey mypk;
std::vector<uint8_t> origpubkey;
double dprice;
uint64_t mask = 0;
int32_t askvout = 0;
int64_t received_assetoshis, total_nValue, orig_assetoshis, paid_nValue, remaining_nValue, inputs, CCchange=0;
//struct CCcontract_info *cpTokens, tokensC;
struct CCcontract_info *cpAssets, assetsC;
if (fillunits < 0)
{
CCerror = strprintf("negative fillunits %lld\n",(long long)fillunits);
fprintf(stderr,"%s\n",CCerror.c_str());
return("");
}
if ( assetid2 != zeroid )
if (assetid2 != zeroid)
{
CCerror = "asset swaps disabled";
fprintf(stderr,"%s\n",CCerror.c_str());
return("");
}
cp = CCinit(&C,EVAL_ASSETS);
if ( txfee == 0 )
cpAssets = CCinit(&assetsC, EVAL_ASSETS);
if (txfee == 0)
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 )
if (GetTransaction(asktxid, vintx, hashBlock, false) != 0)
{
orig_assetoshis = vintx.vout[askvout].nValue;
SetAssetOrigpubkey(origpubkey,total_nValue,vintx);
SetAssetOrigpubkey(origpubkey, total_nValue, vintx);
dprice = (double)total_nValue / orig_assetoshis;
paid_nValue = dprice * fillunits;
mtx.vin.push_back(CTxIn(asktxid,askvout,CScript()));
if ( assetid2 != zeroid )
inputs = AddAssetInputs(cp,mtx,mypk,assetid2,paid_nValue,60);
mtx.vin.push_back(CTxIn(asktxid, askvout, CScript())); // NOTE: this is the reference to tokens -> send cpTokens for signing into FinalizeCCTx!
if (assetid2 != zeroid)
inputs = AddAssetInputs(cpAssets, mtx, mypk, assetid2, paid_nValue, 60); // not implemented yet
else
{
inputs = AddNormalinputs(mtx,mypk,paid_nValue,60);
inputs = AddNormalinputs(mtx, mypk, paid_nValue, 60);
mask = ~((1LL << mtx.vin.size()) - 1);
}
if ( inputs > 0 )
if (inputs > 0)
{
if ( inputs < paid_nValue )
paid_nValue = inputs;
if ( assetid2 != zeroid )
SetSwapFillamounts(received_assetoshis,remaining_nValue,orig_assetoshis,paid_nValue,total_nValue);
else SetAskFillamounts(received_assetoshis,remaining_nValue,orig_assetoshis,paid_nValue,total_nValue);
if ( assetid2 != zeroid && inputs > paid_nValue )
if (inputs < paid_nValue) {
std::cerr << "FillSell(): insufficient coins to fill sell" << std::endl;
CCerror = strprintf("insufficient coins to fill sell");
return ("");
}
if (assetid2 != zeroid)
SetSwapFillamounts(received_assetoshis, remaining_nValue, orig_assetoshis, paid_nValue, total_nValue); //not implemented correctly yet
else
SetAskFillamounts(received_assetoshis, remaining_nValue, orig_assetoshis, paid_nValue, total_nValue);
if (assetid2 != zeroid && inputs > paid_nValue)
CCchange = (inputs - paid_nValue);
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,orig_assetoshis - received_assetoshis,GetUnspendable(cp,0)));
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,received_assetoshis,mypk));
if ( assetid2 != zeroid )
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,paid_nValue,origpubkey));
else mtx.vout.push_back(CTxOut(paid_nValue,CScript() << origpubkey << OP_CHECKSIG));
if ( CCchange != 0 )
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk));
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,EncodeAssetOpRet(assetid2!=zeroid?'E':'S',assetid,assetid2,remaining_nValue,origpubkey)));
mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, orig_assetoshis - received_assetoshis, GetUnspendable(cpAssets, NULL))); // vout.0 tokens remainder to unspendable cc addr
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, received_assetoshis, mypk)); //vout.1 purchased tokens to self single-eval addr
// NOTE: no marker here
if (assetid2 != zeroid) {
std::cerr << "FillSell() WARNING: asset swap not implemented yet! (paid_nValue)" << std::endl;
// TODO: change MakeCC1vout appropriately when implementing:
//mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, paid_nValue, origpubkey)); //vout.2 tokens... (swap is not implemented yet)
}
else {
//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) {
std::cerr << "FillSell() WARNING: asset swap not implemented yet! (CCchange)" << std::endl;
// TODO: change MakeCC1vout appropriately when implementing:
//mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, CCchange, mypk)); //vout.3 coins in Assets cc addr (swap not implemented)
}
uint8_t unspendableAssetsPrivkey[32];
char unspendableAssetsAddr[64];
// init assets 'unspendable' privkey and pubkey
CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey);
GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk);
// add additional eval-tokens unspendable assets privkey:
CCaddr2set(cpAssets, EVAL_TOKENS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr);
// vout verification pubkeys:
std::vector<CPubKey> voutTokenPubkeys;
voutTokenPubkeys.push_back(mypk);
return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee,
EncodeTokenOpRet(assetid, voutTokenPubkeys,
EncodeAssetOpRet(assetid2 != zeroid ? 'E' : 'S', assetid2, remaining_nValue, origpubkey))));
} else {
CCerror = strprintf("filltx not enough utxos");
fprintf(stderr,"%s\n", CCerror.c_str());

2
src/cc/CCchannels.h

@ -21,7 +21,7 @@
#define CHANNELS_MAXPAYMENTS 1000
bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64_t payment);
std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64_t payment,uint256 tokenid);
std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint256 secret);
std::string ChannelClose(uint64_t txfee,uint256 opentxid);
std::string ChannelRefund(uint64_t txfee,uint256 opentxid,uint256 closetxid);

54
src/cc/CCcustom.cpp

@ -30,6 +30,7 @@
#include "CCMarmara.h"
#include "CCPayments.h"
#include "CCGateways.h"
#include "CCtokens.h"
/*
CCcustom has most of the functions that need to be extended to create a new CC contract.
@ -222,6 +223,46 @@ uint8_t GatewaysCCpriv[32] = { 0xf7, 0x4b, 0x5b, 0xa2, 0x7a, 0x5e, 0x9c, 0xda, 0
#undef FUNCNAME
#undef EVALCODE
// Tokens
#define FUNCNAME IsTokensInput
#define EVALCODE EVAL_TOKENS
const char *TokensCCaddr = "RAMvUfoyURBRxAdVeTMHxn3giJZCFWeha2";
const char *TokensNormaladdr = "RCNgAngYAdrfzujYyPgfbjCGNVQZzCgTad";
char TokensCChexstr[67] = { "03e6191c70c9c9a28f9fd87089b9488d0e6c02fb629df64979c9cdb6b2b4a68d95" };
uint8_t TokensCCpriv[32] = { 0x1d, 0x0d, 0x0d, 0xce, 0x2d, 0xd2, 0xe1, 0x9d, 0xf5, 0xb6, 0x26, 0xd5, 0xad, 0xa0, 0xf0, 0x0a, 0xdd, 0x7a, 0x72, 0x7d, 0x17, 0x35, 0xb5, 0xe3, 0x2c, 0x6c, 0xa9, 0xa2, 0x03, 0x16, 0x4b, 0xcf };
#include "CCcustom.inc"
#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)
{
cp->evalcode = evalcode;
@ -347,6 +388,19 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode)
cp->validate = GatewaysValidate;
cp->ismyvin = IsGatewaysInput;
break;
case EVAL_TOKENS:
strcpy(cp->unspendableCCaddr, TokensCCaddr);
strcpy(cp->normaladdr, TokensNormaladdr);
strcpy(cp->CChexstr, TokensCChexstr);
memcpy(cp->CCpriv, TokensCCpriv, 32);
cp->validate = TokensValidate;
cp->ismyvin = IsTokensInput;
break;
default:
if ( CClib_initcp(cp,evalcode) < 0 )
return(0);
break;
}
return(cp);
}

86
src/cc/CCinclude.h

@ -51,18 +51,20 @@ 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;
#define CC_MAXVINS 1024
#define SMALLVAL 0.000000000000001
#define SATOSHIDEN ((uint64_t)100000000L)
#define dstr(x) ((double)(x) / SATOSHIDEN)
#ifndef _BITS256
#define _BITS256
union _bits256 { uint8_t bytes[32]; uint16_t ushorts[16]; uint32_t uints[8]; uint64_t ulongs[4]; uint64_t txid; };
typedef union _bits256 bits256;
#endif
#include "../komodo_cJSON.h"
struct CC_utxo
{
@ -84,12 +86,31 @@ struct CC_meta
struct CCcontract_info
{
char unspendableCCaddr[64],CChexstr[72],normaladdr[64],unspendableaddr2[64],unspendableaddr3[64];
uint8_t CCpriv[32],unspendablepriv2[32],unspendablepriv3[32];
CPubKey unspendablepk2,unspendablepk3;
bool (*validate)(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn);
bool (*ismyvin)(CScript const& scriptSig);
uint8_t evalcode,evalcode2,evalcode3,didinit;
// this is for spending from 'unspendable' CC address
uint8_t evalcode;
char unspendableCCaddr[64], CChexstr[72], normaladdr[64];
uint8_t CCpriv[32];
// this for 1of2 keys coins cryptocondition (for this evalcode)
// NOTE: only one evalcode is allowed at this time
char coins1of2addr[64];
CPubKey coins1of2pk[2];
// the same for tokens 1of2 keys cc
char tokens1of2addr[64];
CPubKey tokens1of2pk[2];
// this is for spending from two additional 'unspendable' CC addresses of other eval codes
// (that is, for spending from several cc contract 'unspendable' addresses):
uint8_t evalcode2, evalcode3;
char unspendableaddr2[64], unspendableaddr3[64];
uint8_t unspendablepriv2[32], unspendablepriv3[32];
CPubKey unspendablepk2, unspendablepk3;
bool (*validate)(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn); // cc contract tx validation callback
bool (*ismyvin)(CScript const& scriptSig); // checks if evalcode is present in the scriptSig param
uint8_t didinit;
};
struct CCcontract_info *CCinit(struct CCcontract_info *cp,uint8_t evalcode);
@ -103,15 +124,21 @@ struct oracleprice_info
#ifdef ENABLE_WALLET
extern CWallet* pwalletMain;
#endif
//extern CCoinsViewCache *pcoinsTip;
bool GetAddressUnspent(uint160 addressHash, int type,std::vector<std::pair<CAddressUnspentKey,CAddressUnspentValue> > &unspentOutputs);
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);
int32_t is_hexstr(char *str,int32_t n);
bool myAddtomempool(CTransaction &tx, CValidationState *pstate = NULL, bool fSkipExpiry = false);
//uint64_t myGettxout(uint256 hash,int32_t n);
int32_t CCgettxout(uint256 txid,int32_t vout,int32_t mempoolflag);
bool myIsutxo_spentinmempool(uint256 txid,int32_t vout);
bool mytxid_inmempool(uint256 txid);
int32_t myIsutxo_spent(uint256 &spenttxid,uint256 txid,int32_t vout);
@ -128,13 +155,25 @@ int64_t OraclePrice(int32_t height,uint256 reforacletxid,char *markeraddr,char *
uint8_t DecodeOraclesCreateOpRet(const CScript &scriptPubKey,std::string &name,std::string &description,std::string &format);
uint256 OracleMerkle(int32_t height,uint256 reforacletxid,char *format,std::vector<struct oracle_merklepair>publishers);
uint256 OraclesBatontxid(uint256 oracletxid,CPubKey pk);
int64_t AddAssetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 assetid,int64_t total,int32_t maxinputs);
//int64_t AddAssetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 assetid,int64_t total,int32_t maxinputs);
int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs);
int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid);
bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx);
bool DecodeAssetCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description);
uint8_t DecodeAssetOpRet(const CScript &scriptPubKey,uint256 &assetid,uint256 &assetid2,int64_t &price,std::vector<uint8_t> &origpubkey);
CScript EncodeAssetOpRet(uint8_t assetFuncId, uint256 assetid2, int64_t price, std::vector<uint8_t> origpubkey);
//bool DecodeAssetCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description);
uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &evalCodeInOpret, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector<uint8_t> &origpubkey);
CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, CScript payload);
CScript EncodeTokenOpRet(uint8_t tokenFuncId, uint8_t evalCodeInOpret, uint256 tokenid, std::vector<CPubKey> voutPubkeys, CScript payload);
uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description);
uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 &tokenid, std::vector<CPubKey> &voutPubkeys, std::vector<uint8_t> &vopretExtra);
uint8_t DecodeOraclesData(const CScript &scriptPubKey,uint256 &oracletxid,uint256 &batontxid,CPubKey &pk,std::vector <uint8_t>&data);
int32_t oracle_format(uint256 *hashp,int64_t *valp,char *str,uint8_t fmt,uint8_t *data,int32_t offset,int32_t datalen);
CScript EncodeAssetOpRet(uint8_t funcid,uint256 assetid,uint256 assetid2,int64_t price,std::vector<uint8_t> origpubkey);
// CCcustom
CPubKey GetUnspendable(struct CCcontract_info *cp,uint8_t *unspendablepriv);
@ -150,6 +189,18 @@ CC *MakeCCcond1of2(uint8_t evalcode,CPubKey pk1,CPubKey pk2);
CC* GetCryptoCondition(CScript const& scriptSig);
void CCaddr2set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr);
void CCaddr3set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr);
void CCaddr1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *coinaddr);
CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2);
CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk);
CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2);
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);
uint64_t stringbits(char *str);
@ -172,9 +223,11 @@ std::vector<uint8_t> Mypubkey();
bool Myprivkey(uint8_t myprivkey[]);
int64_t CCduration(int32_t &numblocks,uint256 txid);
bool komodo_txnotarizedconfirmed(uint256 txid);
CPubKey check_signing_pubkey(CScript scriptSig);
// CCtx
bool SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScript scriptPubKey);
std::string FinalizeCCTx(uint64_t skipmask,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey mypk,uint64_t txfee,CScript opret);
extern std::vector<CPubKey> NULL_pubkeys;
std::string FinalizeCCTx(uint64_t skipmask,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey mypk,uint64_t txfee,CScript opret,std::vector<CPubKey> pubkeys = NULL_pubkeys);
void SetCCunspents(std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &unspentOutputs,char *coinaddr);
void SetCCtxids(std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex,char *coinaddr);
int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int32_t maxinputs);
@ -187,5 +240,6 @@ bits256 curve25519_basepoint9();
bits256 curve25519(bits256 mysecret,bits256 basepoint);
void vcalc_sha256(char deprecated[(256 >> 3) * 2 + 1],uint8_t hash[256 >> 3],uint8_t *src,int32_t len);
bits256 bits256_doublesha256(char *deprecated,uint8_t *data,int32_t datalen);
UniValue ValueFromAmount(const CAmount& amount);
#endif

768
src/cc/CCtokens.cpp

@ -0,0 +1,768 @@
/******************************************************************************
* Copyright © 2014-2018 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. *
* *
******************************************************************************/
#include "CCtokens.h"
/* TODO: correct this:
-----------------------------
The SetTokenFillamounts() and ValidateTokenRemainder() work in tandem to calculate the vouts for a fill and to validate the vouts, respectively.
This pair of functions are critical to make sure the trading is correct and is the trickiest part of the tokens contract.
//vin.0: normal input
//vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
//vin.2+: valid CC output satisfies buyoffer (*tx.vin[2])->nValue
//vout.0: remaining amount of bid to unspendable
//vout.1: vin.1 value to signer of vin.2
//vout.2: vin.2 tokenoshis to original pubkey
//vout.3: CC output for tokenoshis change (if any)
//vout.4: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['B'] [tokenid] [remaining token required] [origpubkey]
ValidateTokenRemainder(remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,totalunits);
Yes, this is quite confusing...
In ValudateTokenRemainder the naming convention is nValue is the coin/token with the offer on the books and "units" is what it is being paid in. The high level check is to make sure we didnt lose any coins or tokens, the harder to validate is the actual price paid as the "orderbook" is in terms of the combined nValue for the combined totalunits.
We assume that the effective unit cost in the orderbook is valid and that that amount was paid and also that any remainder will be close enough in effective unit cost to not matter. At the edge cases, this will probably be not true and maybe some orders wont be practically fillable when reduced to fractional state. However, the original pubkey that created the offer can always reclaim it.
------------------------------
*/
// NOTE: this inital tx won't be used by other contract
// for tokens to be used there should be at least one 't' tx with other contract's custom opret
CScript EncodeTokenCreateOpRet(uint8_t funcid,std::vector<uint8_t> origpubkey,std::string name,std::string description)
{
CScript opret; uint8_t evalcode = EVAL_TOKENS;
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description);
return(opret);
}
// this is for other contracts which use tokens and build customized extra payloads to token's opret:
CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, CScript payload)
{
CScript opret;
uint8_t tokenFuncId = 't';
uint8_t evalCodeInOpret = EVAL_TOKENS;
tokenid = revuint256(tokenid);
uint8_t ccType = 0;
if (voutPubkeys.size() >= 1 && voutPubkeys.size() <= 2)
ccType = voutPubkeys.size();
std::vector<uint8_t> vpayload;
GetOpReturnData(payload, vpayload);
opret << OP_RETURN << E_MARSHAL(ss << evalCodeInOpret << tokenFuncId << tokenid << ccType; \
if (ccType >= 1) ss << voutPubkeys[0]; \
if (ccType == 2) ss << voutPubkeys[1]; \
if (vpayload.size() > 0) ss << vpayload;);
// "error 64: scriptpubkey":
// if (payload.size() > 0)
// opret += payload;
// error 64: scriptpubkey:
// CScript opretPayloadNoOpcode(vpayload);
// return opret + opretPayloadNoOpcode;
// how to attach payload without re-serialization:
// sig_aborted:
// opret.resize(opret.size() + vpayload.size());
// CScript::iterator it = opret.begin() + opret.size();
// for (int i = 0; i < vpayload.size(); i++, it++)
// *it = vpayload[i];
return opret;
}
// overload for compatibility
CScript EncodeTokenOpRet(uint8_t tokenFuncId, uint8_t evalCodeInOpret, uint256 tokenid, std::vector<CPubKey> voutPubkeys, CScript payload)
{
return EncodeTokenOpRet(tokenid, voutPubkeys, payload);
}
uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description)
{
std::vector<uint8_t> vopret; uint8_t dummyEvalcode, funcid, *script;
GetOpReturnData(scriptPubKey, vopret);
script = (uint8_t *)vopret.data();
if ( script != 0 && vopret.size() > 2 && script[0] == EVAL_TOKENS && script[1] == 'c' )
{
if ( E_UNMARSHAL(vopret, ss >> dummyEvalcode; ss >> funcid; ss >> origpubkey; ss >> name; ss >> description) != 0 )
return(funcid);
}
return (uint8_t)0;
}
uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 &tokenid, std::vector<CPubKey> &voutPubkeys, std::vector<uint8_t> &vopretExtra)
{
std::vector<uint8_t> vopret, extra, dummyPubkey;
uint8_t funcId=0, *script, dummyEvalCode, dummyFuncId, ccType;
std::string dummyName; std::string dummyDescription;
CPubKey voutPubkey1, voutPubkey2;
GetOpReturnData(scriptPubKey, vopret);
script = (uint8_t *)vopret.data();
tokenid = zeroid;
if (script != 0 && vopret.size() > 2)
{
// NOTE: if parse error occures, parse might not be able to set error. It is safer to treat that it was eof if it is not set!
bool isEof = true;
evalCode = script[0];
if (evalCode != EVAL_TOKENS)
return (uint8_t)0;
funcId = script[1];
//fprintf(stderr,"decode.[%c]\n",funcId);
switch( funcId )
{
case 'c':
return DecodeTokenCreateOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription);
//break;
case 't':
//not used yet: case 'l':
// NOTE: 'E_UNMARSHAL result==false' means 'parse error' OR 'not eof state'. Consequently, 'result==false' but 'isEof==true' means just 'parse error'
if (E_UNMARSHAL(vopret, ss >> dummyEvalCode; ss >> dummyFuncId; ss >> tokenid; ss >> ccType; if (ccType >= 1) ss >> voutPubkey1; if (ccType == 2) ss >> voutPubkey2; isEof = ss.eof(); vopretExtra = std::vector<uint8_t>(ss.begin(), ss.end()))
|| !isEof)
{
if (!(ccType >= 0 && ccType <= 2)) { //incorrect ccType
std::cerr << "DecodeTokenOpRet() incorrect ccType=" << (int)ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl;
return (uint8_t)0;
}
// add verification pubkeys:
voutPubkeys.clear();
if (voutPubkey1.IsValid())
voutPubkeys.push_back(voutPubkey1);
if (voutPubkey2.IsValid())
voutPubkeys.push_back(voutPubkey2);
tokenid = revuint256(tokenid);
return(funcId);
}
std::cerr << "DecodeTokenOpRet() bad opret format, isEof=" << isEof << " ccType=" << ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl;
return (uint8_t)0;
default:
std::cerr << "DecodeTokenOpRet() illegal funcid=" << (int)funcId << std::endl;
return (uint8_t)0;
}
}
else {
std::cerr << "DecodeTokenOpRet() empty opret, could not parse" << std::endl;
}
return (uint8_t)0;
}
// tx validation
bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
{
static uint256 zero;
CTxDestination address; CTransaction vinTx, createTx; uint256 hashBlock, tokenid, tokenid2;
int32_t i, starti, numvins, numvouts, preventCCvins, preventCCvouts;
int64_t remaining_price, nValue, tokenoshis, outputs, inputs, tmpprice, totalunits, ignore;
std::vector<uint8_t> vopretExtra, tmporigpubkey, ignorepubkey;
uint8_t funcid, evalCodeInOpret;
char destaddr[64], origaddr[64], CCaddr[64];
std::vector<CPubKey> voutTokenPubkeys;
//return true;
numvins = tx.vin.size();
numvouts = tx.vout.size();
outputs = inputs = 0;
preventCCvins = preventCCvouts = -1;
if ((funcid = DecodeTokenOpRet(tx.vout[numvouts - 1].scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, vopretExtra)) == 0)
return eval->Invalid("TokenValidate: invalid opreturn payload");
fprintf(stderr, "TokensValidate (%c) evalcode=0x%0x\n", funcid, cp->evalcode);
if (eval->GetTxUnconfirmed(tokenid, createTx, hashBlock) == 0)
return eval->Invalid("cant find token create txid");
else if (IsCCInput(tx.vin[0].scriptSig) != 0)
return eval->Invalid("illegal token vin0");
else if (numvouts < 1)
return eval->Invalid("no vouts");
else if (funcid != 'c')
{
if (tokenid == zeroid)
return eval->Invalid("illegal tokenid");
else if (!TokensExactAmounts(true, cp, inputs, outputs, eval, tx, tokenid)) {
if (!eval->Valid())
return false; //TokenExactAmounts must call eval->Invalid()!
else
return eval->Invalid("tokens cc inputs != cc outputs");
}
}
switch (funcid)
{
case 'c': // create wont be called to be verified as it has no CC inputs
//vin.0: normal input
//vout.0: issuance tokenoshis to CC
//vout.1: normal output for change (if any)
//vout.n-1: opreturn EVAL_TOKENS 'c' <tokenname> <description>
//if (evalCodeInOpret != EVAL_TOKENS)
// return eval->Invalid("unexpected TokenValidate for createtoken");
//else
return true;
case 't': // transfer
//vin.0: normal input
//vin.1 .. vin.n-1: valid CC outputs
//vout.0 to n-2: tokenoshis output to CC
//vout.n-2: normal output for change (if any)
//vout.n-1: opreturn <other evalcode> 't' tokenid <other contract payload>
if (inputs == 0)
return eval->Invalid("no token inputs for transfer");
fprintf(stderr, "token transfer preliminarily validated %.8f -> %.8f (%d %d)\n", (double)inputs / COIN, (double)outputs / COIN, preventCCvins, preventCCvouts);
break; // breaking to other contract validation...
default:
fprintf(stderr, "illegal tokens funcid.(%c)\n", funcid);
return eval->Invalid("unexpected token funcid");
}
// forward validation if evalcode in opret is not EVAL_TOKENS
// init for forwarding validation call
//if (evalCodeInOpret != EVAL_TOKENS) { // TODO: should we check also only allowed for tokens evalcodes, like EVAL_ASSETS, EVAL_GATEWAYS?
// struct CCcontract_info *cpOther = NULL, C;
// cpOther = CCinit(&C, evalCodeInOpret);
// if (cpOther)
// return cpOther->validate(cpOther, eval, tx, nIn);
// else
// return eval->Invalid("unsupported evalcode in opret");
//}
return true;
// what does this do?
// return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
}
// helper funcs:
// extract my vins pubkeys:
bool ExtractTokensVinPubkeys(CTransaction tx, std::vector<CPubKey> &vinPubkeys) {
bool found = false;
CPubKey pubkey;
struct CCcontract_info *cpTokens, tokensC;
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
for (int32_t i = 0; i < tx.vin.size(); i++)
{ // check for additional contracts which may send tokens to the Tokens contract
if( (*cpTokens->ismyvin)(tx.vin[i].scriptSig) )
{
auto findEval = [](CC *cond, struct CCVisitor _) {
bool r = false; //cc_typeId(cond) == CC_Eval && cond->codeLength == 1 && cond->code[0] == EVAL_TOKENS;
if (cc_typeId(cond) == CC_Secp256k1) {
*(CPubKey*)_.context = buf2pk(cond->publicKey);
//std::cerr << "findEval found pubkey=" << HexStr(*(CPubKey*)_.context) << std::endl;
r = true;
}
// false for a match, true for continue
return r ? 0 : 1;
};
CC *cond = GetCryptoCondition(tx.vin[i].scriptSig);
if (cond) {
CCVisitor visitor = { findEval, (uint8_t*)"", 0, &pubkey };
bool out = !cc_visit(cond, visitor);
cc_free(cond);
if (pubkey.IsValid()) {
vinPubkeys.push_back(pubkey);
found = true;
}
}
}
}
return found;
}
// this is just for log messages indentation fur debugging recursive calls:
thread_local uint32_t tokenValIndentSize = 0;
// validates opret for token tx:
uint8_t ValidateTokenOpret(CTransaction tx, int32_t v, uint256 tokenid, std::vector<CPubKey> &voutPubkeys, std::vector<uint8_t> &vopretExtra) {
uint256 tokenidOpret, tokenidOpret2;
uint8_t funcid;
uint8_t dummyEvalCode;
// this is just for log messages indentation fur debugging recursive calls:
std::string indentStr = std::string().append(tokenValIndentSize, '.');
int32_t n = tx.vout.size();
if ((funcid = DecodeTokenOpRet(tx.vout[n - 1].scriptPubKey, dummyEvalCode, tokenidOpret, voutPubkeys, vopretExtra)) == 0)
{
std::cerr << indentStr << "ValidateTokenOpret() DecodeTokenOpret could not parse opret for txid=" << tx.GetHash().GetHex() << std::endl;
return(false);
}
else if (funcid == 'c')
{
if (tokenid != zeroid && tokenid == tx.GetHash() && v == 0) {
//std::cerr << indentStr << "ValidateTokenOpret() this is the tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl;
return funcid;
}
}
else if (funcid == 't')
{
//std::cerr << indentStr << "ValidateTokenOpret() tokenid=" << tokenid.GetHex() << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl;
if (tokenid != zeroid && tokenid == tokenidOpret) {
//std::cerr << indentStr << "ValidateTokenOpret() this is a transfer 't' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl;
return funcid;
}
}
//std::cerr << indentStr << "ValidateTokenOpret() return false funcid=" << (char)funcid << " tokenid=" << tokenid.GetHex() << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl;
return (uint8_t)0;
}
// Checks if the vout is a really Tokens CC vout
// also checks tokenid in opret or txid if this is 'c' tx
// goDeeper is true: the func also validates amounts of the passed transaction:
// it should be either sum(cc vins) == sum(cc vouts) or the transaction is the 'tokenbase' ('c') tx
// checkPubkeys is true: validates if the vout is token vout1 or token vout1of2. Should always be true!
int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid)
{
// this is just for log messages indentation fur debugging recursive calls:
std::string indentStr = std::string().append(tokenValIndentSize, '.');
//std::cerr << indentStr << "IsTokensvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for tokenid=" << reftokenid.GetHex() << std::endl;
//TODO: validate cc vouts are EVAL_TOKENS!
if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition()) // maybe check address too? dimxy: possibly no, because there are too many cases with different addresses here
{
int32_t n = tx.vout.size();
// just check boundaries:
if (v >= n - 1) { // just moved this up (dimxy)
std::cerr << indentStr << "isTokensvout() internal err: (v >= n - 1), returning 0" << std::endl;
return(0);
}
if (goDeeper) {
//std::cerr << indentStr << "IsTokensvout() maxTokenExactAmountDepth=" << maxTokenExactAmountDepth << std::endl;
//validate all tx
int64_t myCCVinsAmount = 0, myCCVoutsAmount = 0;
tokenValIndentSize++;
// false --> because we already at the 1-st level ancestor tx and do not need to dereference ancestors of next levels
bool isEqual = TokensExactAmounts(false, cp, myCCVinsAmount, myCCVoutsAmount, eval, tx, reftokenid);
tokenValIndentSize--;
if (!isEqual) {
// if ccInputs != ccOutputs and it is not the tokenbase tx
// this means it is possibly a fake tx (dimxy):
if (reftokenid != tx.GetHash()) { // checking that this is the true tokenbase tx, by verifying that funcid=c, is done further in this function (dimxy)
std::cerr << indentStr << "IsTokensvout() warning: for the verified tx detected a bad vintx=" << tx.GetHash().GetHex() << ": cc inputs != cc outputs and not the 'tokenbase' tx, skipping the verified tx" << std::endl;
return 0;
}
}
}
// moved opret checking to this new reusable func (dimxy):
std::vector<CPubKey> voutPubkeys;
std::vector<uint8_t> vopretExtra;
const uint8_t funcId = ValidateTokenOpret(tx, v, reftokenid, voutPubkeys, vopretExtra);
//std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned=" << (char)(funcId?funcId:' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
if (funcId != 0) {
//std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned not-null" << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
if (checkPubkeys && funcId != 'c') { // verify that the vout is token's (for 'c' there is no pubkeys!):
//std::cerr << "IsTokensvout() vopretExtra=" << HexStr(vopretExtra) << std::endl;
uint8_t evalCodeInOpret;
if (vopretExtra.size() >= 2 /*|| vopretExtra.size() != vopretExtra.begin()[0] <-- shold we check this?*/) {
evalCodeInOpret = vopretExtra.begin()[1];
}
else {
// if payload is empty maybe it is a claim to non-payload-one-token-eval vout?
evalCodeInOpret = EVAL_TOKENS;
}
// maybe this is dual-eval 1 pubkey or 1of2 pubkey vout?
if (voutPubkeys.size() >= 1 && voutPubkeys.size() <= 2) {
CTxOut testDualVout;
// check dual-eval 1 pubkey vout with the first pubkey
testDualVout = MakeTokensCC1vout(evalCodeInOpret, tx.vout[v].nValue, voutPubkeys[0]);
if (tx.vout[v].scriptPubKey == testDualVout.scriptPubKey) {
//std::cerr << indentStr << "IsTokensvout() this is dual-eval token vout (i=0), eval2=" << (int)evalCodeInOpret << ", returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
return tx.vout[v].nValue;
}
if(voutPubkeys.size() == 2) {
// check dual eval 1of2 pubkeys vout
testDualVout = MakeTokensCC1of2vout(evalCodeInOpret, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]);
if (tx.vout[v].scriptPubKey == testDualVout.scriptPubKey) {
//std::cerr << indentStr << "IsTokensvout() this is dual-eval token 1of2 vout, eval2=" << (int)evalCodeInOpret << ", returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
return tx.vout[v].nValue;
}
// check dual eval 1 pubkey vout with the second pubkey
testDualVout = MakeTokensCC1vout(evalCodeInOpret, tx.vout[v].nValue, voutPubkeys[1]);
if (tx.vout[v].scriptPubKey == testDualVout.scriptPubKey) {
//std::cerr << indentStr << "IsTokensvout() this is dual-eval token vout (i=1), eval2=" << (int)evalCodeInOpret << ", returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
return tx.vout[v].nValue;
}
}
// maybe this is claim to single-eval token?
CTxOut testTokenVout1;
testTokenVout1 = MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[0]);
if (tx.vout[v].scriptPubKey == testTokenVout1.scriptPubKey) {
//std::cerr << indentStr << "IsTokensvout() this is single-eval token vout (i=0), returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
return tx.vout[v].nValue;
}
if (voutPubkeys.size() == 2) {
testTokenVout1 = MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[1]);
if (tx.vout[v].scriptPubKey == testTokenVout1.scriptPubKey) {
//std::cerr << indentStr << "IsTokensvout() this is single-eval token vout (i=1), returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
return tx.vout[v].nValue;
}
}
}
// maybe it is single-eval or dual-eval token change?
std::vector<CPubKey> vinPubkeys;
ExtractTokensVinPubkeys(tx, vinPubkeys);
for(std::vector<CPubKey>::iterator it = vinPubkeys.begin(); it != vinPubkeys.end(); it++) {
CTxOut testTokenVout1 = MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, *it);
CTxOut testDualVout1 = MakeTokensCC1vout(evalCodeInOpret, tx.vout[v].nValue, *it);
if (tx.vout[v].scriptPubKey == testTokenVout1.scriptPubKey) {
//std::cerr << indentStr << "IsTokensvout() this is single-eval token change, returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
return tx.vout[v].nValue;
}
if (tx.vout[v].scriptPubKey == testDualVout1.scriptPubKey) {
//std::cerr << indentStr << "IsTokensvout() this is dual-eval token change, vout eval2=" << (int)evalCodeInOpret << ", returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
return tx.vout[v].nValue;
}
}
}
else {
//std::cerr << indentStr << "IsTokensvout() returns without pubkey check value=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
return tx.vout[v].nValue;
}
}
//std::cerr << indentStr; fprintf(stderr,"IsTokensvout() CC vout v.%d of n=%d amount=%.8f txid=%s\n",v,n,(double)0/COIN, tx.GetHash().GetHex().c_str());
}
//std::cerr << indentStr; fprintf(stderr,"IsTokensvout() normal output v.%d %.8f\n",v,(double)tx.vout[v].nValue/COIN);
return(0);
}
// compares cc inputs vs cc outputs (to prevent feeding vouts from normal inputs)
bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid)
{
CTransaction vinTx;
uint256 hashBlock;
int64_t tokenoshis;
struct CCcontract_info *cpTokens, tokensC;
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
int32_t numvins = tx.vin.size();
int32_t numvouts = tx.vout.size();
inputs = outputs = 0;
// this is just for log messages indentation for debugging recursive calls:
std::string indentStr = std::string().append(tokenValIndentSize, '.');
for (int32_t i = 0; i<numvins; i++)
{ // check for additional contracts which may send tokens to the Tokens contract
if ((*cpTokens->ismyvin)(tx.vin[i].scriptSig) /*|| IsVinAllowed(tx.vin[i].scriptSig) != 0*/)
{
//std::cerr << indentStr << "TokensExactAmounts() eval is true=" << (eval != NULL) << " ismyvin=ok for_i=" << i << std::endl;
// we are not inside the validation code -- dimxy
if ((eval && eval->GetTxUnconfirmed(tx.vin[i].prevout.hash, vinTx, hashBlock) == 0) || (!eval && !myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock)))
{
std::cerr << indentStr << "TokensExactAmounts() cannot read vintx for i." << i << " numvins." << numvins << std::endl;
return (!eval) ? false : eval->Invalid("always should find vin tx, but didnt");
}
else {
tokenValIndentSize++;
// validate vouts of vintx
//std::cerr << indentStr << "TokenExactAmounts() check vin i=" << i << " nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl;
tokenoshis = IsTokensvout(goDeeper, true, cpTokens, eval, vinTx, tx.vin[i].prevout.n, tokenid);
tokenValIndentSize--;
if (tokenoshis != 0)
{
std::cerr << indentStr << "TokensExactAmounts() vin i=" << i << " tokenoshis=" << tokenoshis << std::endl;
inputs += tokenoshis;
}
}
}
}
for (int32_t i = 0; i<numvouts; i++)
{
tokenValIndentSize++;
// Note: we pass in here 'false' because we don't need to call TokenExactAmounts() recursively from IsTokensvout
// indeed, in this case we'll be checking this tx again
//std::cerr << indentStr << "TokenExactAmounts() check vout i=" << i << " nValue=" << tx.vout[i].nValue << std::endl;
tokenoshis = IsTokensvout(false, true /*<--exclude non-tokens vouts*/, cpTokens, eval, tx, i, tokenid);
tokenValIndentSize--;
if (tokenoshis != 0)
{
std::cerr << indentStr << "TokensExactAmounts() vout i=" << i << " tokenoshis=" << tokenoshis << std::endl;
outputs += tokenoshis;
}
}
//std::cerr << indentStr << "TokensExactAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl;
if (inputs != outputs) {
if (tx.GetHash() != tokenid)
std::cerr << indentStr << "TokenExactAmounts() found unequal token cc inputs=" << inputs << " vs cc outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << " and this is not the create tx" << std::endl;
return false; // do not call eval->Invalid() here!
}
else
return true;
}
// add inputs from token cc addr
int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs)
{
char tokenaddr[64], destaddr[64];
int64_t threshold, nValue, price, totalinputs = 0;
uint256 txid, hashBlock;
//std::vector<uint8_t> vopretExtra;
CTransaction vintx;
int32_t j, vout, n = 0;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
GetTokensCCaddress(cp, tokenaddr, pk);
SetCCunspents(unspentOutputs, tokenaddr);
threshold = total / (maxinputs != 0 ? maxinputs : 64); // TODO: is maxinputs really could not be over 64? what if i want to calc total balance?
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::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;
for (j = 0; j<mtx.vin.size(); j++)
if (txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n)
break;
if (j != mtx.vin.size())
continue;
if (GetTransaction(txid, vintx, hashBlock, false) != 0)
{
Getscriptaddress(destaddr, vintx.vout[vout].scriptPubKey);
if (strcmp(destaddr, tokenaddr) != 0 && strcmp(destaddr, cp->unspendableCCaddr) != 0 && strcmp(destaddr, cp->unspendableaddr2) != 0)
continue;
//fprintf(stderr, "AddTokenCCInputs() check destaddress=%s vout amount=%.8f\n", destaddr, (double)vintx.vout[vout].nValue / COIN);
std::vector<CPubKey> vinPubkeys;
if ((nValue = IsTokensvout(true, true/*<--add only checked token uxtos */, cp, NULL, vintx, vout, tokenid)) > 0 && myIsutxo_spentinmempool(txid, vout) == 0)
{
if (total != 0 && maxinputs != 0)
mtx.vin.push_back(CTxIn(txid, vout, CScript()));
nValue = it->second.satoshis;
totalinputs += nValue;
std::cerr << "AddTokenInputs() adding input nValue=" << nValue << std::endl;
n++;
if ((total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs))
break;
}
}
}
//std::cerr << "AddTokenInputs() found totalinputs=" << totalinputs << std::endl;
return(totalinputs);
}
std::string CreateToken(int64_t txfee, int64_t assetsupply, std::string name, std::string description)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; struct CCcontract_info *cp, C;
if (assetsupply < 0)
{
fprintf(stderr, "negative assetsupply %lld\n", (long long)assetsupply);
return("");
}
cp = CCinit(&C, EVAL_TOKENS);
if (name.size() > 32 || description.size() > 4096)
{
fprintf(stderr, "name.%d or description.%d is too big\n", (int32_t)name.size(), (int32_t)description.size());
return("");
}
if (txfee == 0)
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
if (AddNormalinputs(mtx, mypk, assetsupply + 2 * txfee, 64) > 0)
{
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, assetsupply, mypk));
mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(cp->CChexstr) << OP_CHECKSIG));
return(FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), name, description)));
}
return("");
}
std::string TokenTransfer(int64_t txfee, uint256 assetid, std::vector<uint8_t> destpubkey, int64_t total)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; uint64_t mask; int64_t CCchange = 0, inputs = 0; struct CCcontract_info *cp, C;
std::vector<uint8_t> emptyExtraOpret;
if (total < 0)
{
fprintf(stderr, "negative total %lld\n", (long long)total);
return("");
}
cp = CCinit(&C, EVAL_TOKENS);
if (txfee == 0)
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
if (AddNormalinputs(mtx, mypk, txfee, 3) > 0)
{
//n = outputs.size();
//if ( n == amounts.size() )
//{
// for (i=0; i<n; i++)
// total += amounts[i];
mask = ~((1LL << mtx.vin.size()) - 1);
if ((inputs = AddTokenCCInputs(cp, mtx, mypk, assetid, total, 60)) > 0)
{
if (inputs < total) { //added dimxy
std::cerr << "AssetTransfer(): insufficient funds" << std::endl;
return ("");
}
if (inputs > total)
CCchange = (inputs - total);
//for (i=0; i<n; i++)
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, total, pubkey2pk(destpubkey))); // TODO: or MakeTokensCC1vout??
if (CCchange != 0)
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk));
std::vector<CPubKey> voutTokenPubkeys;
voutTokenPubkeys.push_back(pubkey2pk(destpubkey)); // dest pubkey for validating vout
return(FinalizeCCTx(mask, cp, mtx, mypk, txfee, EncodeTokenOpRet('t', EVAL_TOKENS, assetid, voutTokenPubkeys, CScript()))); // By setting EVAL_TOKENS we're getting out from assets validation code
}
else {
fprintf(stderr, "not enough CC token inputs for %.8f\n", (double)total / COIN);
}
//} else fprintf(stderr,"numoutputs.%d != numamounts.%d\n",n,(int32_t)amounts.size());
}
else {
fprintf(stderr, "not enough normal inputs for txfee\n");
}
return("");
}
int64_t GetTokenBalance(CPubKey pk, uint256 tokenid)
{
uint256 hashBlock;
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CTransaction tokentx;
// CCerror = strprintf("obsolete, cannot return correct value without eval");
// return 0;
if (GetTransaction(tokenid, tokentx, hashBlock, false) == 0)
{
fprintf(stderr, "cant find tokenid\n");
CCerror = strprintf("cant find tokenid");
return 0;
}
struct CCcontract_info *cp, C;
cp = CCinit(&C, EVAL_TOKENS);
return(AddTokenCCInputs(cp, mtx, pk, tokenid, 0, 0));
}
UniValue TokenInfo(uint256 tokenid)
{
UniValue result(UniValue::VOBJ); uint256 hashBlock; CTransaction vintx; std::vector<uint8_t> origpubkey; std::string name, description; char str[67], numstr[65];
if (GetTransaction(tokenid, vintx, hashBlock, false) == 0)
{
fprintf(stderr, "cant find assetid\n");
result.push_back(Pair("result", "error"));
result.push_back(Pair("error", "cant find tokenid"));
return(result);
}
if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description) == 0)
{
fprintf(stderr, "assetid isnt token creation txid\n");
result.push_back(Pair("result", "error"));
result.push_back(Pair("error", "assetid isnt token creation txid"));
}
result.push_back(Pair("result", "success"));
result.push_back(Pair("tokenid", uint256_str(str, tokenid)));
result.push_back(Pair("owner", pubkey33_str(str, origpubkey.data())));
result.push_back(Pair("name", name));
result.push_back(Pair("supply", vintx.vout[0].nValue));
result.push_back(Pair("description", description));
return(result);
}
UniValue TokenList()
{
UniValue result(UniValue::VARR);
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
struct CCcontract_info *cp, C; uint256 txid, hashBlock;
CTransaction vintx; std::vector<uint8_t> origpubkey;
std::string name, description; char str[65];
cp = CCinit(&C, EVAL_TOKENS);
SetCCtxids(addressIndex, cp->normaladdr);
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it = addressIndex.begin(); it != addressIndex.end(); it++)
{
txid = it->first.txhash;
if (GetTransaction(txid, vintx, hashBlock, false) != 0)
{
if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description) != 0)
{
result.push_back(uint256_str(str, txid));
}
}
}
return(result);
}

44
src/cc/CCtokens.h

@ -0,0 +1,44 @@
/******************************************************************************
* Copyright © 2014-2018 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. *
* *
******************************************************************************/
/*
CCassetstx has the functions that create the EVAL_ASSETS transactions. It is expected that rpc calls would call these functions. For EVAL_ASSETS, the rpc functions are in rpcwallet.cpp
CCassetsCore has functions that are used in two contexts, both during rpc transaction create time and also during the blockchain validation. Using the identical functions is a good way to prevent them from being mismatched. The must match or the transaction will get rejected.
*/
#ifndef CC_TOKENS_H
#define CC_TOKENS_H
#include "CCinclude.h"
// CCcustom
bool TokensValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cpTokens, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid);
//int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, std::vector<uint8_t> &origpubkey, const CTransaction& tx, int32_t v, uint256 reftokenid, std::vector<CPubKey> vinPubkeys);
std::string CreateToken(int64_t txfee, int64_t assetsupply, std::string name, std::string description);
std::string TokenTransfer(int64_t txfee, uint256 assetid, std::vector<uint8_t> destpubkey, int64_t total);
int64_t GetTokenBalance(CPubKey pk, uint256 tokenid);
UniValue TokenInfo(uint256 tokenid);
UniValue TokenList();
//this is in CCinclude.h int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs);
//this is in CCinclude.h uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description);
#endif

173
src/cc/CCtx.cpp

@ -16,6 +16,8 @@
#include "CCinclude.h"
#include "key_io.h"
std::vector<CPubKey> NULL_pubkeys;
/*
FinalizeCCTx is a very useful function that will properly sign both CC and normal inputs, adds normal change and the opreturn.
@ -38,13 +40,18 @@ bool SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScrip
return(false);
}
std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey mypk,uint64_t txfee,CScript opret)
std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey mypk,uint64_t txfee,CScript opret,std::vector<CPubKey> pubkeys)
{
auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus());
CTransaction vintx; std::string hex; uint256 hashBlock; uint64_t mask=0,nmask=0,vinimask=0;
int64_t utxovalues[64],change,normalinputs=0,totaloutputs=0,normaloutputs=0,totalinputs=0,normalvins=0,ccvins=0;
int32_t i,utxovout,n,err = 0; char myaddr[64],destaddr[64],unspendable[64];
uint8_t *privkey,myprivkey[32],unspendablepriv[32],*msg32 = 0; CC *mycond=0,*othercond=0,*othercond2=0,*othercond3=0,*cond; CPubKey unspendablepk;
CTransaction vintx; std::string hex; CPubKey globalpk; uint256 hashBlock; uint64_t mask=0,nmask=0,vinimask=0;
int64_t utxovalues[CC_MAXVINS],change,normalinputs=0,totaloutputs=0,normaloutputs=0,totalinputs=0,normalvins=0,ccvins=0;
int32_t i,flag,utxovout,n,err = 0;
char myaddr[64], destaddr[64], unspendable[64], mytokensaddr[64], mysingletokensaddr[64], tokensunspendable[64];
uint8_t *privkey, myprivkey[32], unspendablepriv[32], tokensunspendablepriv[32], *msg32 = 0;
CC *mycond=0, *othercond=0, *othercond2=0,*othercond4=0, *othercond3=0, *othercond1of2=NULL, *othercond1of2tokens = NULL, *cond, *mytokenscond = NULL, *mysingletokenscond = NULL, *othertokenscond = NULL;
CPubKey unspendablepk /*, tokensunspendablepk*/;
struct CCcontract_info *cpTokens, tokensC;
globalpk = GetUnspendable(cp,0);
n = mtx.vout.size();
for (i=0; i<n; i++)
{
@ -52,17 +59,37 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran
normaloutputs += mtx.vout[i].nValue;
totaloutputs += mtx.vout[i].nValue;
}
if ( (n= mtx.vin.size()) > 64 )
if ( (n= mtx.vin.size()) > CC_MAXVINS )
{
fprintf(stderr,"FinalizeCCTx: %d is too many vins\n",n);
return("0");
}
Myprivkey(myprivkey);
unspendablepk = GetUnspendable(cp,unspendablepriv);
GetCCaddress(cp,myaddr,mypk);
mycond = MakeCCcond1(cp->evalcode,mypk);
GetCCaddress(cp,unspendable,unspendablepk);
othercond = MakeCCcond1(cp->evalcode,unspendablepk);
// to spend from single-eval evalcode 'unspendable'
unspendablepk = GetUnspendable(cp, unspendablepriv);
GetCCaddress(cp, unspendable, unspendablepk);
othercond = MakeCCcond1(cp->evalcode, unspendablepk);
// tokens support:
// to spend from dual-eval mypk vout
GetTokensCCaddress(cp, mytokensaddr, mypk);
mytokenscond = MakeTokensCCcond1(cp->evalcode, mypk);
// to spend from single-eval EVAL_TOKENS mypk
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
GetCCaddress(cpTokens, mysingletokensaddr, mypk);
mysingletokenscond = MakeCCcond1(EVAL_TOKENS, mypk);
// to spend from dual-eval EVAL_TOKEN+evalcode 'unspendable' pk
//tokensunspendablepk = GetUnspendable(cpTokens, tokensunspendablepriv);
GetTokensCCaddress(cp, tokensunspendable, unspendablepk);
othertokenscond = MakeTokensCCcond1(cp->evalcode, unspendablepk);
//Reorder vins so that for multiple normal vins all other except vin0 goes to the end
//This is a must to avoid hardfork change of validation in every CC, because there could be maximum one normal vin at the begining with current validation.
for (i=0; i<n; i++)
@ -127,41 +154,96 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran
else
{
Getscriptaddress(destaddr,vintx.vout[utxovout].scriptPubKey);
//fprintf(stderr,"vin.%d is CC %.8f -> (%s)\n",i,(double)utxovalues[i]/COIN,destaddr);
if ( strcmp(destaddr,myaddr) == 0 )
//fprintf(stderr,"FinalizeCCTx() vin.%d is CC %.8f -> (%s)\n",i,(double)utxovalues[i]/COIN,destaddr);
//std::cerr << "FinalizeCCtx() searching destaddr=" << destaddr << " myaddr=" << myaddr << std::endl;
if( strcmp(destaddr,myaddr) == 0 )
{
privkey = myprivkey;
cond = mycond;
}
else if (strcmp(destaddr, mytokensaddr) == 0) // if this is TokensCC1vout
{
privkey = myprivkey;
cond = mytokenscond;
//fprintf(stderr,"FinalizeCCTx() matched dual-eval TokensCC1vout CC addr.(%s)\n",mytokensaddr);
}
else if (strcmp(destaddr, mysingletokensaddr) == 0) // if this is TokensCC1vout
{
privkey = myprivkey;
cond = mysingletokenscond;
//fprintf(stderr, "FinalizeCCTx() matched single-eval token CC1vout CC addr.(%s)\n", mytokensaddr);
}
else if ( strcmp(destaddr,unspendable) == 0 )
{
privkey = unspendablepriv;
cond = othercond;
//fprintf(stderr,"unspendable CC addr.(%s)\n",unspendable);
//fprintf(stderr,"FinalizeCCTx() matched unspendable CC addr.(%s)\n",unspendable);
}
else if (strcmp(destaddr, tokensunspendable) == 0)
{
privkey = unspendablepriv;
cond = othertokenscond;
//fprintf(stderr,"FinalizeCCTx() matched tokensunspendable CC addr.(%s)\n",unspendable);
}
// check if this is the 2nd additional evalcode + 'unspendable' cc addr:
else if ( strcmp(destaddr,cp->unspendableaddr2) == 0)
{
//fprintf(stderr,"matched %s unspendable2!\n",cp->unspendableaddr2);
//fprintf(stderr,"FinalizeCCTx() matched %s unspendable2!\n",cp->unspendableaddr2);
privkey = cp->unspendablepriv2;
if ( othercond2 == 0 && cp->evalcode != EVAL_CHANNELS && cp->evalcode != EVAL_HEIR )
othercond2 = MakeCCcond1(cp->evalcode2,cp->unspendablepk2);
else if ( othercond2 == 0 && (cp->evalcode == EVAL_CHANNELS || cp->evalcode == EVAL_HEIR) )
othercond2 = MakeCCcond1of2(cp->evalcode2,cp->unspendablepk2,cp->unspendablepk3);
if ( othercond2 == 0 )
othercond2 = MakeCCcond1(cp->evalcode2, cp->unspendablepk2);
cond = othercond2;
}
// check if this is 3rd additional evalcode + 'unspendable' cc addr:
else if ( strcmp(destaddr,cp->unspendableaddr3) == 0 )
{
//fprintf(stderr,"matched %s unspendable3!\n",cp->unspendableaddr3);
//fprintf(stderr,"FinalizeCCTx() matched %s unspendable3!\n",cp->unspendableaddr3);
privkey = cp->unspendablepriv3;
if ( othercond3 == 0 )
othercond3 = MakeCCcond1(cp->evalcode3,cp->unspendablepk3);
cond = othercond3;
}
// check if this is spending from 1of2 cc coins addr:
else if (strcmp(cp->coins1of2addr, destaddr) == 0)
{
//fprintf(stderr,"FinalizeCCTx() matched %s unspendable1of2!\n",cp->coins1of2addr);
privkey = myprivkey;
if (othercond1of2 == 0)
othercond1of2 = MakeCCcond1of2(cp->evalcode, cp->coins1of2pk[0], cp->coins1of2pk[1]);
cond = othercond1of2;
}
// check if this is spending from 1of2 cc tokens addr:
else if (strcmp(cp->tokens1of2addr, destaddr) == 0)
{
//fprintf(stderr,"FinalizeCCTx() matched %s cp->tokens1of2addr!\n", cp->tokens1of2addr);
privkey = myprivkey;
if (othercond1of2tokens == 0)
othercond1of2tokens = MakeTokensCCcond1of2(cp->evalcode, cp->tokens1of2pk[0], cp->tokens1of2pk[1]);
cond = othercond1of2tokens;
}
else
{
fprintf(stderr,"CC signing error: vini.%d has unknown CC address.(%s)\n",i,destaddr);
continue;
flag = 0;
if ( pubkeys != NULL_pubkeys )
{
char coinaddr[64];
GetCCaddress1of2(cp,coinaddr,globalpk,pubkeys[i]);
//fprintf(stderr,"%s + %s -> %s vs %s\n",HexStr(globalpk).c_str(),HexStr(pubkeys[i]).c_str(),coinaddr,destaddr);
if ( strcmp(destaddr,coinaddr) == 0 )
{
privkey = cp->CCpriv;
if ( othercond4 != 0 )
cc_free(othercond4);
othercond4 = MakeCCcond1of2(cp->evalcode,globalpk,pubkeys[i]);
cond = othercond4;
flag = 1;
}
}
if ( flag == 0 )
{
fprintf(stderr,"CC signing error: vini.%d has unknown CC address.(%s)\n",i,destaddr);
continue;
}
}
uint256 sighash = SignatureHash(CCPubKey(cond), mtx, i, SIGHASH_ALL, utxovalues[i],consensusBranchId, &txdata);
if ( cc_signTreeSecp256k1Msg32(cond,privkey,sighash.begin()) != 0 )
@ -178,6 +260,7 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran
else
{
fprintf(stderr,"vini.%d has CC signing error address.(%s)\n",i,destaddr);
return("");
}
}
} else fprintf(stderr,"FinalizeCCTx couldnt find %s\n",mtx.vin[i].prevout.hash.ToString().c_str());
@ -190,6 +273,8 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran
cc_free(othercond2);
if ( othercond3 != 0 )
cc_free(othercond3);
if ( othercond4 != 0 )
cc_free(othercond4);
std::string strHex = EncodeHexTx(mtx);
if ( strHex.size() > 0 )
return(strHex);
@ -247,6 +332,40 @@ int64_t CCutxovalue(char *coinaddr,uint256 utxotxid,int32_t utxovout)
return(0);
}
int32_t CCgettxout(uint256 txid,int32_t vout,int32_t mempoolflag)
{
CCoins coins;
if ( mempoolflag != 0 )
{
LOCK(mempool.cs);
CCoinsViewMemPool view(pcoinsTip, mempool);
if (!view.GetCoins(txid, coins))
return(-1);
if ( myIsutxo_spentinmempool(txid,vout) != 0 )
return(-1);
}
else
{
if (!pcoinsTip->GetCoins(txid, coins))
return(-1);
}
if ( vout < coins.vout.size() )
return(coins.vout[vout].nValue);
else return(-1);
}
int32_t CCgetspenttxid(uint256 &spenttxid,int32_t &vini,int32_t &height,uint256 txid,int32_t vout)
{
CSpentIndexKey key(txid, vout);
CSpentIndexValue value;
if ( !GetSpentIndex(key, value) )
return(-1);
spenttxid = value.txid;
vini = (int32_t)value.inputIndex;
height = value.blockHeight;
return(0);
}
int64_t CCaddress_balance(char *coinaddr)
{
int64_t sum = 0; std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
@ -263,7 +382,7 @@ int64_t CCfullsupply(uint256 tokenid)
uint256 hashBlock; int32_t numvouts; CTransaction tx; std::vector<uint8_t> origpubkey; std::string name,description;
if ( GetTransaction(tokenid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 )
{
if ( DecodeAssetCreateOpRet(tx.vout[numvouts-1].scriptPubKey,origpubkey,name,description) > 0 )
if (DecodeTokenCreateOpRet(tx.vout[numvouts-1].scriptPubKey,origpubkey,name,description))
{
return(tx.vout[0].nValue);
}
@ -273,8 +392,11 @@ int64_t CCfullsupply(uint256 tokenid)
int64_t CCtoken_balance(char *coinaddr,uint256 tokenid)
{
int64_t price,sum = 0; int32_t numvouts; CTransaction tx; uint256 assetid,assetid2,txid,hashBlock; std::vector<uint8_t> origpubkey;
int64_t price,sum = 0; int32_t numvouts; CTransaction tx; uint256 assetid,assetid2,txid,hashBlock;
std::vector<uint8_t> vopretExtra;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
uint8_t evalCode;
SetCCunspents(unspentOutputs,coinaddr);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
{
@ -282,7 +404,8 @@ int64_t CCtoken_balance(char *coinaddr,uint256 tokenid)
if ( GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 )
{
char str[65]; fprintf(stderr,"check %s %.8f\n",uint256_str(str,txid),(double)it->second.satoshis/COIN);
if ( DecodeAssetOpRet(tx.vout[numvouts-1].scriptPubKey,assetid,assetid2,price,origpubkey) != 0 && assetid == tokenid )
std::vector<CPubKey> voutTokenPubkeys;
if ( DecodeTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCode, assetid, voutTokenPubkeys, vopretExtra) != 0 && assetid == tokenid )
{
sum += it->second.satoshis;
}
@ -350,8 +473,8 @@ int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int3
{
int32_t abovei,belowi,ind,vout,i,n = 0,maxutxos=64; int64_t sum,threshold,above,below; int64_t remains,nValue,totalinputs = 0; uint256 txid,hashBlock; std::vector<COutput> vecOutputs; CTransaction tx; struct CC_utxo *utxos,*up;
#ifdef ENABLE_WALLET
const CKeyStore& keystore = *pwalletMain;
assert(pwalletMain != NULL);
const CKeyStore& keystore = *pwalletMain;
LOCK2(cs_main, pwalletMain->cs_wallet);
pwalletMain->AvailableCoins(vecOutputs, false, NULL, true);
utxos = (struct CC_utxo *)calloc(maxutxos,sizeof(*utxos));

242
src/cc/CCutils.cpp

@ -76,6 +76,54 @@ CTxOut MakeCC1of2vout(uint8_t evalcode,CAmount nValue,CPubKey pk1,CPubKey pk2)
return(vout);
}
CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2)
{
// make 1of2 sigs cond
std::vector<CC*> pks;
pks.push_back(CCNewSecp256k1(pk1));
pks.push_back(CCNewSecp256k1(pk2));
std::vector<CC*> thresholds;
thresholds.push_back( CCNewEval(E_MARSHAL(ss << evalcode)) );
if( evalcode != EVAL_TOKENS ) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1of2()!
thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc
thresholds.push_back(CCNewThreshold(1, pks)); // this is 1 of 2 sigs cc
return CCNewThreshold(thresholds.size(), thresholds);
}
CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk)
{
std::vector<CC*> pks;
pks.push_back(CCNewSecp256k1(pk));
std::vector<CC*> thresholds;
thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode)));
if (evalcode != EVAL_TOKENS) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1()!
thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc
thresholds.push_back(CCNewThreshold(1, pks)); // signature
return CCNewThreshold(thresholds.size(), thresholds);
}
CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2)
{
CTxOut vout;
CC *payoutCond = MakeTokensCCcond1of2(evalcode, pk1, pk2);
vout = CTxOut(nValue, CCPubKey(payoutCond));
cc_free(payoutCond);
return(vout);
}
CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk)
{
CTxOut vout;
CC *payoutCond = MakeTokensCCcond1(evalcode, pk);
vout = CTxOut(nValue, CCPubKey(payoutCond));
cc_free(payoutCond);
return(vout);
}
CC* GetCryptoCondition(CScript const& scriptSig)
{
auto pc = scriptSig.begin();
@ -195,6 +243,22 @@ void CCaddr3set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *
strcpy(cp->unspendableaddr3,coinaddr);
}
// set pubkeys, myprivkey and 1of2 cc addr for spending from 1of2 cryptocondition vout:
void CCaddr1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *coinaddr)
{
cp->coins1of2pk[0] = pk1;
cp->coins1of2pk[1] = pk2;
strcpy(cp->coins1of2addr, coinaddr);
}
// set pubkeys, myprivkey and 1of2 cc addr for spending from 1of2 tokens cryptocondition vout:
void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *coinaddr)
{
cp->tokens1of2pk[0] = pk1;
cp->tokens1of2pk[1] = pk2;
strcpy(cp->tokens1of2addr, coinaddr);
}
bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey)
{
CTxDestination address; txnouttype whichType;
@ -287,6 +351,27 @@ bool GetCCaddress(struct CCcontract_info *cp,char *destaddr,CPubKey pk)
return(_GetCCaddress(destaddr,cp->evalcode,pk));
}
bool _GetTokensCCaddress(char *destaddr, uint8_t evalcode, CPubKey pk)
{
CC *payoutCond;
destaddr[0] = 0;
if ((payoutCond = MakeTokensCCcond1(evalcode, pk)) != 0)
{
Getscriptaddress(destaddr, CCPubKey(payoutCond));
cc_free(payoutCond);
}
return(destaddr[0] != 0);
}
bool GetTokensCCaddress(struct CCcontract_info *cp, char *destaddr, CPubKey pk)
{
destaddr[0] = 0;
if (pk.size() == 0)
pk = GetUnspendable(cp, 0);
return(_GetTokensCCaddress(destaddr, cp->evalcode, pk));
}
bool GetCCaddress1of2(struct CCcontract_info *cp,char *destaddr,CPubKey pk,CPubKey pk2)
{
CC *payoutCond;
@ -299,17 +384,29 @@ bool GetCCaddress1of2(struct CCcontract_info *cp,char *destaddr,CPubKey pk,CPubK
return(destaddr[0] != 0);
}
bool ConstrainVout(CTxOut vout,int32_t CCflag,char *cmpaddr,int64_t nValue)
bool GetTokensCCaddress1of2(struct CCcontract_info *cp, char *destaddr, CPubKey pk, CPubKey pk2)
{
CC *payoutCond;
destaddr[0] = 0;
if ((payoutCond = MakeTokensCCcond1of2(cp->evalcode, pk, pk2)) != 0)
{
Getscriptaddress(destaddr, CCPubKey(payoutCond));
cc_free(payoutCond);
}
return(destaddr[0] != 0);
}
bool ConstrainVout(CTxOut vout, int32_t CCflag, char *cmpaddr, int64_t nValue)
{
char destaddr[64];
if ( vout.scriptPubKey.IsPayToCryptoCondition() != CCflag )
{
fprintf(stderr,"constrain vout error isCC %d vs %d CCflag\n",vout.scriptPubKey.IsPayToCryptoCondition(),CCflag);
fprintf(stderr,"constrain vout error isCC %d vs %d CCflag\n", vout.scriptPubKey.IsPayToCryptoCondition(), CCflag);
return(false);
}
else if ( cmpaddr != 0 && (Getscriptaddress(destaddr,vout.scriptPubKey) == 0 || strcmp(destaddr,cmpaddr) != 0) )
else if ( cmpaddr != 0 && (Getscriptaddress(destaddr, vout.scriptPubKey) == 0 || strcmp(destaddr, cmpaddr) != 0) )
{
fprintf(stderr,"constrain vout error addr %s vs %s\n",cmpaddr!=0?cmpaddr:"",destaddr!=0?destaddr:"");
fprintf(stderr,"constrain vout error: check addr %s vs script addr %s\n", cmpaddr!=0?cmpaddr:"", destaddr!=0?destaddr:"");
return(false);
}
else if ( nValue != 0 && nValue != vout.nValue ) //(nValue == 0 && vout.nValue < 10000) || (
@ -396,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<uint8_t> 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<uint8_t> 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)
@ -502,3 +570,109 @@ bool komodo_txnotarizedconfirmed(uint256 txid)
return (true);
return (false);
}
CPubKey check_signing_pubkey(CScript scriptSig)
{
bool found = false;
CPubKey pubkey;
auto findEval = [](CC *cond, struct CCVisitor _) {
bool r = false;
if (cc_typeId(cond) == CC_Secp256k1) {
*(CPubKey*)_.context=buf2pk(cond->publicKey);
r = true;
}
// false for a match, true for continue
return r ? 0 : 1;
};
CC *cond = GetCryptoCondition(scriptSig);
if (cond) {
CCVisitor visitor = { findEval, (uint8_t*)"", 0, &pubkey };
bool out = !cc_visit(cond, visitor);
cc_free(cond);
if (pubkey.IsValid()) {
return pubkey;
}
}
return CPubKey();
}
bool ProcessCC(struct CCcontract_info *cp,Eval* eval, std::vector<uint8_t> 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<uint8_t> 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<uint8_t> 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");
}

266
src/cc/assets.cpp

@ -45,32 +45,22 @@
valid CC output: create or transfer or buyoffer or selloffer or exchange or cancel or fill
create
vin.0: normal input
vout.0: issuance assetoshis to CC
vout.1: tag sent to normal address of AssetsCCaddress
vout.2: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['c'] [origpubkey] "<assetname>" "<description>"
transfer
vin.0: normal input
vin.1 .. vin.n-1: valid CC outputs
vout.0 to n-2: assetoshis output to CC
vout.n-2: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['t'] [assetid]
buyoffer:
vins.*: normal inputs (bid + change)
vout.0: amount of bid to unspendable
vout.1: normal output for change (if any)
vout.1: CC output for marker
vout.2: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey]
cancelbuy:
vin.0: normal input
vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
vin.2: CC marker from buyoffer for txfee
vout.0: vin.1 value to original pubkey buyTx.vout[0].nValue -> [origpubkey]
vout.1: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['o'] [assetid]
vout.1: vin.2 back to users pubkey
vout.2: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['o'] [assetid] 0 0 [origpubkey]
fillbuy:
vin.0: normal input
@ -87,8 +77,9 @@
vin.0: normal input
vin.1+: valid CC output for sale
vout.0: vin.1 assetoshis output to CC to unspendable
vout.1: CC output for change (if any)
vout.2: normal output for change (if any)
vout.1: CC output for marker
vout.2: CC output for change (if any)
vout.3: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey]
exchange:
@ -102,8 +93,10 @@
cancel:
vin.0: normal input
vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx
vin.2: CC marker from selloffer for txfee
vout.0: vin.1 assetoshis to original pubkey CC sellTx/exchangeTx.vout[0].nValue -> [origpubkey]
vout.1: normal output for change (if any)
vout.1: vin.2 back to users pubkey
vout.2: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid]
fillsell:
@ -133,45 +126,80 @@
// tx validation
bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransaction &tx, uint32_t nIn)
{
static uint256 zero;
CTxDestination address; CTransaction vinTx,createTx; uint256 hashBlock,assetid,assetid2; int32_t i,starti,numvins,numvouts,preventCCvins,preventCCvouts; int64_t remaining_price,nValue,assetoshis,outputs,inputs,tmpprice,totalunits,ignore; std::vector<uint8_t> origpubkey,tmporigpubkey,ignorepubkey; uint8_t funcid; char destaddr[64],origaddr[64],CCaddr[64];
CTxDestination address; CTransaction vinTx,createTx; uint256 hashBlock,assetid,assetid2;
int32_t i,starti,numvins,numvouts,preventCCvins,preventCCvouts;
int64_t remaining_price,nValue,assetoshis,outputs,inputs,tmpprice,totalunits,ignore; std::vector<uint8_t> origpubkey,tmporigpubkey,ignorepubkey;
uint8_t funcid, evalCodeInOpret;
char destaddr[64], origaddr[64], assetsCCaddr[64], userTokensCCaddr[64]; //, signleEvalTokensCCaddr[64];
//return true;
//CPubKey unspendableTokensPk = GetUnspendable(cpTokens, NULL);
//CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, NULL);
//GetCCaddress(cpTokens, tokensUnspendableCCaddr, unspendableTokensPk);
numvins = tx.vin.size();
numvouts = tx.vout.size();
outputs = inputs = 0;
preventCCvins = preventCCvouts = -1;
if ( (funcid= DecodeAssetOpRet(tx.vout[numvouts-1].scriptPubKey,assetid,assetid2,remaining_price,origpubkey)) == 0 )
return eval->Invalid("Invalid opreturn payload");
if (numvouts == 0)
return eval->Invalid("AssetValidate: no vouts");
if((funcid = DecodeAssetTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCodeInOpret, assetid, assetid2, remaining_price, origpubkey)) == 0 )
return eval->Invalid("AssetValidate: invalid opreturn payload");
// find dual-eval tokens unspendable addr:
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;
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
// find single-eval token user cc addr:
//GetCCaddress(cpTokens, signleEvalTokensCCaddr, pubkey2pk(origpubkey));
fprintf(stderr,"AssetValidate (%c)\n",funcid);
if ( funcid != 'o' && funcid != 'x' && eval->GetTxUnconfirmed(assetid,createTx,hashBlock) == 0 )
if( funcid != 'o' && funcid != 'x' && eval->GetTxUnconfirmed(assetid, createTx, hashBlock) == 0 )
return eval->Invalid("cant find asset create txid");
else if ( funcid != 'o' && funcid != 'x' && assetid2 != zero && eval->GetTxUnconfirmed(assetid2,createTx,hashBlock) == 0 )
else if( funcid != 'o' && funcid != 'x' && assetid2 != zero && eval->GetTxUnconfirmed(assetid2, createTx, hashBlock) == 0 )
return eval->Invalid("cant find asset2 create txid");
else if ( IsCCInput(tx.vin[0].scriptSig) != 0 )
else if( IsCCInput(tx.vin[0].scriptSig) != 0 )
return eval->Invalid("illegal asset vin0");
else if ( numvouts < 1 )
return eval->Invalid("no vouts");
else if ( funcid != 'c' )
else if( numvouts < 2 )
return eval->Invalid("too few vouts"); // it was if(numvouts < 1) but it refers at least to vout[1] below
else if( funcid != 'c' )
{
if ( funcid == 't' )
/* if( funcid == 't' )
starti = 0;
else starti = 1;
if ( assetid == zero )
else
starti = 1; */
if( assetid == zero )
return eval->Invalid("illegal assetid");
else if ( AssetExactAmounts(2, cp,inputs,starti,outputs,eval,tx,assetid) == false )
return eval->Invalid("asset inputs != outputs");
}
else if (!AssetCalcAmounts(cpAssets, inputs, outputs, eval, tx, assetid)) { // Only set inputs and outputs. NOTE: we do not need to check cc inputs == cc outputs
return false; // returns false if some problems with reading vintxes
}
}
switch ( funcid )
switch( funcid )
{
case 'c': // create wont be called to be verified as it has no CC inputs
//vin.0: normal input
//vout.0: issuance assetoshis to CC
//vout.1: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['c'] [{"<assetname>":"<description>"}]
return eval->Invalid("unexpected AssetValidate for createasset");
//if (evalCodeInOpret == EVAL_ASSETS)
// return eval->Invalid("unexpected AssetValidate for createasset");
// return
return eval->Invalid("invalid asset funcid \'c\'");
break;
case 't': // transfer
//vin.0: normal input
@ -179,36 +207,40 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
//vout.0 to n-2: assetoshis output to CC
//vout.n-2: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['t'] [assetid]
if ( inputs == 0 )
return eval->Invalid("no asset inputs for transfer");
fprintf(stderr,"transfer validated %.8f -> %.8f (%d %d)\n",(double)inputs/COIN,(double)outputs/COIN,preventCCvins,preventCCvouts);
//if (inputs == 0)
// return eval->Invalid("no asset inputs for transfer");
//fprintf(stderr,"transfer preliminarily validated %.8f -> %.8f (%d %d)\n",(double)inputs/COIN,(double)outputs/COIN,preventCCvins,preventCCvouts);
return eval->Invalid("invalid asset funcid \'t\'");
break;
case 'b': // buyoffer
//vins.*: normal inputs (bid + change)
//vout.0: amount of bid to unspendable
//vout.1: normal output for change (if any)
//vout.1: CC output for marker
//vout.2: normal output for change (if any)
// vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey]
if ( remaining_price == 0 )
if( remaining_price == 0 )
return eval->Invalid("illegal null amount for buyoffer");
else if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,0) == 0 )
else if( ConstrainVout(tx.vout[0],1,cpAssets->unspendableCCaddr,0) == 0 )
return eval->Invalid("invalid vout for buyoffer");
preventCCvins = 1;
preventCCvouts = 1;
fprintf(stderr,"buy offer validated to destaddr.(%s)\n",cp->unspendableCCaddr);
fprintf(stderr,"buy offer validated to destaddr.(%s)\n",cpAssets->unspendableCCaddr);
break;
case 'o': // cancelbuy
//vin.0: normal input
//vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
//vin.2: CC marker from buyoffer for txfee
//vout.0: vin.1 value to original pubkey buyTx.vout[0].nValue -> [origpubkey]
//vout.1: normal output for change (if any)
//vout.1: vin.2 back to users pubkey
//vout.2: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['o']
if ( (nValue= AssetValidateBuyvin(cp,eval,tmpprice,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
if( (nValue= AssetValidateBuyvin(cpAssets, eval, tmpprice, tmporigpubkey, assetsCCaddr, origaddr, tx, assetid)) == 0 )
return(false);
else if ( ConstrainVout(tx.vout[0],0,origaddr,nValue) == 0 )
else if( ConstrainVout(tx.vout[0],0, origaddr, nValue) == 0 )
return eval->Invalid("invalid refund for cancelbuy");
preventCCvins = 2;
preventCCvins = 3;
preventCCvouts = 0;
fprintf(stderr,"cancelbuy validated to origaddr.(%s)\n",origaddr);
break;
@ -224,32 +256,35 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
//vout.4: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey]
preventCCvouts = 4;
if ( (nValue= AssetValidateBuyvin(cp,eval,totalunits,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
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 )
else if( tmporigpubkey != origpubkey )
return eval->Invalid("mismatched origpubkeys for fillbuy");
else
{
if ( nValue != tx.vout[0].nValue+tx.vout[1].nValue )
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,CCaddr,0) == 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,CCaddr,inputs) == 0 )
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 )
else if( ConstrainVout(tx.vout[1],0,0,0) == 0 )
return eval->Invalid("vout1 is CC for fillbuy");
else if ( ValidateBidRemainder(remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,totalunits) == false )
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 )
else if( remaining_price != 0 )
{
if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,0) == 0 )
if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr, 0) == 0 ) // coins on asset unspendable cc addr
return eval->Invalid("mismatched vout0 AssetsCCaddr for fillbuy");
}
}
@ -261,36 +296,43 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
//vin.0: normal input
//vin.1+: valid CC output for sale
//vout.0: vin.1 assetoshis output to CC to unspendable
//vout.1: CC output for change (if any)
//vout.2: normal output for change (if any)
//vout.1: CC output for marker
//vout.2: CC output for change (if any)
//vout.3: normal output for change (if any)
//'s'.vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey]
//'e'.vout.n-1: opreturn [EVAL_ASSETS] ['e'] [assetid] [assetid2] [amount of asset2 required] [origpubkey]
preventCCvouts = 1;
if ( remaining_price == 0 )
preventCCvouts = 2;
if( remaining_price == 0 )
return eval->Invalid("illegal null remaining_price for selloffer");
if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() != 0 )
if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 )
return eval->Invalid("invalid normal vout1 for sellvin");
if( tx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 ) // cc change
{
preventCCvouts++;
if ( ConstrainVout(tx.vout[0],1,(char *)cp->unspendableCCaddr,0) == 0 )
return eval->Invalid("mismatched vout0 AssetsCCaddr for selloffer");
else if ( tx.vout[0].nValue+tx.vout[1].nValue != inputs )
return eval->Invalid("mismatched vout0+vout1 total for selloffer");
} else if ( ConstrainVout(tx.vout[0],1,(char *)cp->unspendableCCaddr,inputs) == 0 )
return eval->Invalid("mismatched vout0 AssetsCCaddr for selloffer");
if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, 0) == 0 ) // check also cc vout[0]
return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer");
else if( tx.vout[0].nValue + tx.vout[2].nValue != inputs )
return eval->Invalid("mismatched vout0+vout2 total for selloffer");
}
else if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, inputs) == 0 ) // no cc change, just vout[0]
return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer");
//fprintf(stderr,"remaining.%d for sell\n",(int32_t)remaining_price);
break;
case 'x': // cancel
case 'x': // cancel
//vin.0: normal input
//vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx
//vin.2: CC marker from selloffer for txfee
//vout.0: vin.1 assetoshis to original pubkey CC sellTx/exchangeTx.vout[0].nValue -> [origpubkey]
//vout.1: normal output for change (if any)
//vout.1: vin.2 back to users pubkey
//vout.2: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid]
if ( (assetoshis= AssetValidateSellvin(cp,eval,tmpprice,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
if( (assetoshis = AssetValidateSellvin(cpAssets, eval, tmpprice, tmporigpubkey, userTokensCCaddr, origaddr, tx, assetid)) == 0 ) // NOTE:
return(false);
else if ( ConstrainVout(tx.vout[0],1,CCaddr,assetoshis) == 0 )
else if( ConstrainVout(tx.vout[0], 1, userTokensCCaddr, assetoshis) == 0 )
return eval->Invalid("invalid vout for cancel");
preventCCvins = 2;
preventCCvins = 3;
preventCCvouts = 1;
break;
@ -303,31 +345,37 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
//'S'.vout.2: vin.2 value to original pubkey [origpubkey]
//vout.3: normal output for change (if any)
//'S'.vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey]
if ( (assetoshis= AssetValidateSellvin(cp,eval,totalunits,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
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 )
else if( tmporigpubkey != origpubkey )
return eval->Invalid("mismatched origpubkeys for fillask");
else
{
if ( assetoshis != tx.vout[0].nValue+tx.vout[1].nValue )
if( assetoshis != tx.vout[0].nValue + tx.vout[1].nValue )
return eval->Invalid("locked value doesnt match vout0+1 fillask");
if ( ValidateAskRemainder(remaining_price,tx.vout[0].nValue,assetoshis,tx.vout[1].nValue,tx.vout[2].nValue,totalunits) == false )
if( ValidateAskRemainder(remaining_price, tx.vout[0].nValue, assetoshis, tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false )
return eval->Invalid("mismatched remainder for fillask");
else if ( ConstrainVout(tx.vout[1],1,0,0) == 0 )
else if( ConstrainVout(tx.vout[1], 1, 0, 0) == 0 )
return eval->Invalid("normal vout1 for fillask");
else if ( ConstrainVout(tx.vout[2],0,origaddr,0) == 0 )
else if( ConstrainVout(tx.vout[2], 0, origaddr, 0) == 0 )
return eval->Invalid("normal vout1 for fillask");
else if ( remaining_price != 0 )
else if( ConstrainVout(tx.vout[3], 1, origpubkeyCCaddr, 10000) == 0 )
return eval->Invalid("invalid marker for original pubkey");
else if( remaining_price != 0 )
{
if ( ConstrainVout(tx.vout[0],1,(char *)cp->unspendableCCaddr,0) == 0 )
return eval->Invalid("mismatched vout0 AssetsCCaddr for fill");
//char tokensUnspendableAddr[64];
//GetTokensCCaddress(cpAssets, tokensUnspendableAddr, GetUnspendable(cpAssets, NULL));
if ( ConstrainVout(tx.vout[0], 1, tokensUnspendableAddr /*(char *)cpAssets->unspendableCCaddr*/, 0) == 0 )
return eval->Invalid("mismatched vout0 assets dual unspendable CCaddr for fill sell");
}
}
fprintf(stderr,"fill validated\n");
break;
case 'E': // fillexchange
////////// not implemented yet ////////////
return eval->Invalid("unexpected assets fillexchange funcid");
break; // disable asset swaps
//vin.0: normal input
@ -339,51 +387,63 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
//vout.3: CC output for asset2 change (if any)
//vout.3/4: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['E'] [assetid vin0+1] [assetid vin2] [remaining asset2 required] [origpubkey]
if ( AssetExactAmounts(1, cp,inputs,1,outputs,eval,tx,assetid2) == false )
eval->Invalid("asset2 inputs != outputs");
if ( (assetoshis= AssetValidateSellvin(cp,eval,totalunits,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
//if ( AssetExactAmounts(false, cp,inputs,outputs,eval,tx,assetid2) == false )
// eval->Invalid("asset2 inputs != outputs");
////////// not implemented yet ////////////
if( (assetoshis= AssetValidateSellvin(cpTokens, eval, totalunits, tmporigpubkey, userTokensCCaddr, origaddr, tx, assetid)) == 0 )
return(false);
else if ( numvouts < 3 )
else if( numvouts < 3 )
return eval->Invalid("not enough vouts for fillex");
else if ( tmporigpubkey != origpubkey )
else if( tmporigpubkey != origpubkey )
return eval->Invalid("mismatched origpubkeys for fillex");
else
{
if ( assetoshis != tx.vout[0].nValue+tx.vout[1].nValue )
if( assetoshis != tx.vout[0].nValue + tx.vout[1].nValue )
return eval->Invalid("locked value doesnt match vout0+1 fillex");
else if ( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 )
else if( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 )
////////// not implemented yet ////////////
{
if ( ConstrainVout(tx.vout[2],1,CCaddr,0) == 0 )
if( ConstrainVout(tx.vout[2], 1, userTokensCCaddr, 0) == 0 )
return eval->Invalid("vout2 doesnt go to origpubkey fillex");
else if ( inputs != tx.vout[2].nValue+tx.vout[3].nValue )
else if( inputs != tx.vout[2].nValue + tx.vout[3].nValue )
{
fprintf(stderr,"inputs %.8f != %.8f + %.8f\n",(double)inputs/COIN,(double)tx.vout[2].nValue/COIN,(double)tx.vout[3].nValue/COIN);
return eval->Invalid("asset inputs doesnt match vout2+3 fillex");
}
}
else if ( ConstrainVout(tx.vout[2],1,CCaddr,inputs) == 0 )
////////// not implemented yet ////////////
else if( ConstrainVout(tx.vout[2], 1, userTokensCCaddr, inputs) == 0 )
return eval->Invalid("vout2 doesnt match inputs fillex");
else if ( ConstrainVout(tx.vout[1],0,0,0) == 0 )
else if( ConstrainVout(tx.vout[1], 0, 0, 0) == 0 )
return eval->Invalid("vout1 is CC for fillex");
fprintf(stderr,"assets vout0 %llu, vin1 %llu, vout2 %llu -> orig, vout1 %llu, total %llu\n",(long long)tx.vout[0].nValue,(long long)assetoshis,(long long)tx.vout[2].nValue,(long long)tx.vout[1].nValue,(long long)totalunits);
if ( ValidateSwapRemainder(remaining_price,tx.vout[0].nValue,assetoshis,tx.vout[1].nValue,tx.vout[2].nValue,totalunits) == false )
if( ValidateSwapRemainder(remaining_price, tx.vout[0].nValue, assetoshis,tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false )
return eval->Invalid("mismatched remainder for fillex");
else if ( ConstrainVout(tx.vout[1],1,0,0) == 0 )
else if( ConstrainVout(tx.vout[1], 1, 0, 0) == 0 )
////////// not implemented yet ////////////
return eval->Invalid("normal vout1 for fillex");
else if ( remaining_price != 0 )
else if( remaining_price != 0 )
{
if ( ConstrainVout(tx.vout[0],1,(char *)cp->unspendableCCaddr,0) == 0 )
if( ConstrainVout(tx.vout[0], 1, (char *)cpAssets->unspendableCCaddr, 0) == 0 ) // TODO: unsure about this, but this is not impl yet anyway
return eval->Invalid("mismatched vout0 AssetsCCaddr for fillex");
}
}
////////// not implemented yet ////////////
fprintf(stderr,"fill validated\n");
break;
default:
fprintf(stderr,"illegal assets funcid.(%c)\n",funcid);
return eval->Invalid("unexpected assets funcid");
break;
//break;
}
return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
// what does this do?
bool bPrevent = PreventCC(eval, tx, preventCCvins, numvins, preventCCvouts, numvouts);
std::cerr << "AssetsValidate() PreventCC returned=" << bPrevent << std::endl;
return (bPrevent);
}

2
src/cc/auction.cpp

@ -73,7 +73,7 @@ bool AuctionExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransactio
bool AuctionValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
{
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; bool retval;
return(false); // reject any auction CC for now
return eval->Invalid("no validation yet");
numvins = tx.vin.size();
numvouts = tx.vout.size();
preventCCvins = preventCCvouts = -1;

317
src/cc/cclib.cpp

@ -0,0 +1,317 @@
/******************************************************************************
* 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. *
* *
******************************************************************************/
#include <assert.h>
#include <cryptoconditions.h>
#include "primitives/block.h"
#include "primitives/transaction.h"
#include "script/cc.h"
#include "cc/eval.h"
#include "cc/utils.h"
#include "cc/CCinclude.h"
#include "main.h"
#include "chain.h"
#include "core_io.h"
#include "crosschain.h"
#define FAUCET2SIZE COIN
struct CClib_rpcinfo
{
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 *)"<no args>", 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<sizeof(CClib_methods)/sizeof(*CClib_methods); i++)
{
UniValue obj(UniValue::VOBJ);
if ( CClib_methods[i].funcid < ' ' || CClib_methods[i].funcid >= 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<sizeof(CClib_methods)/sizeof(*CClib_methods); i++)
{
if ( strcmp(method,CClib_methods[i].method) == 0 )
{
result.push_back(Pair("result","success"));
result.push_back(Pair("method",CClib_methods[i].method));
rawtx = CClib_rawtxgen(cp,CClib_methods[i].funcid,params);
result.push_back(Pair("rawtx",rawtx));
return(result);
}
}
result.push_back(Pair("result","error"));
result.push_back(Pair("method",CClib_methods[i].method));
result.push_back(Pair("error","method not found"));
return(result);
}
int64_t IsCClibvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v)
{
char destaddr[64];
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
{
if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 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; i<numvins; i++)
{
//fprintf(stderr,"vini.%d\n",i);
if ( (*cp->ismyvin)(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; i<numvouts; i++)
{
//fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
if ( (assetoshis= IsCClibvout(cp,tx,i)) != 0 )
outputs += assetoshis;
}
if ( inputs != outputs+FAUCET2SIZE+txfee )
{
fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
return eval->Invalid("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<std::pair<CAddressIndexKey, CAmount> > txids;
numvins = tx.vin.size();
numvouts = tx.vout.size();
preventCCvins = preventCCvouts = -1;
if ( numvouts < 1 )
return eval->Invalid("no vouts");
else
{
for (i=0; i<numvins; i++)
{
if ( IsCCInput(tx.vin[0].scriptSig) == 0 )
{
fprintf(stderr,"faucetget invalid vini\n");
return eval->Invalid("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<std::pair<CAddressIndexKey, CAmount> >::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<uint8_t> origpubkey; CTransaction vintx; int32_t vout,n = 0;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
GetCCaddress(cp,coinaddr,pk);
SetCCunspents(unspentOutputs,coinaddr);
threshold = total/(maxinputs+1);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::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("");
}

421
src/cc/channels.cpp

@ -64,15 +64,16 @@ Possible third iteration:
int64_t IsChannelsvout(struct CCcontract_info *cp,const CTransaction& tx,CPubKey srcpub, CPubKey destpub,int32_t v)
{
char destaddr[65],channeladdr[65];
char destaddr[65],channeladdr[65],tokenschanneladdr[65];
GetCCaddress1of2(cp,channeladdr,srcpub,destpub);
GetTokensCCaddress1of2(cp,tokenschanneladdr,srcpub,destpub);
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
{
if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,channeladdr) == 0 )
if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && (strcmp(destaddr,channeladdr) == 0 || strcmp(destaddr,tokenschanneladdr) == 0))
return(tx.vout[v].nValue);
}
return(0);
return(0);
}
int64_t IsChannelsMarkervout(struct CCcontract_info *cp,const CTransaction& tx,CPubKey pubkey,int32_t v)
@ -88,17 +89,31 @@ int64_t IsChannelsMarkervout(struct CCcontract_info *cp,const CTransaction& tx,C
return(0);
}
CScript EncodeChannelsOpRet(uint8_t funcid,uint256 opentxid,CPubKey srcpub,CPubKey destpub,int32_t numpayments,int64_t payment,uint256 hashchain)
CScript EncodeChannelsOpRet(uint8_t funcid,uint256 tokenid,uint256 opentxid,CPubKey srcpub,CPubKey destpub,int32_t numpayments,int64_t payment,uint256 hashchain)
{
CScript opret; uint8_t evalcode = EVAL_CHANNELS;
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << opentxid << srcpub << destpub << numpayments << payment << hashchain);
if (tokenid!=zeroid)
{
std::vector<CPubKey> pks;
pks.push_back(srcpub);
pks.push_back(destpub);
return(EncodeTokenOpRet(tokenid,pks,opret));
}
return(opret);
}
uint8_t DecodeChannelsOpRet(const CScript &scriptPubKey,uint256 &opentxid, CPubKey &srcpub,CPubKey &destpub,int32_t &numpayments,int64_t &payment,uint256 &hashchain)
uint8_t DecodeChannelsOpRet(const CScript &scriptPubKey, uint256 &tokenid, uint256 &opentxid, CPubKey &srcpub,CPubKey &destpub,int32_t &numpayments,int64_t &payment,uint256 &hashchain)
{
std::vector<uint8_t> vopret; uint8_t *script,e,f;
GetOpReturnData(scriptPubKey, vopret);
std::vector<uint8_t> vopret; uint8_t *script,e,f,tokenevalcode;
std::vector<CPubKey> pubkeys; std::vector<uint8_t> vOpretExtra;
if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys,vOpretExtra)!=0 && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0)
{
if (!E_UNMARSHAL(vOpretExtra, { ss >> vopret; })) return (0);
}
else GetOpReturnData(scriptPubKey, vopret);
if ( vopret.size() > 2 )
{
script = (uint8_t *)vopret.data();
@ -115,46 +130,56 @@ uint8_t DecodeChannelsOpRet(const CScript &scriptPubKey,uint256 &opentxid, CPubK
bool ChannelsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee)
{
static uint256 zerohash;
uint256 txid,param3;
uint256 txid,param3,tokenid;
CPubKey srcpub,destpub;
int32_t param1; int64_t param2; uint8_t funcid;
CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis;
numvins = tx.vin.size();
numvouts = tx.vout.size();
int32_t param1,numvouts; int64_t param2; uint8_t funcid;
CTransaction vinTx; uint256 hashBlock; int64_t inputs=0,outputs=0;
if ((numvouts=tx.vout.size()) > 0 && DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey, txid, srcpub, destpub, param1, param2, param3)!=0)
{
for (i=0; i<numvins; i++)
if ((numvouts=tx.vout.size()) > 0 && (funcid=DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, param1, param2, param3))!=0)
{
switch (funcid)
{
if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
return eval->Invalid("cant find vinTx");
else
{
inputs += vinTx.vout[tx.vin[i].prevout.n].nValue;
}
case 'O':
return (true);
case 'P':
if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 )
return eval->Invalid("cant find vinTx");
inputs = vinTx.vout[tx.vin[1].prevout.n].nValue;
outputs = tx.vout[0].nValue + tx.vout[3].nValue;
break;
case 'C':
if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 )
return eval->Invalid("cant find vinTx");
inputs = vinTx.vout[tx.vin[1].prevout.n].nValue;
outputs = tx.vout[0].nValue;
break;
case 'R':
if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 )
return eval->Invalid("cant find vinTx");
inputs = vinTx.vout[tx.vin[1].prevout.n].nValue;
outputs = tx.vout[2].nValue;
break;
default:
return (false);
}
if ( inputs != outputs )
{
fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
return eval->Invalid("mismatched inputs != outputs");
}
else return (true);
}
else
{
return eval->Invalid("invalid op_return data");
}
for (i=0; i<numvouts; i++)
{
outputs += tx.vout[i].nValue;
}
if ( inputs != outputs+txfee )
{
fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
return eval->Invalid("mismatched inputs != outputs + txfee");
}
else return(true);
return(false);
}
bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
{
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numpayments,p1,param1; bool retval;
uint256 txid,hashblock,p3,param3,opentxid,tmp_txid,genhashchain,hashchain;
uint256 txid,hashblock,p3,param3,opentxid,tmp_txid,genhashchain,hashchain,tokenid;
uint8_t funcid,hash[32],hashdest[32];
int64_t p2,param2,payment;
CPubKey srcpub, destpub;
@ -169,15 +194,13 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &
{
if (ChannelsExactAmounts(cp,eval,tx,1,10000) == false )
{
fprintf(stderr,"Channelsget invalid amount\n");
return false;
return eval->Invalid("invalid channel inputs vs. outputs!");
}
else
{
txid = tx.GetHash();
memcpy(hash,&txid,sizeof(hash));
if ( (funcid = DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey, opentxid, srcpub, destpub, param1, param2, param3)) != 0)
if ( (funcid = DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey, tokenid, opentxid, srcpub, destpub, param1, param2, param3)) != 0)
{
switch ( funcid )
{
@ -213,9 +236,13 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &
return eval->Invalid("vout.1 is CC for channelPayment (marker to srcPub)!");
else if ( IsChannelsMarkervout(cp,tx,destpub,2)==0 )
return eval->Invalid("vout.2 is CC for channelPayment (marker to dstPub)!");
else if ( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 )
else if ( tokenid!=zeroid && tx.vout[3].scriptPubKey.IsPayToCryptoCondition() == 0 )
return eval->Invalid("vout.3 is CC for channelPayment!");
else if ( tokenid==zeroid && tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 )
return eval->Invalid("vout.3 is normal for channelPayment!");
else if ( tx.vout[3].scriptPubKey!=CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG)
else if ( tokenid!=zeroid && tx.vout[3].scriptPubKey!=MakeCC1vout(EVAL_TOKENS,tx.vout[3].nValue,destpub).scriptPubKey)
return eval->Invalid("payment funds do not go to receiver!");
else if ( tokenid==zeroid && tx.vout[3].scriptPubKey!=CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG)
return eval->Invalid("payment funds do not go to receiver!");
else if ( param1 > CHANNELS_MAXPAYMENTS)
return eval->Invalid("too many payment increments!");
@ -223,7 +250,7 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &
{
if (myGetTransaction(opentxid,channelOpenTx,hashblock) != 0)
{
if ((numvouts=channelOpenTx.vout.size()) > 0 && (funcid=DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tmp_txid, srcpub, destpub, numpayments, payment, hashchain)) != 0 && funcid!='O')
if ((numvouts=channelOpenTx.vout.size()) > 0 && (funcid=DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, numpayments, payment, hashchain)) != 0 && funcid!='O')
return eval->Invalid("invalid channelopen OP_RETURN data!");
endiancpy(hash, (uint8_t * ) & param3, 32);
for (i = 0; i < numpayments-param1; i++)
@ -239,7 +266,7 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &
}
if (myGetTransaction(tx.vin[1].prevout.hash,prevTx,hashblock) != 0)
{
if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tmp_txid, srcpub, destpub, p1, p2, p3) == 0)
if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0)
return eval->Invalid("invalid previous tx OP_RETURN data!");
else if (tx.vout[1].scriptPubKey != prevTx.vout[1].scriptPubKey)
return eval->Invalid("invalid destination for sender marker!");
@ -279,13 +306,13 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &
return eval->Invalid("too many payment increments!");
else if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0)
return eval->Invalid("invalid open txid!");
else if ((numvouts=channelOpenTx.vout.size()) > 0 && DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tmp_txid, srcpub, destpub, numpayments, payment, hashchain) != 'O')
else if ((numvouts=channelOpenTx.vout.size()) > 0 && DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, numpayments, payment, hashchain) != 'O')
return eval->Invalid("invalid channelopen OP_RETURN data!");
else if (tx.vout[0].nValue != param1*payment)
return eval->Invalid("vout amount does not match number_of_payments*payment!");
else if (myGetTransaction(tx.vin[1].prevout.hash,prevTx,hashblock) != 0)
{
if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tmp_txid, srcpub, destpub, p1, p2, p3) == 0)
if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0)
return eval->Invalid("invalid previous tx OP_RETURN data!");
else if (tx.vout[1].scriptPubKey != prevTx.vout[1].scriptPubKey)
return eval->Invalid("invalid destination for sender marker!");
@ -318,19 +345,23 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &
return eval->Invalid("vout.0 is CC for channelRefund (marker to srcPub)!");
else if ( IsChannelsMarkervout(cp,tx,destpub,1)==0 )
return eval->Invalid("vout.1 is CC for channelRefund (marker to dstPub)!");
else if ( tx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 )
return eval->Invalid("vout.2 is normal for channelRefund!");
else if ( tx.vout[2].scriptPubKey!=CScript() << ParseHex(HexStr(srcpub)) << OP_CHECKSIG)
else if ( tokenid!=zeroid && tx.vout[2].scriptPubKey.IsPayToCryptoCondition() == 0 )
return eval->Invalid("vout.2 is CC for channelPayment!");
else if ( tokenid==zeroid && tx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 )
return eval->Invalid("vout.2 is normal for channelPayment!");
else if ( tokenid!=zeroid && tx.vout[2].scriptPubKey!=MakeCC1vout(EVAL_TOKENS,tx.vout[2].nValue,srcpub).scriptPubKey)
return eval->Invalid("payment funds do not go to sender!");
else if ( tokenid==zeroid && tx.vout[2].scriptPubKey!=CScript() << ParseHex(HexStr(srcpub)) << OP_CHECKSIG)
return eval->Invalid("payment funds do not go to sender!");
else if ( param1 > CHANNELS_MAXPAYMENTS)
return eval->Invalid("too many payment increments!");
else if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0)
return eval->Invalid("invalid open txid!");
else if ((numvouts=channelOpenTx.vout.size()) > 0 && DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tmp_txid, srcpub, destpub, numpayments, payment, hashchain) != 'O')
else if ((numvouts=channelOpenTx.vout.size()) > 0 && DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, numpayments, payment, hashchain) != 'O')
return eval->Invalid("invalid channelopen OP_RETURN data!");
else if (myGetTransaction(param3,channelCloseTx,hashblock) == 0)
return eval->Invalid("invalid close txid!");
else if ((numvouts=channelCloseTx.vout.size()) > 0 && DecodeChannelsOpRet(channelCloseTx.vout[numvouts-1].scriptPubKey, tmp_txid, srcpub, destpub, param1, param2, param3) != 'C')
else if ((numvouts=channelCloseTx.vout.size()) > 0 && DecodeChannelsOpRet(channelCloseTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, param1, param2, param3) != 'C')
return eval->Invalid("invalid channelclose OP_RETURN data!");
else if (tmp_txid!=opentxid)
return eval->Invalid("invalid close tx, opentxid do not match on close and refund!");
@ -338,7 +369,7 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &
return eval->Invalid("vout amount does not match number_of_payments*payment!");
else if (myGetTransaction(tx.vin[1].prevout.hash,prevTx,hashblock) != 0)
{
if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tmp_txid, srcpub, destpub, p1, p2, p3) == 0)
if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0)
return eval->Invalid("invalid previous tx OP_RETURN data!");
else if (tx.vout[0].scriptPubKey != prevTx.vout[1].scriptPubKey)
return eval->Invalid("invalid destination for sender marker!");
@ -351,9 +382,9 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &
default:
fprintf(stderr,"illegal channels funcid.(%c)\n",funcid);
return eval->Invalid("unexpected channels funcid");
break;
}
} else return eval->Invalid("unexpected channels missing funcid");
}
else return eval->Invalid("unexpected channels missing funcid");
retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts);
if ( retval != 0 )
fprintf(stderr,"Channel tx validated\n");
@ -368,14 +399,15 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &
int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx, CTransaction openTx, uint256 &prevtxid, CPubKey mypk)
{
char coinaddr[65]; int64_t param2,totalinputs = 0,numvouts; uint256 txid=zeroid,tmp_txid,hashBlock,param3; CTransaction tx; int32_t marker,param1;
char coinaddr[65]; int64_t param2,totalinputs = 0,numvouts; uint256 txid=zeroid,tmp_txid,hashBlock,param3,tokenid; CTransaction tx; int32_t marker,param1;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
CPubKey srcpub,destpub;
uint8_t myprivkey[32];
if ((numvouts=openTx.vout.size()) > 0 && DecodeChannelsOpRet(openTx.vout[numvouts-1].scriptPubKey,tmp_txid,srcpub,destpub,param1,param2,param3)=='O')
if ((numvouts=openTx.vout.size()) > 0 && DecodeChannelsOpRet(openTx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3)=='O')
{
GetCCaddress1of2(cp,coinaddr,srcpub,destpub);
if (tokenid!=zeroid) GetTokensCCaddress1of2(cp,coinaddr,srcpub,destpub);
else GetCCaddress1of2(cp,coinaddr,srcpub,destpub);
SetCCunspents(unspentOutputs,coinaddr);
}
else
@ -389,9 +421,9 @@ int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx, C
{
if ( (int32_t)it->first.index==0 && GetTransaction(it->first.txhash,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size()) > 0)
{
if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tmp_txid,srcpub,destpub,param1,param2,param3)!=0 &&
(tmp_txid==openTx.GetHash() || tx.GetHash()==openTx.GetHash()) &&
(totalinputs=IsChannelsvout(cp,tx,srcpub,destpub,0)+IsChannelsMarkervout(cp,tx,srcpub,marker))>0)
if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3)!=0 &&
(tmp_txid==openTx.GetHash() || tx.GetHash()==openTx.GetHash()) && IsChannelsMarkervout(cp,tx,marker==1?srcpub:destpub,marker)>0 &&
(totalinputs=IsChannelsvout(cp,tx,srcpub,destpub,0))>0)
{
txid = it->first.txhash;
break;
@ -407,11 +439,11 @@ int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx, C
const CTransaction &txmempool = e.GetTx();
const uint256 &hash = txmempool.GetHash();
if ((numvouts=txmempool.vout.size()) > 0 && DecodeChannelsOpRet(txmempool.vout[numvouts-1].scriptPubKey,tmp_txid,srcpub,destpub,param1,param2,param3) != 0 &&
if ((numvouts=txmempool.vout.size()) > 0 && DecodeChannelsOpRet(txmempool.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3) != 0 &&
tmp_txid==openTx.GetHash() && param1 < mindepth)
{
txid=hash;
totalinputs=txmempool.vout[0].nValue+txmempool.vout[1].nValue;
totalinputs=txmempool.vout[0].nValue;
mindepth=param1;
}
}
@ -421,19 +453,19 @@ int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx, C
prevtxid=txid;
mtx.vin.push_back(CTxIn(txid,0,CScript()));
mtx.vin.push_back(CTxIn(txid,marker,CScript()));
Myprivkey(myprivkey);
CCaddr2set(cp,EVAL_CHANNELS,srcpub,myprivkey,coinaddr);
CCaddr3set(cp,EVAL_CHANNELS,destpub,myprivkey,coinaddr);
Myprivkey(myprivkey);
if (tokenid!=zeroid) CCaddrTokens1of2set(cp,srcpub,destpub,coinaddr);
else CCaddr1of2set(cp,srcpub,destpub,coinaddr);
return totalinputs;
}
else return 0;
}
std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64_t payment)
std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64_t payment, uint256 tokenid)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
uint8_t hash[32],hashdest[32]; uint64_t funds; int32_t i; uint256 hashchain,entropy,hentropy;
CPubKey mypk; struct CCcontract_info *cp,C;
uint8_t hash[32],hashdest[32]; uint64_t amount,tokens=0,funds; int32_t i; uint256 hashchain,entropy,hentropy;
CPubKey mypk; struct CCcontract_info *cp,*cpTokens,C,CTokens;
if ( numpayments <= 0 || payment <= 0 || numpayments > CHANNELS_MAXPAYMENTS )
{
@ -442,11 +474,18 @@ std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64
return("");
}
cp = CCinit(&C,EVAL_CHANNELS);
cpTokens = CCinit(&CTokens,EVAL_TOKENS);
if ( txfee == 0 )
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
funds = numpayments * payment;
if ( AddNormalinputs(mtx,mypk,funds+3*txfee,64) > 0 )
if (tokenid!=zeroid)
{
amount=AddNormalinputs(mtx,mypk,3*txfee,5);
tokens=AddTokenCCInputs(cpTokens, mtx, mypk, tokenid, funds, 64);
}
else amount=AddNormalinputs(mtx,mypk,funds+3*txfee,64);
if (amount+tokens >= funds+2*txfee)
{
hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash,mtx.vin[0].prevout.n,1);
endiancpy(hash,(uint8_t *)&hentropy,32);
@ -456,18 +495,22 @@ std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64
memcpy(hash,hashdest,32);
}
endiancpy((uint8_t *)&hashchain,hashdest,32);
mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS,funds,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('O',zeroid,mypk,destpub,numpayments,payment,hashchain)));
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("");
}
std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint256 secret)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk,srcpub,destpub; uint256 txid,hashchain,gensecret,hashblock,entropy,hentropy,prevtxid,param3;
CPubKey mypk,srcpub,destpub; uint256 txid,hashchain,gensecret,hashblock,entropy,hentropy,prevtxid,param3,tokenid;
struct CCcontract_info *cp,C; int32_t i,funcid,prevdepth,numvouts,numpayments,totalnumpayments;
int64_t payment,change,funds,param2;
uint8_t hash[32],hashdest[32];
@ -479,94 +522,106 @@ 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");
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)
{
CCerror = strprintf("this is not our channel");
fprintf(stderr,"%s\n",CCerror.c_str());
return("");
}
else if (amount % payment != 0 || amount<payment)
{
CCerror = strprintf("invalid amount, not a magnitude of payment size");
fprintf(stderr,"%s\n",CCerror.c_str());
return ("");
}
}
else
{
CCerror = strprintf("invalid channel open tx");
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 && (change=funds-amount-txfee)>=0)
{
if ((numvouts=channelOpenTx.vout.size()) > 0 && DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, txid, srcpub, destpub, totalnumpayments, payment, hashchain)=='O')
if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && (change=funds-amount)>=0)
{
numpayments=amount/payment;
if (GetTransaction(prevtxid,prevTx,hashblock,false) != 0 && (numvouts=prevTx.vout.size()) > 0 &&
((funcid = DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, prevdepth, param2, param3)) != 0) &&
(funcid == 'P' || funcid=='O'))
{
if (mypk != srcpub && mypk != destpub)
if (numpayments > prevdepth)
{
fprintf(stderr,"this is not our channel\n");
return("");
}
else if (amount % payment != 0 || amount<payment)
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, not a magnitude of payment size\n");
CCerror = strprintf("invalid amount");
fprintf(stderr,"%s\n",CCerror.c_str());
return ("");
}
numpayments=amount/payment;
if (GetTransaction(prevtxid,prevTx,hashblock,false) != 0 && (numvouts=prevTx.vout.size()) > 0 &&
((funcid = DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, txid, srcpub, destpub, prevdepth, param2, param3)) != 0) &&
(funcid == 'P' || funcid=='O'))
if (secret!=zeroid)
{
if (numpayments > prevdepth)
endiancpy(hash, (uint8_t * ) & secret, 32);
for (i = 0; i < totalnumpayments-(prevdepth-numpayments); i++)
{
fprintf(stderr,"not enough funds in channel for that amount\n");
return ("");
} else if (numpayments == 0)
vcalc_sha256(0, hashdest, hash, 32);
memcpy(hash, hashdest, 32);
}
endiancpy((uint8_t * ) & gensecret, hashdest, 32);
if (gensecret!=hashchain)
{
fprintf(stderr,"invalid amount\n");
return ("");
CCerror = strprintf("invalid secret supplied");
fprintf(stderr,"%s\n",CCerror.c_str());
return("");
}
if (secret!=zeroid)
}
else
{
hentropy = DiceHashEntropy(entropy,channelOpenTx.vin[0].prevout.hash,channelOpenTx.vin[0].prevout.n,1);
if (prevdepth-numpayments)
{
endiancpy(hash, (uint8_t * ) & secret, 32);
for (i = 0; i < totalnumpayments-(prevdepth-numpayments); i++)
endiancpy(hash, (uint8_t * ) & hentropy, 32);
for (i = 0; i < prevdepth-numpayments; i++)
{
vcalc_sha256(0, hashdest, hash, 32);
memcpy(hash, hashdest, 32);
}
endiancpy((uint8_t * ) & gensecret, hashdest, 32);
if (gensecret!=hashchain)
{
fprintf(stderr,"invalid secret supplied\n");
return("");
}
endiancpy((uint8_t * ) & secret, hashdest, 32);
}
else
{
hentropy = DiceHashEntropy(entropy,channelOpenTx.vin[0].prevout.hash,channelOpenTx.vin[0].prevout.n,1);
if (prevdepth-numpayments)
{
endiancpy(hash, (uint8_t * ) & hentropy, 32);
for (i = 0; i < prevdepth-numpayments; i++)
{
vcalc_sha256(0, hashdest, hash, 32);
memcpy(hash, hashdest, 32);
}
endiancpy((uint8_t * ) & secret, hashdest, 32);
}
else endiancpy((uint8_t * ) & secret, (uint8_t * ) & hentropy, 32);
}
}
else
{
fprintf(stderr,"invalid previous tx\n");
return("");
else endiancpy((uint8_t * ) & secret, (uint8_t * ) & hentropy, 32);
}
}
else
{
fprintf(stderr, "invalid channel open tx\n");
return ("");
}
mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS, change, srcpub, destpub));
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));
else mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS, change, srcpub, destpub));
mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,srcpub));
mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,destpub));
mtx.vout.push_back(CTxOut(amount, CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG));
return (FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeChannelsOpRet('P', opentxid, srcpub, destpub, prevdepth-numpayments, numpayments, secret)));
if (tokenid!=zeroid) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, amount, destpub));
else mtx.vout.push_back(CTxOut(amount, CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG));
return (FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeChannelsOpRet('P', tokenid, opentxid, srcpub, destpub, prevdepth-numpayments, numpayments, secret)));
}
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("");
}
@ -575,7 +630,7 @@ std::string ChannelClose(uint64_t txfee,uint256 opentxid)
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk,srcpub,destpub; struct CCcontract_info *cp,C;
CTransaction channelOpenTx;
uint256 hashblock,tmp_txid,prevtxid,hashchain;
uint256 hashblock,tmp_txid,prevtxid,hashchain,tokenid;
int32_t numvouts,numpayments;
int64_t payment,funds;
@ -586,35 +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,tmp_txid,srcpub,destpub,numpayments,payment,hashchain)!='O')
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)
{
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',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("");
}
@ -623,10 +684,9 @@ std::string ChannelRefund(uint64_t txfee,uint256 opentxid,uint256 closetxid)
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; struct CCcontract_info *cp,C; int64_t funds,payment,param2;
int32_t i,numpayments,numvouts,param1;
uint256 hashchain,hashblock,txid,prevtxid,param3,entropy,hentropy,secret;
uint256 hashchain,hashblock,txid,prevtxid,param3,tokenid;
CTransaction channelOpenTx,channelCloseTx,prevTx;
CPubKey srcpub,destpub;
uint8_t funcid,hash[32],hashdest[32];;
// verify stoptxid and origtxid match and are mine
cp = CCinit(&C,EVAL_CHANNELS);
@ -635,71 +695,75 @@ 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,txid,srcpub,destpub,param1,param2,param3)!='C')
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,txid,srcpub,destpub,numpayments,payment,hashchain)!='O')
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, txid, srcpub, destpub, param1, param2, param3) != 0)
DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, param1, param2, param3) != 0)
{
hentropy = DiceHashEntropy(entropy, channelOpenTx.vin[0].prevout.hash, channelOpenTx.vin[0].prevout.n,1);
endiancpy(hash, (uint8_t * ) & hentropy, 32);
for (i = 0; i < param1; i++)
{
vcalc_sha256(0, hashdest, hash, 32);
memcpy(hash, hashdest, 32);
}
endiancpy((uint8_t * ) & secret, hashdest, 32);
mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk));
mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,destpub));
mtx.vout.push_back(CTxOut(funds-txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('R',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<std::pair<CAddressIndexKey, CAmount> > txids; struct CCcontract_info *cp,C; uint256 txid,hashBlock,tmp_txid,param3;
UniValue result(UniValue::VOBJ); std::vector<std::pair<CAddressIndexKey, CAmount> > txids; struct CCcontract_info *cp,C; uint256 txid,hashBlock,tmp_txid,param3,tokenid;
CTransaction tx; char myCCaddr[65],addr[65],str[256]; CPubKey mypk,srcpub,destpub; int32_t vout,numvouts,param1;
int64_t nValue,param2;
@ -716,7 +780,7 @@ UniValue ChannelsList()
nValue = (int64_t)it->second;
if ( (vout == 1 || vout == 2) && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 )
{
if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tmp_txid,srcpub,destpub,param1,param2,param3) == 'O')
if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3) == 'O')
{
GetCCaddress1of2(cp,addr,srcpub,destpub);
sprintf(str,"%s - %lld payments of %lld satoshi",addr,(long long)param1,(long long)param2);
@ -729,16 +793,16 @@ UniValue ChannelsList()
UniValue ChannelsInfo(uint256 channeltxid)
{
UniValue result(UniValue::VOBJ),array(UniValue::VARR); CTransaction tx,opentx; uint256 txid,tmp_txid,hashBlock,param3,opentxid,hashchain,prevtxid;
UniValue result(UniValue::VOBJ),array(UniValue::VARR); CTransaction tx,opentx; uint256 txid,tmp_txid,hashBlock,param3,opentxid,hashchain,prevtxid,tokenid;
struct CCcontract_info *cp,C; char CCaddr[65],addr[65],str[512]; int32_t vout,numvouts,param1,numpayments;
int64_t nValue,param2,payment; CPubKey srcpub,destpub,mypk;
int64_t param2,payment; CPubKey srcpub,destpub,mypk;
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex; std::vector<uint256> txids;
cp = CCinit(&C,EVAL_CHANNELS);
mypk = pubkey2pk(Mypubkey());
if (GetTransaction(channeltxid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 &&
(DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,opentxid,srcpub,destpub,param1,param2,param3) == 'O'))
(DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3) == 'O'))
{
GetCCaddress(cp,CCaddr,mypk);
Getscriptaddress(addr,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG);
@ -746,13 +810,22 @@ UniValue ChannelsInfo(uint256 channeltxid)
result.push_back(Pair("Channel CC address",CCaddr));
result.push_back(Pair("Destination address",addr));
result.push_back(Pair("Number of payments",param1));
result.push_back(Pair("Denomination",i64tostr(param2)+" satoshi"));
result.push_back(Pair("Amount",i64tostr(param1*param2)+" satoshi"));
if(tokenid!=zeroid)
{
result.push_back(Pair("Token id",tokenid.GetHex().data()));
result.push_back(Pair("Denomination (token satoshi)",i64tostr(param2)));
result.push_back(Pair("Amount (token satoshi)",i64tostr(param1*param2)));
}
else
{
result.push_back(Pair("Denomination (satoshi)",i64tostr(param2)));
result.push_back(Pair("Amount (satoshi)",i64tostr(param1*param2)));
}
SetCCtxids(addressIndex,CCaddr);
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
{
if (GetTransaction(it->first.txhash,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 )
if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tmp_txid,srcpub,destpub,param1,param2,param3)!=0 && (tmp_txid==channeltxid || tx.GetHash()==channeltxid))
if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3)!=0 && (tmp_txid==channeltxid || tx.GetHash()==channeltxid))
txids.push_back(it->first.txhash);
}
BOOST_FOREACH(const CTxMemPoolEntry &e, mempool.mapTx)
@ -760,7 +833,7 @@ UniValue ChannelsInfo(uint256 channeltxid)
const CTransaction &txmempool = e.GetTx();
const uint256 &hash = txmempool.GetHash();
if ((numvouts=txmempool.vout.size()) > 0 && DecodeChannelsOpRet(txmempool.vout[numvouts-1].scriptPubKey,tmp_txid,srcpub,destpub,param1,param2,param3) == 'P' && tmp_txid==channeltxid)
if ((numvouts=txmempool.vout.size()) > 0 && DecodeChannelsOpRet(txmempool.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3) == 'P' && tmp_txid==channeltxid)
txids.push_back(hash);
}
prevtxid=zeroid;
@ -770,14 +843,14 @@ UniValue ChannelsInfo(uint256 channeltxid)
if (txid!=prevtxid && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 )
{
UniValue obj(UniValue::VOBJ);
if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tmp_txid,srcpub,destpub,param1,param2,param3) == 'O' && tx.GetHash()==channeltxid)
if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3) == 'O' && tx.GetHash()==channeltxid)
{
obj.push_back(Pair("Open",txid.GetHex().data()));
}
else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,opentxid,srcpub,destpub,param1,param2,param3) == 'P' && opentxid==channeltxid)
else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3) == 'P' && opentxid==channeltxid)
{
if (GetTransaction(opentxid,opentx,hashBlock,false) != 0 && (numvouts=opentx.vout.size()) > 0 &&
DecodeChannelsOpRet(opentx.vout[numvouts-1].scriptPubKey,tmp_txid,srcpub,destpub,numpayments,payment,hashchain) == 'O')
DecodeChannelsOpRet(opentx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,numpayments,payment,hashchain) == 'O')
{
Getscriptaddress(str,tx.vout[3].scriptPubKey);
obj.push_back(Pair("Payment",txid.GetHex().data()));
@ -788,11 +861,11 @@ UniValue ChannelsInfo(uint256 channeltxid)
obj.push_back(Pair("Payments left",param1));
}
}
else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,opentxid,srcpub,destpub,param1,param2,param3) == 'C' && opentxid==channeltxid)
else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3) == 'C' && opentxid==channeltxid)
{
obj.push_back(Pair("Close",txid.GetHex().data()));
}
else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,opentxid,srcpub,destpub,param1,param2,param3) == 'R' && opentxid==channeltxid)
else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3) == 'R' && opentxid==channeltxid)
{
Getscriptaddress(str,tx.vout[2].scriptPubKey);
obj.push_back(Pair("Refund",txid.GetHex().data()));

20
src/cc/eval.cpp

@ -27,6 +27,8 @@
#include "core_io.h"
#include "crosschain.h"
bool CClib_Dispatch(const CC *cond,Eval *eval,std::vector<uint8_t> paramsNull,const CTransaction &txTo,unsigned int nIn);
char *CClib_name();
Eval* EVAL_TEST = 0;
struct CCcontract_info CCinfos[0x100];
@ -38,8 +40,9 @@ bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn)
pthread_mutex_lock(&KOMODO_CC_mutex);
bool out = eval->Dispatch(cond, tx, nIn);
pthread_mutex_unlock(&KOMODO_CC_mutex);
//fprintf(stderr,"out %d vs %d isValid\n",(int32_t)out,(int32_t)eval->state.IsValid());
assert(eval->state.IsValid() == out);
if ( eval->state.IsValid() != out)
fprintf(stderr,"out %d vs %d isValid\n",(int32_t)out,(int32_t)eval->state.IsValid());
//assert(eval->state.IsValid() == out);
if (eval->state.IsValid()) return true;
@ -64,13 +67,24 @@ bool Eval::Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn)
return Invalid("empty-eval");
uint8_t ecode = cond->code[0];
if ( ASSETCHAINS_CCDISABLES[ecode] != 0 )
{
fprintf(stderr,"%s evalcode.%d %02x\n",txTo.GetHash().GetHex().c_str(),ecode,ecode);
return Invalid("disabled-code, -ac_ccenables didnt include this ecode");
}
std::vector<uint8_t> vparams(cond->code+1, cond->code+cond->codeLength);
if ( ecode >= EVAL_FIRSTUSER && ecode <= EVAL_LASTUSER )
{
if ( ASSETCHAINS_CCLIB.size() > 0 && ASSETCHAINS_CCLIB == CClib_name() )
return CClib_Dispatch(cond,this,vparams,txTo,nIn);
else return Invalid("mismatched -ac_cclib vs CClib_name");
}
cp = &CCinfos[(int32_t)ecode];
if ( cp->didinit == 0 )
{
CCinit(cp,ecode);
cp->didinit = 1;
}
std::vector<uint8_t> vparams(cond->code+1, cond->code+cond->codeLength);
switch ( ecode )
{
case EVAL_IMPORTPAYOUT:

7
src/cc/eval.h

@ -55,9 +55,14 @@
EVAL(EVAL_PEGS, 0xee) \
EVAL(EVAL_MARMARA, 0xef) \
EVAL(EVAL_PAYMENTS, 0xf0) \
EVAL(EVAL_GATEWAYS, 0xf1)
EVAL(EVAL_GATEWAYS, 0xf1) \
EVAL(EVAL_TOKENS, 0xf2)
// evalcodes 0x10 to 0x7f are reserved for cclib dynamic CC
#define EVAL_FIRSTUSER 0x10
#define EVAL_LASTUSER 0x7f
typedef uint8_t EvalCode;

2
src/cc/fsm.cpp

@ -75,7 +75,7 @@ bool FSMExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &t
bool FSMValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
{
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; bool retval;
return(false); // reject any FSM CC for now
return eval->Invalid("no validation yet");
numvins = tx.vin.size();
numvouts = tx.vout.size();
preventCCvins = preventCCvouts = -1;

153
src/cc/gateways.cpp

@ -132,14 +132,18 @@
Implementation Issues:
When thinking about validation, it is clear that we cant use EVAL_ASSETS for the locked coins as there wont be any enforcement of the gateways locking. This means we need a way to transfer assets into gateways outputs and back. It seems a tokenconvert rpc will be needed and hopefully that will be enough to make it all work properly.
-- When thinking about validation, it is clear that we cant use EVAL_ASSETS for the locked coins as there wont be any enforcement of the gateways locking.
-- This means we need a way to transfer assets into gateways outputs and back. It seems a tokenconvert rpc will be needed and hopefully that will be enough to make it all work properly.
++ The use of tokenconvert has been changed to the use of the new Tokens contract which can enforce other contracts validation by forwarding eval->validate call to GatewaysValidate
++ So all tokens remain within that Tokens contract eval code.
Care must be taken so that tokens are not lost and can be converted back.
This changes the usage to require tokenconvert before doing the bind and also tokenconvert before doing a withdraw. EVAL_GATEWAYS has evalcode of 241
The gatewaysclaim automatically converts the deposit amount of tokens back to EVAL_ASSETS.
-- Care must be taken so that tokens are not lost and can be converted back.
-- This changes the usage to require tokenconvert before doing the bind and also tokenconvert before doing a withdraw. EVAL_GATEWAYS has evalcode of 241
++ tokenconvert now returns 'not implemented', no need to use it at all.
-- The gatewaysclaim automatically converts the deposit amount of tokens back to EVAL_ASSETS.
++ The gatewaysclaim automatically transfers the deposit amount of tokens to depositor's address (within EVAL_TOKENS).
*/
@ -204,38 +208,42 @@ uint8_t DecodeGatewaysDepositOpRet(const CScript &scriptPubKey,std::string &coin
return(0);
}
CScript EncodeGatewaysClaimOpRet(uint8_t funcid,uint256 assetid,std::string refcoin,uint256 bindtxid,uint256 deposittxid,CPubKey destpub,int64_t amount)
// encodes payload for the token opret (needs to be added to the tail of it)
CScript EncodeGatewaysClaimOpRet(uint8_t funcid, std::string refcoin, uint256 bindtxid, uint256 deposittxid, CPubKey destpub, int64_t amount)
{
CScript opret; uint8_t evalcode = EVAL_ASSETS;
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << assetid << refcoin << bindtxid << deposittxid << destpub << amount);
CScript opret;
opret << OP_RETURN << E_MARSHAL(ss << funcid << refcoin << bindtxid << deposittxid << destpub << amount);
return(opret);
}
uint8_t DecodeGatewaysClaimOpRet(const CScript &scriptPubKey,uint256 &assetid,std::string &refcoin,uint256 &bindtxid,uint256 &deposittxid,CPubKey &destpub,int64_t &amount)
uint8_t DecodeGatewaysClaimOpRet(const CScript &scriptPubKey,uint256 &tokenid,std::string &refcoin,uint256 &bindtxid,uint256 &deposittxid,CPubKey &destpub,int64_t &amount)
{
std::vector<uint8_t> vopret; uint8_t *script,e,f;
GetOpReturnData(scriptPubKey, vopret);
script = (uint8_t *)vopret.data();
if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> assetid; ss >> refcoin; ss >> bindtxid; ss >> deposittxid; ss >> destpub; ss >> amount) != 0 )
if ( vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> tokenid; ss >> refcoin; ss >> bindtxid; ss >> deposittxid; ss >> destpub; ss >> amount) != 0 )
{
return(f);
}
return(0);
}
CScript EncodeGatewaysWithdrawOpRet(uint8_t funcid,uint256 assetid, std::string refcoin, CPubKey withdrawpub, int64_t amount)
CScript EncodeGatewaysWithdrawOpRet(uint8_t funcid, std::string refcoin, CPubKey withdrawpub, int64_t amount)
{
CScript opret; uint8_t evalcode = EVAL_GATEWAYS;
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << assetid << refcoin << withdrawpub << amount);
CScript opret;
opret << OP_RETURN << E_MARSHAL(ss << funcid << refcoin << withdrawpub << amount);
return(opret);
}
uint8_t DecodeGatewaysWithdrawOpRet(const CScript &scriptPubKey, uint256 &assetid, std::string &refcoin, CPubKey &withdrawpub, int64_t &amount)
uint8_t DecodeGatewaysWithdrawOpRet(const CScript &scriptPubKey, uint256 &tokenid, std::string &refcoin, CPubKey &withdrawpub, int64_t &amount)
{
std::vector<uint8_t> vopret; uint8_t *script,e,f;
GetOpReturnData(scriptPubKey, vopret);
script = (uint8_t *)vopret.data();
if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> assetid; ss >> refcoin; ss >> withdrawpub; ss >> amount) != 0 )
if ( vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> tokenid; ss >> refcoin; ss >> withdrawpub; ss >> amount) != 0 )
{
return(f);
}
@ -304,7 +312,7 @@ uint8_t DecodeGatewaysOpRet(const CScript &scriptPubKey)
std::vector<uint8_t> vopret; uint8_t *script,e,f;
GetOpReturnData(scriptPubKey, vopret);
script = (uint8_t *)vopret.data();
if ( vopret.size() > 2 && (script[0] == EVAL_GATEWAYS || script[0] == EVAL_ASSETS) && E_UNMARSHAL(vopret,ss >> e; ss >> f) != 0 )
if ( vopret.size() > 2 && (script[0] == EVAL_GATEWAYS || script[0] == EVAL_TOKENS) && E_UNMARSHAL(vopret,ss >> e; ss >> f) != 0 )
{
if (f == 'B' && f == 'D' && f == 't' && f == 'W' && f == 'P' && f == 'M')
return(f);
@ -512,7 +520,7 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks,height,claimvout; bool retval; uint8_t funcid,hash[32],M,N,taddr,prefix,prefix2;
char str[65],destaddr[64],depositaddr[65],validationError[512];
std::vector<uint256> txids; std::vector<CPubKey> pubkeys,publishers,tmppublishers; std::vector<uint8_t> proof; int64_t totalsupply,amount,tmpamount;
uint256 hashblock,txid,bindtxid,deposittxid,assetid,oracletxid,tokenid,cointxid,tmptxid,tmpxtxid2,merkleroot,mhash; CTransaction bindtx,deposittx,oracletx;
uint256 hashblock,txid,bindtxid,deposittxid,tokenidClaim,oracletxid,tokenidBind,cointxid,tmptxid,tmpxtxid2,merkleroot,mhash; CTransaction bindtx,deposittx,oracletx;
std::string refcoin,tmprefcoin,deposithex; CPubKey destpub,tmpdestpub;
fprintf(stderr,"return true without gateways validation\n");
@ -565,8 +573,8 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &
//vin.2: CC input of marker from gatewaysdeposit tx
//vout.0: CC vout of total tokens from deposit amount to asset eval code
//(vout.1): CC vout if there is change of unused tokens back to owner of tokens (deposit amount less than available tokens)
//vout.n-1: opreturn - 't' assetid zeroid 0 mypubkey (NOTE: opreturn is with asset eval code)
if ((numvouts=tx.vout.size()) > 0 && DecodeGatewaysClaimOpRet(tx.vout[numvouts-1].scriptPubKey,assetid,refcoin,bindtxid,deposittxid,destpub,amount)==0)
//vout.n-1: opreturn - 't' tokenid zeroid 0 mypubkey (NOTE: opreturn is with asset eval code)
if ((numvouts=tx.vout.size()) > 0 && DecodeGatewaysClaimOpRet(tx.vout[numvouts-1].scriptPubKey,tokenidClaim,refcoin,bindtxid,deposittxid,destpub,amount)==0)
return eval->Invalid("invalid gatewaysclaim OP_RETURN data!");
else if ( IsCCInput(tx.vin[0].scriptSig) != 0 )
return eval->Invalid("vin.0 is normal for gatewaysClaim!");
@ -580,12 +588,12 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &
return eval->Invalid("vout.1 is CC for gatewaysClaim!");
else if (myGetTransaction(bindtxid,bindtx,hashblock) == 0)
return eval->Invalid("invalid gatewaysbind txid!");
else if ((numvouts=bindtx.vout.size()) > 0 && DecodeGatewaysBindOpRet(depositaddr,bindtx.vout[numvouts-1].scriptPubKey,tmprefcoin,tokenid,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2) != 'B')
else if ((numvouts=bindtx.vout.size()) > 0 && DecodeGatewaysBindOpRet(depositaddr,bindtx.vout[numvouts-1].scriptPubKey,tmprefcoin,tokenidBind,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2) != 'B')
return eval->Invalid("invalid gatewaysbind OP_RETURN data!");
else if (tmprefcoin!=refcoin)
return eval->Invalid("refcoin different in bind tx");
else if (tokenid!=assetid)
return eval->Invalid("assetid does not match tokenid from gatewaysbind");
else if (tokenidClaim!=tokenidBind)
return eval->Invalid("tokenid does not match tokenid from gatewaysbind");
else if (komodo_txnotarizedconfirmed(bindtxid) == false)
return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!");
else if (myGetTransaction(deposittxid,deposittx,hashblock) == 0)
@ -642,7 +650,7 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &
//vout.1: normal vout txfee marker to withdraw destination pubkey
//vout.2: CC vout txfee marker to gateways CC address
//vout.n-2: CC vout if there is change of unused tokens back to owner of tokens (withdraw amount less than owner available tokens)
//vout.n-1: opreturn - 'W' assetid refcoin withdrawpub amount
//vout.n-1: opreturn - 'W' tokenid refcoin withdrawpub amount
break;
case 'P':
//vin.0: normal input
@ -675,9 +683,9 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &
// helper functions for rpc calls in rpcwallet.cpp
int64_t AddGatewaysInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 refassetid,int64_t total,int32_t maxinputs)
int64_t AddGatewaysInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 reftokenid,int64_t total,int32_t maxinputs)
{
char coinaddr[64],destaddr[64]; int64_t threshold,nValue,price,totalinputs = 0; uint256 assetid,txid,hashBlock; std::vector<uint8_t> origpubkey; std::vector<uint8_t> vopret; CTransaction vintx; int32_t j,vout,n = 0; uint8_t evalcode,funcid;
char coinaddr[64],destaddr[64]; int64_t threshold,nValue,price,totalinputs = 0; uint256 tokenid,txid,hashBlock; std::vector<uint8_t> origpubkey; std::vector<uint8_t> vopret; CTransaction vintx; int32_t j,vout,n = 0; uint8_t evalcode,funcid;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
GetCCaddress(cp,coinaddr,pk);
SetCCunspents(unspentOutputs,coinaddr);
@ -701,11 +709,11 @@ int64_t AddGatewaysInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP
if ( strcmp(destaddr,coinaddr) != 0 && strcmp(destaddr,cp->unspendableCCaddr) != 0 && strcmp(destaddr,cp->unspendableaddr2) != 0 )
continue;
GetOpReturnData(vintx.vout[vintx.vout.size()-1].scriptPubKey, vopret);
if ( E_UNMARSHAL(vopret,ss >> evalcode; ss >> funcid; ss >> assetid) != 0 )
if ( E_UNMARSHAL(vopret,ss >> evalcode; ss >> funcid; ss >> tokenid) != 0 )
{
assetid = revuint256(assetid);
char str[65],str2[65]; fprintf(stderr,"vout.%d %d:%d (%c) check for refassetid.%s vs %s %.8f\n",vout,evalcode,cp->evalcode,funcid,uint256_str(str,refassetid),uint256_str(str2,assetid),(double)vintx.vout[vout].nValue/COIN);
if ( assetid == refassetid && funcid == 't' && (nValue= vintx.vout[vout].nValue) > 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
tokenid = revuint256(tokenid);
char str[65],str2[65]; fprintf(stderr,"vout.%d %d:%d (%c) check for reftokenid.%s vs %s %.8f\n",vout,evalcode,cp->evalcode,funcid,uint256_str(str,reftokenid),uint256_str(str2,tokenid),(double)vintx.vout[vout].nValue/COIN);
if ( tokenid == reftokenid && funcid == 't' && (nValue= vintx.vout[vout].nValue) > 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
{
//fprintf(stderr,"total %llu maxinputs.%d %.8f\n",(long long)total,maxinputs,(double)it->second.satoshis/COIN);
if ( total != 0 && maxinputs != 0 )
@ -905,7 +913,7 @@ std::string GatewaysClaim(uint64_t txfee,uint256 bindtxid,std::string refcoin,ui
CTransaction tx; CPubKey mypk,gatewayspk,tmpdestpub; struct CCcontract_info *cp,C; uint8_t M,N,taddr,prefix,prefix2;
std::string coin, deposithex; std::vector<CPubKey> msigpubkeys,publishers; int64_t totalsupply,depositamount,tmpamount,inputs,CCchange=0;
int32_t numvouts,claimvout,height; std::vector<uint8_t> proof;
uint256 hashBlock,assetid,oracletxid,tmptxid,cointxid; char str[65],depositaddr[64],coinaddr[64],destaddr[64]; std::vector<uint256> txids;
uint256 hashBlock,tokenid,oracletxid,tmptxid,cointxid; char str[65],depositaddr[64],coinaddr[64],destaddr[64]; std::vector<uint256> txids;
cp = CCinit(&C,EVAL_GATEWAYS);
if ( txfee == 0 )
@ -917,7 +925,7 @@ std::string GatewaysClaim(uint64_t txfee,uint256 bindtxid,std::string refcoin,ui
fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid));
return("");
}
if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,assetid,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2) != 'B' || coin != refcoin )
if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,tokenid,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2) != 'B' || coin != refcoin )
{
fprintf(stderr,"invalid coin - bindtxid %s coin.%s\n",uint256_str(str,bindtxid),coin.c_str());
return("");
@ -945,17 +953,24 @@ std::string GatewaysClaim(uint64_t txfee,uint256 bindtxid,std::string refcoin,ui
//fprintf(stderr,"depositaddr.(%s) vs %s\n",depositaddr,cp->unspendableaddr2);
if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 )
{
if ( (inputs= AddGatewaysInputs(cp,mtx,gatewayspk,assetid,amount,60)) > 0 )
///////// if ( (inputs= AddGatewaysInputs(cp,mtx,gatewayspk,tokenid,amount,60)) > 0 )
if ((inputs = AddTokenCCInputs(cp, mtx, gatewayspk, tokenid, amount, 60)) > 0)
{
if ( inputs > amount )
CCchange = (inputs - amount);
_GetCCaddress(destaddr,EVAL_GATEWAYS,mypk);
//printf("expecting deposittxid/v0 to be to %s\n",destaddr);
mtx.vin.push_back(CTxIn(deposittxid,0,CScript())); // triggers EVAL_GATEWAYS validation
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,amount,mypk)); // transfer back to normal token
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,amount,mypk)); // transfer back to normal token
if ( CCchange != 0 )
mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,CCchange,gatewayspk));
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysClaimOpRet('t',assetid,refcoin,bindtxid,deposittxid,destpub,amount)));
mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,CCchange,gatewayspk));
std::vector<CPubKey> voutTokenPubkeys;
voutTokenPubkeys.push_back(mypk); // the pubkey where tokens are going (vout[0])
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,
EncodeTokenOpRet('t', EVAL_GATEWAYS, tokenid, voutTokenPubkeys,
EncodeGatewaysClaimOpRet('t', refcoin, bindtxid, deposittxid, destpub, amount)))); // yes, 't' is passed twice
}
}
CCerror = strprintf("cant find enough inputs or mismatched total");
@ -966,39 +981,67 @@ std::string GatewaysClaim(uint64_t txfee,uint256 bindtxid,std::string refcoin,ui
std::string GatewaysWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CTransaction tx; CPubKey mypk,gatewayspk; struct CCcontract_info *cp,C;
uint256 assetid,hashBlock,oracletxid; int32_t numvouts; int64_t totalsupply,inputs,CCchange=0; uint8_t M,N,taddr,prefix,prefix2; std::string coin;
CTransaction tx;
CPubKey mypk, gatewayspk;
uint256 tokenid,hashBlock,oracletxid; int32_t numvouts; int64_t totalsupply,inputs,CCchange=0; uint8_t M,N,taddr,prefix,prefix2; std::string coin;
std::vector<CPubKey> msigpubkeys; char depositaddr[64],str[65],coinaddr[64];
cp = CCinit(&C,EVAL_GATEWAYS);
struct CCcontract_info *cpGateways, gatewaysC;
struct CCcontract_info *cpTokens, tokensC;
cpGateways = CCinit(&gatewaysC, EVAL_GATEWAYS);
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
if ( txfee == 0 )
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
gatewayspk = GetUnspendable(cp,0);
if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 )
gatewayspk = GetUnspendable(cpGateways, 0);
if( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 )
{
fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid));
return("");
}
if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,assetid,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2) != 'B' || coin != refcoin )
if( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,tokenid,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2) != 'B' || coin != refcoin )
{
fprintf(stderr,"invalid bindtxid %s coin.%s\n",uint256_str(str,bindtxid),coin.c_str());
return("");
}
if ( AddNormalinputs(mtx,mypk,3*txfee,4) > 0 )
if( AddNormalinputs(mtx, mypk, 3*txfee, 4) > 0 )
{
if ( (inputs= AddGatewaysInputs(cp,mtx,mypk,assetid,amount,60)) > 0 )
/////if ( (inputs= AddGatewaysInputs(cp,mtx,mypk,tokenid,amount,60)) > 0 )
if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, tokenid, amount, 60)) > 0)
{
if ( inputs > amount )
CCchange = (inputs - amount);
mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,amount,gatewayspk));
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, amount, gatewayspk));
mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(withdrawpub)) << OP_CHECKSIG));
mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,txfee,gatewayspk));
mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS, txfee, gatewayspk));
if ( CCchange != 0 )
mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,CCchange,mypk));
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysWithdrawOpRet('W',assetid,refcoin,withdrawpub,amount)));
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk));
char unspendableGatewaysAddr[64];
GetCCaddress(cpGateways, unspendableGatewaysAddr, gatewayspk);
uint8_t unspendableGatewaysPrivkey[32];
GetUnspendable(cpGateways, unspendableGatewaysPrivkey);
// add additional unspendable addr from Gateways:
CCaddr2set(cpTokens, EVAL_GATEWAYS, gatewayspk, unspendableGatewaysPrivkey, unspendableGatewaysAddr);
std::vector<CPubKey> voutTokenPubkeys;
voutTokenPubkeys.push_back(gatewayspk); // the pubkey where tokens are going vout[0] (and for checking the 'change' the Tokens contract will find pubkeys itself)
return(FinalizeCCTx(0, cpTokens, mtx, mypk, txfee,
EncodeTokenOpRet('t', EVAL_GATEWAYS, tokenid, voutTokenPubkeys,
EncodeGatewaysWithdrawOpRet('W', refcoin, withdrawpub, amount))));
}
CCerror = strprintf("cant find enough token inputs or mismatched total");
fprintf(stderr, "%s\n", CCerror.c_str());
return("");
}
CCerror = strprintf("cant find enough inputs or mismatched total");
CCerror = strprintf("cant find enough normal inputs or mismatched total");
fprintf(stderr,"%s\n", CCerror.c_str() );
return("");
}
@ -1073,7 +1116,7 @@ std::string GatewaysMarkDone(uint64_t txfee,uint256 completetxid,std::string ref
UniValue GatewaysPendingWithdraws(uint256 bindtxid,std::string refcoin)
{
UniValue result(UniValue::VOBJ),pending(UniValue::VARR); CTransaction tx; std::string tmprefcoin; CPubKey mypk,gatewayspk,withdrawpub; std::vector<CPubKey> msigpubkeys;
uint256 hashBlock,assetid,txid,oracletxid; uint8_t M,N,taddr,prefix,prefix2;
uint256 hashBlock,tokenid,txid,oracletxid; uint8_t M,N,taddr,prefix,prefix2;
char depositaddr[64],coinaddr[64],destaddr[64],str[65],withaddr[64],numstr[32],txidaddr[64],cctxidaddr[64],signeraddr[64];
int32_t i,n,numvouts,vout,queueflag; int64_t totalsupply,amount,nValue; struct CCcontract_info *cp,C;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
@ -1087,7 +1130,7 @@ UniValue GatewaysPendingWithdraws(uint256 bindtxid,std::string refcoin)
fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid));
return(result);
}
if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmprefcoin,assetid,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2) != 'B' || tmprefcoin != refcoin )
if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmprefcoin,tokenid,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2) != 'B' || tmprefcoin != refcoin )
{
fprintf(stderr,"invalid bindtxid %s coin.%s\n",uint256_str(str,bindtxid),tmprefcoin.c_str());
return(result);
@ -1108,7 +1151,7 @@ UniValue GatewaysPendingWithdraws(uint256 bindtxid,std::string refcoin)
nValue = (int64_t)it->second.satoshis;
fprintf(stderr,"%s %d %ld\n",txid.ToString().c_str(),vout,(long)nValue);
if ( vout == 2 && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size())>0 &&
DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,assetid,tmprefcoin,withdrawpub,amount) == 'W' && myIsutxo_spentinmempool(txid,vout) == 0)
DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmprefcoin,withdrawpub,amount) == 'W' && myIsutxo_spentinmempool(txid,vout) == 0)
{
Getscriptaddress(destaddr,tx.vout[0].scriptPubKey);
Getscriptaddress(withaddr,tx.vout[1].scriptPubKey);
@ -1141,7 +1184,7 @@ UniValue GatewaysPendingWithdraws(uint256 bindtxid,std::string refcoin)
UniValue GatewaysProcessedWithdraws(uint256 bindtxid,std::string refcoin)
{
UniValue result(UniValue::VOBJ),processed(UniValue::VARR); CTransaction tx; std::string tmprefcoin,hex; CPubKey mypk,gatewayspk,withdrawpub; std::vector<CPubKey> msigpubkeys;
uint256 withdrawtxid,hashBlock,txid,assetid,oracletxid; uint8_t M,N,taddr,prefix,prefix2;
uint256 withdrawtxid,hashBlock,txid,tokenid,oracletxid; uint8_t M,N,taddr,prefix,prefix2;
char depositaddr[64],coinaddr[64],str[65],numstr[32],txidaddr[64],cctxidaddr[64],withaddr[64];
int32_t i,n,numvouts,vout,queueflag; int64_t totalsupply,nValue,amount; struct CCcontract_info *cp,C;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
@ -1155,7 +1198,7 @@ UniValue GatewaysProcessedWithdraws(uint256 bindtxid,std::string refcoin)
fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid));
return(result);
}
if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmprefcoin,assetid,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2) != 'B' || tmprefcoin != refcoin )
if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmprefcoin,tokenid,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2) != 'B' || tmprefcoin != refcoin )
{
fprintf(stderr,"invalid bindtxid %s coin.%s\n",uint256_str(str,bindtxid),tmprefcoin.c_str());
return(result);
@ -1178,7 +1221,7 @@ UniValue GatewaysProcessedWithdraws(uint256 bindtxid,std::string refcoin)
if ( vout == 0 && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size())>0 &&
DecodeGatewaysCompleteSigningOpRet(tx.vout[numvouts-1].scriptPubKey,tmprefcoin,withdrawtxid,hex) == 'C' && myIsutxo_spentinmempool(txid,vout) == 0)
{
if (GetTransaction(withdrawtxid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size())>0 && DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,assetid,tmprefcoin,withdrawpub,amount) == 'W')
if (GetTransaction(withdrawtxid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size())>0 && DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmprefcoin,withdrawpub,amount) == 'W')
{
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("txid",uint256_str(str,txid)));

1382
src/cc/heir.cpp

File diff suppressed because it is too large

667
src/cc/heir_validate.h

@ -0,0 +1,667 @@
#ifndef HEIR_VALIDATE_H
#define HEIR_VALIDATE_H
#include "CCinclude.h"
#include "CCHeir.h"
#define IS_CHARINSTR(c, str) (std::string(str).find((char)(c)) != std::string::npos)
// makes coin initial tx opret
CScript EncodeHeirCreateOpRet(uint8_t funcid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName);
CScript EncodeHeirOpRet(uint8_t funcid, uint256 fundingtxid, uint8_t isHeirSpendingBegan);
uint256 FindLatestFundingTx(uint256 fundingtxid, uint256 &tokenid, CScript& opRetScript, uint8_t &isHeirSpendingBegan);
//uint8_t DecodeHeirOpRet(CScript scriptPubKey, uint256& fundingtxid, uint8_t &isHeirSpendingBegan, bool noLogging = false);
//uint8_t DecodeHeirOpRet(CScript scriptPubKey, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging = false);
uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging = false);
uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, uint256 &fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging = false);
inline static bool isMyFuncId(uint8_t funcid) { return IS_CHARINSTR(funcid, "FAC"); }
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<CPubKey> 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<CPubKey> dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) {
return EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan);
}
static CScript makeClaimOpRet(uint256 dummyid, std::vector<CPubKey> 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<CPubKey> 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<CPubKey> voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) {
return EncodeTokenOpRet(tokenid, voutTokenPubkeys,
EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan));
}
static CScript makeClaimOpRet(uint256 tokenid, std::vector<CPubKey> 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)
*/
/**
* base class for all validators
*/
class CValidatorBase
{
public:
CValidatorBase(CCcontract_info* cp) : m_cp(cp) {}
virtual bool isVinValidator() const = 0;
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> 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;
};
/**
* Base class for classes which identify vins as normal or cc inputs
*/
class CInputIdentifierBase
{
public:
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;
};
/**
* 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 <typename TValidatorBase>
class CInputValidationPlan
{
using ValidatorsRow = std::vector<TValidatorBase*>;
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 <typename TValidatorBaseX, typename... ARGS>
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 = "<empty>";
//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], pTx->vin[iv].prevout.n, refMessage);
if (!result) {
return result;
}
}
return true; // validation OK
}
private:
//std::map<CInputIdentifierBase*, ValidatorsRow> m_arrayValidators;
std::vector< std::pair<CInputIdentifierBase*, ValidatorsRow> > 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
*/
template <typename TValidatorBase>
class COutputValidationPlan
{
using ValidatorsRow = std::vector<TValidatorBase*>;
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 <typename TValidatorBaseX, typename... ARGS>
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 = "<empty>";
//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], iv, refMessage);
if (!result)
return result;
}
}
return true; // validation OK
}
private:
//std::map<int32_t, ValidatorsRow> m_mapValidators;
std::vector< std::pair<int32_t, ValidatorsRow> > 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);
}
};
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);
}
};
/**
* Validates 1of2address for vout (may be used for either this or prev tx)
*/
template <class Helper> 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, 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<CTxOut> prevVout, int32_t prevN, std::string& message) const { return false; }
private:
CScript m_fundingOpretScript;
std::string m_customMessage;
};
/**
* Validates if this is vout to owner or heir from opret (funding or change)
*/
template <class Helper> 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, 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<CTxOut> prevVout, int32_t prevN, std::string& message) const { return true; }
private:
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)
*/
template <class Helper> 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, 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<CTxOut> prevVout, int32_t prevN, std::string& message) const { return true; }
private:
CScript m_fundingOpretScript;
uint256 m_latesttxid;
uint8_t m_isHeirSpendingBegan;
};
/**
* Validates this opreturn and compares it with the opreturn from the previous tx
*/
template <class Helper> class COpRetValidator : CValidatorBase
{
public:
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<CTxOut> prevVout, int32_t prevN, std::string& message) const { return true; }
private:
CScript m_fundingOpretScript;
};
/**
* marker spending prevention validator,
* returns false if for tx with funcid=F vout.1 is being tried to spend
*/
template <class Helper> 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<CTxOut> prevVout, int32_t prevN, std::string& message) const {
uint256 fundingTxidInOpret = zeroid, dummyTxid, tokenid = zeroid, initialTokenid = zeroid;
uint8_t dummyIsHeirSpendingBegan;
//std::cerr << "CMarkerValidator::validateVin() prevVout.size()=" << prevVout.size() << " prevN=" << prevN << std::endl;
if (prevVout.size() > 0) {
// get funcId for prev tx:
uint8_t funcId = DecodeHeirEitherOpRet(prevVout[prevVout.size()-1].scriptPubKey, tokenid, fundingTxidInOpret, dummyIsHeirSpendingBegan, true);
//std::cerr << "CMarkerValidator::validateVin() funcId=" << (funcId?funcId:' ') << std::endl;
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 << "CMarkerValidator::validateVin() exits with true" << std::endl;
return true;
}
};
/**
* empty validator always returns true
*/
template <class Helper> class CNullValidator : CValidatorBase
{
public:
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<CTxOut> prevVout, int32_t prevN, std::string& message) const { return true; }
};
#endif

2
src/cc/lotto.cpp

@ -115,7 +115,7 @@ bool LottoExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction
bool LottoValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
{
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; bool retval;
return(false); // reject any lotto CC for now
return eval->Invalid("no validation yet");
numvins = tx.vin.size();
numvouts = tx.vout.size();
preventCCvins = preventCCvouts = -1;

1
src/cc/makecclib

@ -0,0 +1 @@
gcc -std=c++11 -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o ../cclib.so cclib.cpp

1079
src/cc/marmara.cpp

File diff suppressed because it is too large

2
src/cc/payments.cpp

@ -75,7 +75,7 @@ bool PaymentsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransacti
bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
{
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64];
return(false);
return eval->Invalid("no validation yet");
std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
numvins = tx.vin.size();
numvouts = tx.vout.size();

2
src/cc/pegs.cpp

@ -82,7 +82,7 @@ bool PegsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &
bool PegsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
{
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64];
return(false);
return eval->Invalid("no validation yet");
std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
numvins = tx.vin.size();
numvouts = tx.vout.size();

8
src/cc/prices.cpp

@ -89,7 +89,7 @@ uint8_t DecodePricesFundingOpRet(CScript scriptPubKey,CPubKey &planpk,uint256 &o
bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
{
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64];
return(false);
return eval->Invalid("no validation yet");
std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
numvins = tx.vin.size();
numvouts = tx.vout.size();
@ -326,7 +326,11 @@ std::string PricesAddFunding(uint64_t txfee,uint256 refbettoken,uint256 fundingt
CCchange = (inputs - amount);
mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,mypk));
// add addr2
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeAssetOpRet('t',bettoken,zeroid,0,Mypubkey())));
std::vector<CPubKey> voutTokenPubkeysEmpty; //TODO: add token vout pubkeys
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,
EncodeTokenOpRet(bettoken, voutTokenPubkeysEmpty,
EncodeAssetOpRet('t',/*bettoken,*/zeroid, 0, Mypubkey()))));
}
else
{

20
src/chainparams.cpp

@ -92,13 +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;
extern int8_t is_STAKED(const char *chain_name);
const arith_uint256 maxUint = UintToArith256(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
class CMainParams : public CChainParams {
@ -108,7 +101,7 @@ public:
strNetworkID = "main";
strCurrencyUnits = "KMD";
bip44CoinType = 141; // As registered in https://github.com/satoshilabs/slips/blob/master/slip-0044.md (ZCASH, should be VRSC)
bip44CoinType = 141; // As registered in https://github.com/satoshilabs/slips/blob/master/slip-0044.md
consensus.fCoinbaseMustBeProtected = false; // true this is only true wuth Verus and enforced after block 12800
consensus.nSubsidySlowStartInterval = 20000;
consensus.nSubsidyHalvingInterval = 840000;
@ -269,8 +262,7 @@ void *chainparams_commandline(void *ptr)
mainParams.pchMessageStart[2] = (ASSETCHAINS_MAGIC >> 16) & 0xff;
mainParams.pchMessageStart[3] = (ASSETCHAINS_MAGIC >> 24) & 0xff;
fprintf(stderr,">>>>>>>>>> %s: p2p.%u rpc.%u magic.%08x %u %u coins\n",ASSETCHAINS_SYMBOL,ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT,ASSETCHAINS_MAGIC,ASSETCHAINS_MAGIC,(uint32_t)ASSETCHAINS_SUPPLY);
if (ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH)
if (ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH)
{
// this is only good for 60 second blocks with an averaging window of 45. for other parameters, use:
// nLwmaAjustedWeight = (N+1)/2 * (0.9989^(500/nPowAveragingWindow)) * nPowTargetSpacing
@ -278,6 +270,14 @@ void *chainparams_commandline(void *ptr)
mainParams.consensus.nPowAveragingWindow = 45;
mainParams.consensus.powAlternate = uint256S("00000f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f");
}
else if (ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASHV2)
{
// this is only good for 60 second blocks with an averaging window of 45. for other parameters, use:
// nLwmaAjustedWeight = (N+1)/2 * (0.9989^(500/nPowAveragingWindow)) * nPowTargetSpacing
mainParams.consensus.nLwmaAjustedWeight = 1350;
mainParams.consensus.nPowAveragingWindow = 45;
mainParams.consensus.powAlternate = uint256S("0000000f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f");
}
if (ASSETCHAINS_LWMAPOS != 0)
{

8
src/init.cpp

@ -1177,7 +1177,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
globalVerifyHandle.reset(new ECCVerifyHandle());
// set the hash algorithm to use for this chain
extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH;
// Again likely better solution here, than using long IF ELSE.
extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH, ASSETCHAINS_VERUSHASHV2;
CVerusHash::init();
CVerusHashV2::init();
if (ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH)
@ -1185,6 +1186,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// initialize VerusHash
CBlockHeader::SetVerusHash();
}
else if (ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASHV2)
{
// initialize VerusHashV2
CBlockHeader::SetVerusHashV2();
}
// Sanity check
if (!InitSanityCheck())

18
src/komodo.h

@ -806,6 +806,11 @@ void komodo_connectblock(CBlockIndex *pindex,CBlock& block)
uint64_t signedmask,voutmask; char symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; struct komodo_state *sp;
uint8_t scriptbuf[10001],pubkeys[64][33],rmd160[20],scriptPubKey[35]; uint256 zero,btctxid,txhash;
int32_t i,j,k,numnotaries,notarized,scriptlen,isratification,nid,numvalid,specialtx,notarizedheight,notaryid,len,numvouts,numvins,height,txn_count;
if ( pindex == 0 )
{
fprintf(stderr,"komodo_connectblock null pindex\n");
return;
}
memset(&zero,0,sizeof(zero));
komodo_init(pindex->GetHeight());
KOMODO_INITDONE = (uint32_t)time(NULL);
@ -975,12 +980,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);
}
}
}
}

497
src/komodo_bitcoind.h

@ -28,6 +28,10 @@ int32_t komodo_electednotary(int32_t *numnotariesp,uint8_t *pubkey33,int32_t hei
unsigned int lwmaGetNextPOSRequired(const CBlockIndex* pindexLast, const Consensus::Params& params);
bool EnsureWalletIsAvailable(bool avoidException);
extern bool fRequestShutdown;
int32_t MarmaraSignature(uint8_t *utxosig,CMutableTransaction &txNew);
uint8_t DecodeMaramaraCoinbaseOpRet(const CScript scriptPubKey,CPubKey &pk,int32_t &height,int32_t &unlockht);
//#define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"curl",(char *)"http://127.0.0.1:7776",0,0,(char *)(cmdstr))
struct MemoryStruct { char *memory; size_t size; };
@ -557,9 +561,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 +574,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 +620,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,23 +640,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;
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
}
}
}
}
}
@ -1151,7 +1175,12 @@ uint64_t komodo_commission(const CBlock *pblock,int32_t height)
if ( ASSETCHAINS_FOUNDERS > 1 )
{
if ( (height % ASSETCHAINS_FOUNDERS) == 0 )
commission = commission * ASSETCHAINS_FOUNDERS;
{
if ( ASSETCHAINS_FOUNDERS_REWARD == 0 )
commission = commission * ASSETCHAINS_FOUNDERS;
else
commission = ASSETCHAINS_FOUNDERS_REWARD;
}
else commission = 0;
}
}
@ -1162,15 +1191,22 @@ uint64_t komodo_commission(const CBlock *pblock,int32_t height)
n = pblock->vtx[i].vout.size();
for (j=0; j<n; j++)
{
//fprintf(stderr,"(%d %.8f).%d ",i,dstr(block.vtx[i].vout[j].nValue),j);
//fprintf(stderr,"(%d %.8f).%d ",i,dstr(pblock->vtx[i].vout[j].nValue),j);
if ( i != 0 || j != 1 )
total += pblock->vtx[i].vout[j].nValue;
if ( total > 1000000 * COIN )
{
total = 1000000 * COIN;
break;
}
}
}
commission = ((total * ASSETCHAINS_COMMISSION) / COIN);
commission = ((total / 10000) * ASSETCHAINS_COMMISSION) / 10000;
//commission = ((total * ASSETCHAINS_COMMISSION) / COIN);
}
if ( commission < 10000 )
commission = 0;
//fprintf(stderr,"-> %.8f\n",(double)commission/COIN);
return(commission);
}
@ -1183,7 +1219,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 )
@ -1195,7 +1231,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());
@ -1246,100 +1282,18 @@ uint32_t komodo_stakehash(uint256 *hashp,char *address,uint8_t *hashbuf,uint256
return(addrhash.uints[0]);
}
uint32_t komodo_stake(int32_t validateflag,arith_uint256 bnTarget,int32_t nHeight,uint256 txid,int32_t vout,uint32_t blocktime,uint32_t prevtime,char *destaddr)
{
bool fNegative,fOverflow; uint8_t hashbuf[256]; char address[64]; bits256 addrhash; arith_uint256 hashval,mindiff,ratio,coinage256; uint256 hash,pasthash; int32_t diff=0,segid,minage,i,iter=0; uint32_t txtime,segid32,winner = 0 ; uint64_t value,coinage;
txtime = komodo_txtime2(&value,txid,vout,address);
if ( validateflag == 0 )
{
//fprintf(stderr,"blocktime.%u -> ",blocktime);
if ( blocktime < prevtime+3 )
blocktime = prevtime+3;
if ( blocktime < GetAdjustedTime()-60 )
blocktime = GetAdjustedTime()+30;
//fprintf(stderr,"blocktime.%u txtime.%u\n",blocktime,txtime);
}
if ( value == 0 || txtime == 0 || blocktime == 0 || prevtime == 0 )
{
//fprintf(stderr,"komodo_stake null %.8f %u %u %u\n",dstr(value),txtime,blocktime,prevtime);
return(0);
}
if ( value < SATOSHIDEN )
return(0);
value /= SATOSHIDEN;
mindiff.SetCompact(KOMODO_MINDIFF_NBITS,&fNegative,&fOverflow);
ratio = (mindiff / bnTarget);
if ( (minage= nHeight*3) > 6000 ) // about 100 blocks
minage = 6000;
komodo_segids(hashbuf,nHeight-101,100);
segid32 = komodo_stakehash(&hash,address,hashbuf,txid,vout);
segid = ((nHeight + segid32) & 0x3f);
for (iter=0; iter<600; iter++)
{
if ( blocktime+iter+segid*2 < txtime+minage )
continue;
diff = (iter + blocktime - txtime - minage);
if ( diff < 0 )
diff = 60;
else if ( diff > 3600*24*30 )
{
//printf("diff.%d (iter.%d blocktime.%u txtime.%u minage.%d)\n",(int32_t)diff,iter,blocktime,txtime,(int32_t)minage);
diff = 3600*24*30;
}
if ( iter > 0 )
diff += segid*2;
coinage = (value * diff);
if ( blocktime+iter+segid*2 > prevtime+480 )
coinage *= ((blocktime+iter+segid*2) - (prevtime+400));
coinage256 = arith_uint256(coinage+1);
hashval = ratio * (UintToArith256(hash) / coinage256);
if ( hashval <= bnTarget )
{
winner = 1;
if ( validateflag == 0 )
{
//fprintf(stderr,"winner blocktime.%u iter.%d segid.%d\n",blocktime,iter,segid);
blocktime += iter;
blocktime += segid * 2;
}
break;
}
if ( validateflag != 0 )
{
/*for (i=31; i>=24; i--)
fprintf(stderr,"%02x",((uint8_t *)&hashval)[i]);
fprintf(stderr," vs ");
for (i=31; i>=24; i--)
fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]);
fprintf(stderr," segid.%d iter.%d winner.%d coinage.%llu %d ht.%d t.%u v%d diff.%d\n",segid,iter,winner,(long long)coinage,(int32_t)(blocktime - txtime),nHeight,blocktime,(int32_t)value,(int32_t)diff);*/
break;
}
}
//fprintf(stderr,"iterated until i.%d winner.%d\n",i,winner);
if ( 0 && validateflag != 0 )
{
for (i=31; i>=24; i--)
fprintf(stderr,"%02x",((uint8_t *)&hashval)[i]);
fprintf(stderr," vs ");
for (i=31; i>=24; i--)
fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]);
fprintf(stderr," segid.%d iter.%d winner.%d coinage.%llu %d ht.%d t.%u v%d diff.%d ht.%d\n",segid,iter,winner,(long long)coinage,(int32_t)(blocktime - txtime),nHeight,blocktime,(int32_t)value,(int32_t)diff,nHeight);
}
if ( nHeight < 10 )
return(blocktime);
return(blocktime * winner);
}
arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc)
{
int32_t oldflag = 0,dispflag = 0;
CBlockIndex *pindex; arith_uint256 easydiff,bnTarget,hashval,sum,ave; bool fNegative,fOverflow; int32_t i,n,m,ht,percPoS,diff,val;
*percPoSp = percPoS = 0;
if ( height <= 10 || (ASSETCHAINS_STAKED == 100 && height <= 100) )
if ( height <= 10 || (ASSETCHAINS_STAKED == 100 && height < 100) )
return(target);
sum = arith_uint256(0);
ave = sum;
easydiff.SetCompact(KOMODO_MINDIFF_NBITS,&fNegative,&fOverflow);
easydiff.SetCompact(STAKING_MIN_DIFF,&fNegative,&fOverflow);
for (i=n=m=0; i<100; i++)
{
ht = height - 100 + i;
@ -1366,7 +1320,17 @@ arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t he
fprintf(stderr," %d, ",percPoS);
}
if ( m+n < 100 )
percPoS = ((percPoS * n) + (goalperc * (100-n))) / 100;
{
// We do actual PoS % at the start. Requires coin distribution in first 10 blocks!
if ( ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH || ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASHV2 )
percPoS = (percPoS*100) / (m+n);
else
// This seems to be inverse. The actual PoS % is backwards in the first 100 blocks.
// I dont't understand the math here, or why its backwards, so I am just disabling it for VerusHash.
// No doubt this is probably wrong for equihash aswell, we may need to test an equihash chain with the rule above.
// Need to ask james what the deal is here! Seems to be causeing ALL the problems.
percPoS = ((percPoS * n) + (goalperc * (100-n))) / 100;
}
if ( dispflag != 0 && ASSETCHAINS_STAKED < 100 )
fprintf(stderr," -> %d%% percPoS vs goalperc.%d ht.%d\n",percPoS,goalperc,height);
*percPoSp = percPoS;
@ -1425,25 +1389,139 @@ arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t he
fprintf(stderr," ht.%d percPoS.%d vs goal.%d -> diff %d\n",height,percPoS,goalperc,goalperc - percPoS);
}
}
else bnTarget = ave; // recent ave is perfect
else
bnTarget = ave; // recent ave is perfect
return(bnTarget);
}
uint32_t komodo_stake(int32_t validateflag,arith_uint256 bnTarget,int32_t nHeight,uint256 txid,int32_t vout,uint32_t blocktime,uint32_t prevtime,char *destaddr,int32_t PoSperc)
{
bool fNegative,fOverflow; uint8_t hashbuf[256]; char address[64]; bits256 addrhash; arith_uint256 hashval,mindiff,ratio,coinage256; uint256 hash,pasthash; int32_t segid,minage,i,iter=0; int64_t diff=0; uint32_t txtime,segid32,winner = 0 ; uint64_t value,coinage;
txtime = komodo_txtime2(&value,txid,vout,address);
if ( validateflag == 0 )
{
//fprintf(stderr,"blocktime.%u -> ",blocktime);
if ( blocktime < prevtime+3 )
blocktime = prevtime+3;
if ( blocktime < GetAdjustedTime()-60 )
blocktime = GetAdjustedTime()+30;
//fprintf(stderr,"blocktime.%u txtime.%u\n",blocktime,txtime);
}
if ( value == 0 || txtime == 0 || blocktime == 0 || prevtime == 0 )
{
//fprintf(stderr,"komodo_stake null %.8f %u %u %u\n",dstr(value),txtime,blocktime,prevtime);
return(0);
}
if ( value < SATOSHIDEN )
return(0);
value /= SATOSHIDEN;
mindiff.SetCompact(STAKING_MIN_DIFF,&fNegative,&fOverflow);
ratio = (mindiff / bnTarget);
if ( (minage= nHeight*3) > 6000 ) // about 100 blocks
minage = 6000;
komodo_segids(hashbuf,nHeight-101,100);
segid32 = komodo_stakehash(&hash,address,hashbuf,txid,vout);
segid = ((nHeight + segid32) & 0x3f);
for (iter=0; iter<600; iter++)
{
if ( blocktime+iter+segid*2 < txtime+minage )
continue;
diff = (iter + blocktime - txtime - minage);
if ( ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH || ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASHV2 )
{
if ( PoSperc < ASSETCHAINS_STAKED )
{
// Under PoS % target and we need to increase diff.
//fprintf(stderr, "PoS too low diff.%i changed to.",diff);
diff = diff * ( (ASSETCHAINS_STAKED - PoSperc + 1) * (ASSETCHAINS_STAKED - PoSperc + 1) );
//fprintf(stderr, "%i \n",diff);
}
else if ( PoSperc > ASSETCHAINS_STAKED )
{
// Over PoS target need to lower diff.
//fprintf(stderr, "PoS too high diff.%i changed to.",diff);
diff = diff / ( (PoSperc - ASSETCHAINS_STAKED + 1) * (PoSperc - ASSETCHAINS_STAKED + 1) );
//fprintf(stderr, "%i \n",diff);
}
}
if ( diff < 0 )
diff = 60;
else if ( diff > 3600*24*30 )
{
//printf("diff.%d (iter.%d blocktime.%u txtime.%u minage.%d)\n",(int32_t)diff,iter,blocktime,txtime,(int32_t)minage);
diff = 3600*24*30;
}
if ( iter > 0 )
diff += segid*2;
coinage = (value * diff);
if ( ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH || ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASHV2 )
{
if ( blocktime+iter+segid*2 > prevtime+128 )
coinage *= ((blocktime+iter+segid*2) - (prevtime+102));
}
else
{
if ( blocktime+iter+segid*2 > prevtime+480 )
coinage *= ((blocktime+iter+segid*2) - (prevtime+400));
}
coinage256 = arith_uint256(coinage+1);
hashval = ratio * (UintToArith256(hash) / coinage256);
if ( hashval <= bnTarget )
{
winner = 1;
if ( validateflag == 0 )
{
//fprintf(stderr,"winner blocktime.%u iter.%d segid.%d\n",blocktime,iter,segid);
blocktime += iter;
blocktime += segid * 2;
}
break;
}
if ( validateflag != 0 )
{
/*for (i=31; i>=24; i--)
fprintf(stderr,"%02x",((uint8_t *)&hashval)[i]);
fprintf(stderr," vs ");
for (i=31; i>=24; i--)
fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]);
fprintf(stderr," segid.%d iter.%d winner.%d coinage.%llu %d ht.%d t.%u v%d diff.%d\n",segid,iter,winner,(long long)coinage,(int32_t)(blocktime - txtime),nHeight,blocktime,(int32_t)value,(int32_t)diff); */
break;
}
}
//fprintf(stderr,"iterated until i.%d winner.%d\n",i,winner);
if ( 0 && validateflag != 0 )
{
for (i=31; i>=24; i--)
fprintf(stderr,"%02x",((uint8_t *)&hashval)[i]);
fprintf(stderr," vs ");
for (i=31; i>=24; i--)
fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]);
fprintf(stderr," segid.%d iter.%d winner.%d coinage.%llu %d ht.%d t.%u v%d diff.%d ht.%d\n",segid,iter,winner,(long long)coinage,(int32_t)(blocktime - txtime),nHeight,blocktime,(int32_t)value,(int32_t)diff,nHeight);
}
if ( nHeight < 10 )
return(blocktime);
return(blocktime * winner);
}
int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_uint256 bnTarget,arith_uint256 bhash)
{
CBlockIndex *previndex,*pindex; char voutaddr[64],destaddr[64]; uint256 txid; uint32_t txtime,prevtime=0; int32_t vout,PoSperc,txn_count,eligible=0,isPoS = 0,segid; uint64_t value; CTxDestination voutaddress;
if ( ASSETCHAINS_STAKED == 100 && height <= 10 )
CBlockIndex *previndex,*pindex; char voutaddr[64],destaddr[64]; uint256 txid; uint32_t txtime,prevtime=0; int32_t vout,PoSperc,txn_count,eligible=0,isPoS = 0,segid; uint64_t value; CTxDestination voutaddress; arith_uint256 POWTarget;
if ( ASSETCHAINS_STAKED == 100 && height <= 100 )
return(1);
BlockMap::const_iterator it = mapBlockIndex.find(pblock->GetHash());
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);
}
// Get PoSperc and POW Target. for later.
POWTarget = komodo_PoWtarget(&PoSperc,bnTarget,height,ASSETCHAINS_STAKED);
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 )
@ -1453,15 +1531,15 @@ 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 *)"");
eligible = komodo_stake(1,bnTarget,height,txid,vout,pblock->nTime,prevtime+27,(char *)"",PoSperc);
}
if ( eligible == 0 || eligible > pblock->nTime )
{
if ( 0 && ASSETCHAINS_STAKED < 100 )
fprintf(stderr,"komodo_is_PoSblock PoS failure ht.%d eligible.%u vs blocktime.%u, lag.%d -> check to see if it is PoW block\n",height,eligible,(uint32_t)pblock->nTime,(int32_t)(eligible - pblock->nTime));
if ( slowflag != 0 && pindex != 0 && height > 100)
if ( slowflag != 0 && pindex != 0 )
{
pindex->segid = -1;
//fprintf(stderr,"PoW block detected set segid.%d <- %d\n",height,pindex->segid);
@ -1470,7 +1548,7 @@ int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_
else
{
isPoS = 2; // 2 means staking utxo validated
if ( slowflag != 0 && height > 100 )
if ( slowflag != 0 )
{
CTxDestination voutaddress; char voutaddr[64];
if ( ExtractDestination(pblock->vtx[txn_count-1].vout[0].scriptPubKey,voutaddress) )
@ -1488,7 +1566,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 )
@ -1500,8 +1578,7 @@ int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_
}
else
{
bnTarget = komodo_PoWtarget(&PoSperc,bnTarget,height,ASSETCHAINS_STAKED);
if ( bhash < bnTarget )
if ( bhash < POWTarget )
{
//fprintf(stderr,"ht.%d isPoS but meets PoW diff!\n",height);
isPoS = 0;
@ -1696,14 +1773,22 @@ bool verusCheckPOSBlock(int32_t slowflag, CBlock *pblock, int32_t height)
int64_t komodo_checkcommission(CBlock *pblock,int32_t height)
{
int64_t checktoshis=0; uint8_t *script,scripthex[8192]; int32_t scriptlen,matched = 0;
if ( ASSETCHAINS_COMMISSION != 0 )
if ( ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD != 0 )
{
checktoshis = komodo_commission(pblock,height);
//fprintf(stderr,"height.%d commission %.8f\n",height,(double)checktoshis/COIN);
if ( checktoshis != 0 )
if ( checktoshis >= 10000 && pblock->vtx[0].vout.size() < 2 )
{
//fprintf(stderr,"komodo_checkcommission vsize.%d height.%d commission %.8f\n",(int32_t)pblock->vtx[0].vout.size(),height,(double)checktoshis/COIN);
return(-1);
}
else if ( checktoshis != 0 )
{
script = (uint8_t *)&pblock->vtx[0].vout[1].scriptPubKey[0];
scriptlen = (int32_t)pblock->vtx[0].vout[1].scriptPubKey.size();
//int32_t i;
//for (i=0; i<scriptlen; i++)
// fprintf(stderr,"%02x",script[i]);
//fprintf(stderr," vout[1] %.8f vs %.8f\n",(double)checktoshis/COIN,(double)pblock->vtx[0].vout[1].nValue/COIN);
if ( ASSETCHAINS_SCRIPTPUB.size() > 1 )
{
if ( ASSETCHAINS_SCRIPTPUB.size()/2 == scriptlen && scriptlen < sizeof(scripthex) )
@ -1719,10 +1804,7 @@ int64_t komodo_checkcommission(CBlock *pblock,int32_t height)
matched = 25;
if ( matched == 0 )
{
//int32_t i;
//for (i=0; i<25; i++)
// fprintf(stderr,"%02x",script[i]);
//fprintf(stderr," payment to wrong pubkey scriptlen.%d, scriptpub[%d]\n",scriptlen,(int32_t)ASSETCHAINS_SCRIPTPUB.size()/2);
fprintf(stderr," payment to wrong pubkey scriptlen.%d, scriptpub[%d] checktoshis.%llu\n",scriptlen,(int32_t)ASSETCHAINS_SCRIPTPUB.size()/2,(long long)checktoshis);
return(-1);
}
@ -1740,7 +1822,7 @@ bool KOMODO_TEST_ASSETCHAIN_SKIP_POW = 0;
int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height)
{
uint256 hash; arith_uint256 bnTarget,bhash; bool fNegative,fOverflow; uint8_t *script,pubkey33[33],pubkeys[64][33]; int32_t i,possible,PoSperc,is_PoSblock=0,n,failed = 0,notaryid = -1; int64_t checktoshis,value; CBlockIndex *pprev;
uint256 hash; arith_uint256 bnTarget,bhash; bool fNegative,fOverflow; uint8_t *script,pubkey33[33],pubkeys[64][33]; int32_t i,scriptlen,possible,PoSperc,is_PoSblock=0,n,failed = 0,notaryid = -1; int64_t checktoshis,value; CBlockIndex *pprev;
if ( KOMODO_TEST_ASSETCHAIN_SKIP_POW == 0 && Params().NetworkIDString() == "regtest" )
KOMODO_TEST_ASSETCHAIN_SKIP_POW = 1;
if ( !CheckEquihashSolution(pblock, Params()) )
@ -1796,6 +1878,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 )
@ -1819,16 +1902,15 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height)
return(-1);
} else
{
// I think this means the block is valid PoW. We need to set the pindex->segid here.
failed = 0;
CBlockIndex *pindex;
BlockMap::const_iterator it = mapBlockIndex.find(pblock->GetHash());
pindex = it != mapBlockIndex.end() ? it->second : NULL;
if ( pindex != 0 && height > 100 && pindex->segid == -2 ) {
if ( pindex != 0 && pindex->segid == -2 ) {
pindex->segid = -1;
//fprintf(stderr,"PoW block detected set segid.%d <- %d\n",height,pindex->segid);
}
}
}
}
}
else if ( is_PoSblock < 0 )
@ -1858,7 +1940,8 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height)
else
{
script = (uint8_t *)&pblock->vtx[0].vout[0].scriptPubKey[0];
if ( script[0] != 33 || script[34] != OP_CHECKSIG || memcmp(script+1,ASSETCHAINS_OVERRIDE_PUBKEY33,33) != 0 )
scriptlen = (int32_t)pblock->vtx[0].vout[0].scriptPubKey.size();
if ( scriptlen != 35 || script[0] != 33 || script[34] != OP_CHECKSIG || memcmp(script+1,ASSETCHAINS_OVERRIDE_PUBKEY33,33) != 0 )
return(-1);
}
}
@ -2011,64 +2094,15 @@ struct komodo_staking *komodo_addutxo(struct komodo_staking *array,int32_t *numk
return(array);
}
arith_uint256 _komodo_eligible(struct komodo_staking *kp,arith_uint256 ratio,uint32_t blocktime,int32_t iter,int32_t minage,int32_t segid,int32_t nHeight,uint32_t prevtime)
{
int32_t diff; uint64_t coinage; arith_uint256 coinage256,hashval;
diff = (iter + blocktime - kp->txtime - minage);
if ( diff < 0 )
diff = 60;
else if ( diff > 3600*24*30 )
diff = 3600*24*30;
if ( iter > 0 )
diff += segid*2;
coinage = ((uint64_t)kp->nValue * diff);
if ( blocktime+iter+segid*2 > prevtime+480 )
coinage *= ((blocktime+iter+segid*2) - (prevtime+400));
coinage256 = arith_uint256(coinage+1);
hashval = ratio * (kp->hashval / coinage256);
return(hashval);
}
uint32_t komodo_eligible(arith_uint256 bnTarget,arith_uint256 ratio,struct komodo_staking *kp,int32_t nHeight,uint32_t blocktime,uint32_t prevtime,int32_t minage,uint8_t *hashbuf)
{
int32_t maxiters = 600; uint256 hash;
int32_t segid,iter,diff; uint64_t coinage; arith_uint256 hashval,coinage256;
komodo_stakehash(&hash,kp->address,hashbuf,kp->txid,kp->vout);
kp->hashval = UintToArith256(hash);
segid = ((nHeight + kp->segid32) & 0x3f);
hashval = _komodo_eligible(kp,ratio,blocktime,maxiters,minage,segid,nHeight,prevtime);
//for (int i=32; i>=0; i--)
// fprintf(stderr,"%02x",((uint8_t *)&hashval)[i]);
//fprintf(stderr," b.%u minage.%d segid.%d ht.%d prev.%u\n",blocktime,minage,segid,nHeight,prevtime);
if ( hashval <= bnTarget )
{
for (iter=0; iter<maxiters; iter++)
{
if ( blocktime+iter+segid*2 < kp->txtime+minage )
continue;
hashval = _komodo_eligible(kp,ratio,blocktime,iter,minage,segid,nHeight,prevtime);
if ( hashval <= bnTarget )
{
//fprintf(stderr,"winner %.8f blocktime.%u iter.%d segid.%d\n",(double)kp->nValue/COIN,blocktime,iter,segid);
blocktime += iter;
blocktime += segid * 2;
return(blocktime);
}
}
} //else fprintf(stderr,"maxiters is not good enough\n");
return(0);
}
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;
set<CBitcoinAddress> setAddress; struct komodo_staking *kp; int32_t winners,segid,minage,nHeight,counter=0,i,m,siglen=0,nMinDepth = 1,nMaxDepth = 99999999; vector<COutput> vecOutputs; uint32_t block_from_future_rejecttime,besttime,eligible,eligible2,earliest = 0; CScript best_scriptPubKey; arith_uint256 mindiff,ratio,bnTarget; CBlockIndex *tipindex,*pindex; CTxDestination address; bool fNegative,fOverflow; uint8_t hashbuf[256]; CTransaction tx; uint256 hashBlock;
int32_t PoSperc;
set<CBitcoinAddress> setAddress; struct komodo_staking *kp; int32_t winners,segid,minage,nHeight,counter=0,i,m,siglen=0,nMinDepth = 1,nMaxDepth = 99999999; vector<COutput> vecOutputs; uint32_t block_from_future_rejecttime,besttime,eligible,earliest = 0; CScript best_scriptPubKey; arith_uint256 mindiff,ratio,bnTarget,tmpTarget; CBlockIndex *tipindex,*pindex; CTxDestination address; bool fNegative,fOverflow; uint8_t hashbuf[256]; CTransaction tx; uint256 hashBlock;
if (!EnsureWalletIsAvailable(0))
return 0;
bnTarget.SetCompact(nBits, &fNegative, &fOverflow);
mindiff.SetCompact(KOMODO_MINDIFF_NBITS,&fNegative,&fOverflow);
ratio = (mindiff / bnTarget);
assert(pwalletMain != NULL);
*utxovaluep = 0;
memset(utxotxidp,0,sizeof(*utxotxidp));
@ -2077,12 +2111,14 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt
if ( (tipindex= chainActive.Tip()) == 0 )
return(0);
nHeight = tipindex->GetHeight() + 1;
// Get the PoS% so we can pass it to komodo_stake, this is to adjust PoS dofficulty when it is under the target %!
tmpTarget = komodo_PoWtarget(&PoSperc,bnTarget,nHeight,ASSETCHAINS_STAKED);
if ( (minage= nHeight*3) > 6000 ) // about 100 blocks
minage = 6000;
komodo_segids(hashbuf,nHeight-101,100);
if ( *blocktimep < tipindex->nTime+60 )
if ( *blocktimep < tipindex->nTime+60)
*blocktimep = tipindex->nTime+60;
//fprintf(stderr,"Start scan of utxo for staking %u ht.%d\n",(uint32_t)time(NULL),nHeight);
//fprintf(stderr,"Start scan of utxo for staking %u ht.%d\n",(uint32_t)time(NULL),nHeight);
bool resetstaker = false;
if ( array != 0 )
@ -2096,7 +2132,7 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt
}
}
if ( time(NULL) > lasttime+600 || array == 0 || resetstaker )
if ( resetstaker || array == 0 || time(NULL) > lasttime+600 )
{
LOCK2(cs_main, pwalletMain->cs_wallet);
pwalletMain->AvailableCoins(vecOutputs, false, NULL, true);
@ -2107,31 +2143,60 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt
maxkp = numkp = 0;
lasttime = 0;
}
BOOST_FOREACH(const COutput& out, vecOutputs)
if ( ASSETCHAINS_MARMARA == 0 )
{
if ( (tipindex= chainActive.Tip()) == 0 || tipindex->GetHeight()+1 > nHeight )
{
fprintf(stderr,"chain tip changed during staking loop t.%u counter.%d\n",(uint32_t)time(NULL),counter);
return(0);
}
counter++;
if ( out.nDepth < nMinDepth || out.nDepth > nMaxDepth )
BOOST_FOREACH(const COutput& out, vecOutputs)
{
//fprintf(stderr,"komodo_staked invalid depth %d\n",(int32_t)out.nDepth);
continue;
if ( (tipindex= chainActive.Tip()) == 0 || tipindex->GetHeight()+1 > nHeight )
{
fprintf(stderr,"chain tip changed during staking loop t.%u counter.%d\n",(uint32_t)time(NULL),counter);
return(0);
}
counter++;
if ( out.nDepth < nMinDepth || out.nDepth > nMaxDepth )
{
//fprintf(stderr,"komodo_staked invalid depth %d\n",(int32_t)out.nDepth);
continue;
}
CAmount nValue = out.tx->vout[out.i].nValue;
if ( nValue < COIN || !out.fSpendable )
continue;
const CScript& pk = out.tx->vout[out.i].scriptPubKey;
if ( ExtractDestination(pk,address) != 0 )
{
if ( IsMine(*pwalletMain,address) == 0 )
continue;
if ( GetTransaction(out.tx->GetHash(),tx,hashBlock,true) != 0 && (pindex= komodo_getblockindex(hashBlock)) != 0 )
{
array = komodo_addutxo(array,&numkp,&maxkp,(uint32_t)pindex->nTime,(uint64_t)nValue,out.tx->GetHash(),out.i,(char *)CBitcoinAddress(address).ToString().c_str(),hashbuf,(CScript)pk);
//fprintf(stderr,"addutxo numkp.%d vs max.%d\n",numkp,maxkp);
}
}
}
CAmount nValue = out.tx->vout[out.i].nValue;
if ( nValue < COIN || !out.fSpendable )
continue;
const CScript& pk = out.tx->vout[out.i].scriptPubKey;
if ( ExtractDestination(pk,address) != 0 )
}
else
{
struct CCcontract_info *cp,C; uint256 txid; int32_t vout,ht,unlockht; CAmount nValue; char coinaddr[64]; CPubKey mypk,Marmarapk,pk;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
cp = CCinit(&C,EVAL_MARMARA);
mypk = pubkey2pk(Mypubkey());
Marmarapk = GetUnspendable(cp,0);
GetCCaddress1of2(cp,coinaddr,Marmarapk,mypk);
SetCCunspents(unspentOutputs,coinaddr);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
{
if ( IsMine(*pwalletMain,address) == 0 )
txid = it->first.txhash;
vout = (int32_t)it->first.index;
if ( (nValue= it->second.satoshis) < COIN )
continue;
if ( GetTransaction(out.tx->GetHash(),tx,hashBlock,true) != 0 && (pindex= komodo_getblockindex(hashBlock)) != 0 )
if ( GetTransaction(txid,tx,hashBlock,true) != 0 && (pindex= komodo_getblockindex(hashBlock)) != 0 && myIsutxo_spentinmempool(txid,vout) == 0 )
{
array = komodo_addutxo(array,&numkp,&maxkp,(uint32_t)pindex->nTime,(uint64_t)nValue,out.tx->GetHash(),out.i,(char *)CBitcoinAddress(address).ToString().c_str(),hashbuf,(CScript)pk);
//fprintf(stderr,"addutxo numkp.%d vs max.%d\n",numkp,maxkp);
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);
}
}
}
@ -2139,7 +2204,7 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt
//fprintf(stderr,"finished kp data of utxo for staking %u ht.%d numkp.%d maxkp.%d\n",(uint32_t)time(NULL),nHeight,numkp,maxkp);
}
//fprintf(stderr,"numkp.%d blocktime.%u\n",numkp,*blocktimep);
block_from_future_rejecttime = (uint32_t)GetAdjustedTime() + 57;
block_from_future_rejecttime = (uint32_t)GetAdjustedTime() + 57;
for (i=winners=0; i<numkp; i++)
{
if (fRequestShutdown)
@ -2150,16 +2215,14 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt
return(0);
}
kp = &array[i];
if ( (eligible2= komodo_eligible(bnTarget,ratio,kp,nHeight,*blocktimep,(uint32_t)tipindex->nTime+27,minage,hashbuf)) == 0 )
continue;
eligible = komodo_stake(0,bnTarget,nHeight,kp->txid,kp->vout,0,(uint32_t)tipindex->nTime+27,kp->address);
//fprintf(stderr,"i.%d %u vs %u\n",i,eligible2,eligible);
eligible = komodo_stake(0,bnTarget,nHeight,kp->txid,kp->vout,0,(uint32_t)tipindex->nTime+27,kp->address,PoSperc);
//fprintf(stderr,"i.%d %u vs %u\n",i,eligible2,eligible);
if ( eligible > 0 )
{
besttime = m = 0;
if ( eligible == komodo_stake(1,bnTarget,nHeight,kp->txid,kp->vout,eligible,(uint32_t)tipindex->nTime+27,kp->address) )
if ( eligible == komodo_stake(1,bnTarget,nHeight,kp->txid,kp->vout,eligible,(uint32_t)tipindex->nTime+27,kp->address,PoSperc) )
{
while ( eligible == komodo_stake(1,bnTarget,nHeight,kp->txid,kp->vout,eligible,(uint32_t)tipindex->nTime+27,kp->address) )
while ( eligible == komodo_stake(1,bnTarget,nHeight,kp->txid,kp->vout,eligible,(uint32_t)tipindex->nTime+27,kp->address,PoSperc) )
{
besttime = eligible;
eligible--;
@ -2213,16 +2276,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);
signSuccess = ProduceSignature(TransactionSignatureCreator(&keystore, &txNewConst, 0, *utxovaluep, SIGHASH_ALL), best_scriptPubKey, sigdata, consensusBranchId);
if (!signSuccess)
fprintf(stderr,"failed to create signature\n");
else
if ( ASSETCHAINS_MARMARA == 0 )
{
signSuccess = ProduceSignature(TransactionSignatureCreator(&keystore, &txNewConst, 0, *utxovaluep, SIGHASH_ALL), best_scriptPubKey, sigdata, consensusBranchId);
UpdateTransaction(txNew,0,sigdata);
ptr = (uint8_t *)&sigdata.scriptSig[0];
siglen = sigdata.scriptSig.size();
for (i=0; i<siglen; i++)
utxosig[i] = ptr[i];//, fprintf(stderr,"%02x",ptr[i]);
}
else
{
siglen = MarmaraSignature(utxosig,txNew);
if ( siglen > 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;

44
src/komodo_defs.h

@ -30,7 +30,51 @@
#define KOMODO_SAPLING_DEADLINE 1550188800 // Feb 15th, 2019
#define _COINBASE_MATURITY 100
#define SETBIT(bits,bitoffset) (((uint8_t *)bits)[(bitoffset) >> 3] |= (1 << ((bitoffset) & 7)))
#define GETBIT(bits,bitoffset) (((uint8_t *)bits)[(bitoffset) >> 3] & (1 << ((bitoffset) & 7)))
#define CLEARBIT(bits,bitoffset) (((uint8_t *)bits)[(bitoffset) >> 3] &= ~(1 << ((bitoffset) & 7)))
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, ASSETCHAINS_FOUNDERS_REWARD;
extern uint64_t ASSETCHAINS_TIMELOCKGTE;
extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH,ASSETCHAINS_EQUIHASH,KOMODO_INITDONE;
extern int32_t KOMODO_MININGTHREADS,KOMODO_LONGESTCHAIN,ASSETCHAINS_SEED,IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,KOMODO_ON_DEMAND,KOMODO_PASSPORT_INITDONE,ASSETCHAINS_STAKED;
extern uint64_t ASSETCHAINS_COMMISSION, ASSETCHAINS_LASTERA;
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_VERUSHASHV2, 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;
extern char NOTARYADDRS[64][36];
extern uint8_t NUM_NOTARIES;
#endif

12
src/komodo_gateway.h

@ -16,6 +16,8 @@
// paxdeposit equivalent in reverse makes opreturn and KMD does the same in reverse
#include "komodo_defs.h"
int32_t MarmaraValidateCoinbase(int32_t height,CTransaction tx);
int32_t pax_fiatstatus(uint64_t *available,uint64_t *deposited,uint64_t *issued,uint64_t *withdrawn,uint64_t *approved,uint64_t *redeemed,char *base)
{
int32_t baseid; struct komodo_state *sp; int64_t netliability,maxallowed,maxval;
@ -687,6 +689,14 @@ int32_t komodo_check_deposit(int32_t height,const CBlock& block,uint32_t prevtim
}
}
}
if ( height > 0 && ASSETCHAINS_MARMARA != 0 && (height & 1) == 0 )
{
if ( MarmaraValidateCoinbase(height,block.vtx[0]) < 0 )
{
fprintf(stderr,"MARMARA error ht.%d constrains even height blocks to pay 100%% to CC in vout0 with opreturn\n",height);
return(-1);
}
}
// we don't want these checks in VRSC, leave it at the Sapling upgrade
if ( ASSETCHAINS_SYMBOL[0] == 0 ||
(ASSETCHAINS_COMMISSION != 0 && height > 1) ||
@ -1378,7 +1388,7 @@ void komodo_passport_iteration()
{
static long lastpos[34]; static char userpass[33][1024]; static uint32_t lasttime,callcounter,lastinterest;
int32_t maxseconds = 10;
FILE *fp; uint8_t *filedata; long fpos,datalen,lastfpos; int32_t baseid,limit,n,ht,isrealtime,expired,refid,blocks,longest; struct komodo_state *sp,*refsp; char *retstr,fname[512],*base,symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; uint32_t buf[3],starttime; cJSON *infoobj,*result; uint64_t RTmask = 0; //CBlockIndex *pindex;
FILE *fp; uint8_t *filedata; long fpos,datalen,lastfpos; int32_t baseid,limit,n,ht,isrealtime,expired,refid,blocks,longest; struct komodo_state *sp,*refsp; char *retstr,fname[512],*base,symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; uint32_t buf[3],starttime; uint64_t RTmask = 0; //CBlockIndex *pindex;
expired = 0;
while ( KOMODO_INITDONE == 0 )
{

27
src/komodo_globals.h

@ -47,13 +47,13 @@ unsigned int WITNESS_CACHE_SIZE = _COINBASE_MATURITY+10;
int32_t KOMODO_MININGTHREADS = -1,IS_KOMODO_NOTARY,IS_STAKED_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,ASSETCHAINS_SEED,KOMODO_ON_DEMAND,KOMODO_EXTERNAL_NOTARIES,KOMODO_PASSPORT_INITDONE,KOMODO_PAX,KOMODO_EXCHANGEWALLET,KOMODO_REWIND,STAKED_ERA,KOMODO_CONNECTING = -1,KOMODO_DEALERNODE,KOMODO_EXTRASATOSHI,ASSETCHAINS_FOUNDERS;
int32_t KOMODO_INSYNC,KOMODO_LASTMINED,prevKOMODO_LASTMINED,KOMODO_CCACTIVATE,JUMBLR_PAUSE = 1;
std::string NOTARY_PUBKEY,ASSETCHAINS_NOTARIES,ASSETCHAINS_OVERRIDE_PUBKEY,DONATION_PUBKEY,ASSETCHAINS_SCRIPTPUB,NOTARY_ADDRESS,WHITELIST_ADDRESS,ASSETCHAINS_SELFIMPORT;
uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEYHASH[20],ASSETCHAINS_PUBLIC,ASSETCHAINS_PRIVATE,ASSETCHAINS_TXPOW,NUM_NOTARIES;
std::string NOTARY_PUBKEY,ASSETCHAINS_NOTARIES,ASSETCHAINS_OVERRIDE_PUBKEY,DONATION_PUBKEY,ASSETCHAINS_SCRIPTPUB,NOTARY_ADDRESS,WHITELIST_ADDRESS,ASSETCHAINS_SELFIMPORT,ASSETCHAINS_CCLIB;
uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEYHASH[20],ASSETCHAINS_PUBLIC,ASSETCHAINS_PRIVATE,ASSETCHAINS_TXPOW,NUM_NOTARIES,ASSETCHAINS_MARMARA;
bool VERUS_MINTBLOCKS;
char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN],ASSETCHAINS_USERPASS[4096],NOTARYADDRS[64][36];
uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT,ASSETCHAINS_BEAMPORT,ASSETCHAINS_CODAPORT;
uint32_t ASSETCHAIN_INIT,ASSETCHAINS_CC,KOMODO_STOPAT,KOMODO_DPOWCONFS = 1;
uint32_t ASSETCHAIN_INIT,ASSETCHAINS_CC,KOMODO_STOPAT,KOMODO_DPOWCONFS = 1,STAKING_MIN_DIFF;
uint32_t ASSETCHAINS_MAGIC = 2387029918;
int64_t ASSETCHAINS_GENESISTXVAL = 5000000000;
@ -68,19 +68,23 @@ int64_t MAX_MONEY = 200000000 * 100000000LL;
uint64_t ASSETCHAINS_TIMELOCKGTE = _ASSETCHAINS_TIMELOCKOFF;
uint64_t ASSETCHAINS_TIMEUNLOCKFROM = 0, ASSETCHAINS_TIMEUNLOCKTO = 0;
uint32_t ASSETCHAINS_LASTERA = 1;
uint64_t ASSETCHAINS_LASTERA = 1;
uint64_t ASSETCHAINS_ENDSUBSIDY[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_HALVING[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_DECAY[ASSETCHAINS_MAX_ERAS];
uint8_t ASSETCHAINS_CCDISABLES[256];
#define _ASSETCHAINS_EQUIHASH 0
uint32_t ASSETCHAINS_NUMALGOS = 2;
uint32_t ASSETCHAINS_NUMALGOS = 3;
uint32_t ASSETCHAINS_EQUIHASH = _ASSETCHAINS_EQUIHASH;
uint32_t ASSETCHAINS_VERUSHASH = 1;
const char *ASSETCHAINS_ALGORITHMS[] = {"equihash", "verushash"};
uint64_t ASSETCHAINS_NONCEMASK[] = {0xffff,0xfffffff};
uint32_t ASSETCHAINS_NONCESHIFT[] = {32,16};
uint32_t ASSETCHAINS_HASHESPERROUND[] = {1,4096};
uint32_t ASSETCHAINS_VERUSHASHV2 = 2;
const char *ASSETCHAINS_ALGORITHMS[] = {"equihash", "verushash", "verushash11"};
uint64_t ASSETCHAINS_NONCEMASK[] = {0xffff,0xfffffff,0xfffffff};
uint32_t ASSETCHAINS_NONCESHIFT[] = {32,16,16};
uint32_t ASSETCHAINS_HASHESPERROUND[] = {1,4096,4096};
uint32_t ASSETCHAINS_ALGO = _ASSETCHAINS_EQUIHASH;
// min diff returned from GetNextWorkRequired needs to be added here for each algo, so they can work with ac_staked.
uint32_t ASSETCHAINS_MINDIFF[] = {537857807,504303375,487526159};
// ^ wrong!
// Verus proof of stake controls
int32_t ASSETCHAINS_LWMAPOS = 0; // percentage of blocks should be PoS
int32_t VERUS_BLOCK_POSUNITS = 1024; // one block is 1000 units
@ -92,7 +96,8 @@ int32_t ASSETCHAINS_SAPLING = -1;
int32_t ASSETCHAINS_OVERWINTER = -1;
uint64_t KOMODO_INTERESTSUM,KOMODO_WALLETBALANCE;
uint64_t ASSETCHAINS_COMMISSION,ASSETCHAINS_STAKED,ASSETCHAINS_SUPPLY = 10,MIN_RECV_SATS;
int32_t ASSETCHAINS_STAKED;
uint64_t ASSETCHAINS_COMMISSION,ASSETCHAINS_SUPPLY = 10,MIN_RECV_SATS,ASSETCHAINS_FOUNDERS_REWARD;
uint32_t KOMODO_INITDONE;
char KMDUSERPASS[8192+512+1],BTCUSERPASS[8192]; uint16_t KMD_PORT = 7771,BITCOIND_RPCPORT = 7771;

10
src/komodo_jumblr.h

@ -629,7 +629,7 @@ uint64_t jumblr_increment(uint8_t r,int32_t height,uint64_t total,uint64_t bigge
void jumblr_iteration()
{
static int32_t lastheight; static uint32_t lasttime;
char *zaddr,*addr,*retstr,secretaddr[64]; cJSON *array; int32_t i,iter,height,acpublic,counter,chosen_one,n; uint64_t smallest,medium,biggest,amount=0,total=0; double fee; struct jumblr_item *ptr,*tmp; uint16_t r,s;
char *zaddr,*addr,*retstr=0,secretaddr[64]; cJSON *array; int32_t i,iter,height,acpublic,counter,chosen_one,n; uint64_t smallest,medium,biggest,amount=0,total=0; double fee; struct jumblr_item *ptr,*tmp; uint16_t r,s;
acpublic = ASSETCHAINS_PUBLIC;
if ( ASSETCHAINS_SYMBOL[0] == 0 && GetTime() >= KOMODO_SAPLING_DEADLINE )
acpublic = 1;
@ -648,7 +648,7 @@ void jumblr_iteration()
}
free_json(array);
}
free(retstr);
free(retstr), retstr = 0;
}
}
height = (int32_t)chainActive.LastTip()->GetHeight();
@ -691,7 +691,7 @@ void jumblr_iteration()
if ( amount > 0 && (retstr= jumblr_sendt_to_z(Jumblr_deposit,addr,dstr(amount))) != 0 )
{
printf("sendt_to_z.(%s)\n",retstr);
free(retstr);
free(retstr), retstr = 0;
}
free(zaddr);
} else printf("no zaddr from jumblr_zgetnewaddress\n");
@ -723,7 +723,7 @@ void jumblr_iteration()
if ( (retstr= jumblr_sendz_to_z(ptr->dest,addr,dstr(total))) != 0 )
{
printf("n.%d counter.%d chosen_one.%d send z_to_z.(%s)\n",n,counter,chosen_one,retstr);
free(retstr);
free(retstr), retstr = 0;
}
ptr->spent = (uint32_t)time(NULL);
free(zaddr);
@ -768,7 +768,7 @@ void jumblr_iteration()
if ( (retstr= jumblr_sendz_to_t(ptr->dest,secretaddr,dstr(total))) != 0 )
{
printf("%s send z_to_t.(%s)\n",secretaddr,retstr);
free(retstr);
free(retstr), retstr = 0;
} else printf("null return from jumblr_sendz_to_t\n");
ptr->spent = (uint32_t)time(NULL);
break;

108
src/komodo_utils.h

@ -1655,15 +1655,17 @@ 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],*extraptr=0; FILE *fp; uint64_t val; uint16_t port; int32_t i,baseid,len,n,extralen = 0;
IS_KOMODO_NOTARY = GetBoolArg("-notary", false);
IS_STAKED_NOTARY = GetArg("-stakednotary", -1);
if ( IS_STAKED_NOTARY != -1 && IS_KOMODO_NOTARY == true ) {
fprintf(stderr, "Cannot be STAKED and KMD notary at the same time!\n");
exit(0);
}
MIN_RECV_SATS = GetArg("-mintxvalue",-1);
WHITELIST_ADDRESS = GetArg("-whitelistaddress","");
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];
IS_KOMODO_NOTARY = GetBoolArg("-notary", false);
IS_STAKED_NOTARY = GetArg("-stakednotary", -1);
if ( IS_STAKED_NOTARY != -1 && IS_KOMODO_NOTARY == true ) {
fprintf(stderr, "Cannot be STAKED and KMD notary at the same time!\n");
exit(0);
}
MIN_RECV_SATS = GetArg("-mintxvalue",-1);
WHITELIST_ADDRESS = GetArg("-whitelistaddress","");
memset(ccenables,0,sizeof(ccenables));
memset(disablebits,0,sizeof(disablebits));
if ( GetBoolArg("-gen", false) != 0 )
{
KOMODO_MININGTHREADS = GetArg("-genproclimit",-1);
@ -1684,13 +1686,13 @@ void komodo_args(char *argv0)
IS_KOMODO_NOTARY = 1;
KOMODO_MININGTHREADS = 1;
mapArgs ["-genproclimit"] = itostr(KOMODO_MININGTHREADS);
IS_STAKED_NOTARY = -1;
IS_STAKED_NOTARY = -1;
fprintf(stderr,"running as notary.%d %s\n",i,Notaries_elected1[i][0]);
break;
}
}
}
name = GetArg("-ac_name","");
name = GetArg("-ac_name","");
if ( argv0 != 0 )
{
len = (int32_t)strlen(argv0);
@ -1726,6 +1728,7 @@ void komodo_args(char *argv0)
if (std::string(ASSETCHAINS_ALGORITHMS[i]) == selectedAlgo)
{
ASSETCHAINS_ALGO = i;
STAKING_MIN_DIFF = ASSETCHAINS_MINDIFF[i];
// only worth mentioning if it's not equihash
if (ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH)
printf("ASSETCHAINS_ALGO, algorithm set to %s\n", selectedAlgo.c_str());
@ -1741,9 +1744,10 @@ void komodo_args(char *argv0)
if ( ASSETCHAINS_LASTERA < 1 || ASSETCHAINS_LASTERA > ASSETCHAINS_MAX_ERAS )
{
ASSETCHAINS_LASTERA = 1;
printf("ASSETCHAINS_LASTERA, if specified, must be between 1 and %u. ASSETCHAINS_LASTERA set to %u\n", ASSETCHAINS_MAX_ERAS, ASSETCHAINS_LASTERA);
printf("ASSETCHAINS_LASTERA, if specified, must be between 1 and %u. ASSETCHAINS_LASTERA set to %lu\n", ASSETCHAINS_MAX_ERAS, ASSETCHAINS_LASTERA);
}
ASSETCHAINS_LASTERA -= 1;
printf("ASSETCHAINS_LASTERA = %lu\n", ASSETCHAINS_LASTERA);
ASSETCHAINS_TIMELOCKGTE = (uint64_t)GetArg("-ac_timelockgte", _ASSETCHAINS_TIMELOCKOFF);
ASSETCHAINS_TIMEUNLOCKFROM = GetArg("-ac_timeunlockfrom", 0);
@ -1777,12 +1781,46 @@ void komodo_args(char *argv0)
MAX_BLOCK_SIGOPS = 60000;
ASSETCHAINS_TXPOW = GetArg("-ac_txpow",0) & 3;
ASSETCHAINS_FOUNDERS = GetArg("-ac_founders",0);// & 1;
ASSETCHAINS_FOUNDERS_REWARD = GetArg("-ac_founders_reward",0);
ASSETCHAINS_SUPPLY = GetArg("-ac_supply",10);
ASSETCHAINS_COMMISSION = GetArg("-ac_perc",0);
ASSETCHAINS_OVERRIDE_PUBKEY = GetArg("-ac_pubkey","");
ASSETCHAINS_SCRIPTPUB = GetArg("-ac_script","");
ASSETCHAINS_BEAMPORT = GetArg("-ac_beam",0);
ASSETCHAINS_CODAPORT = GetArg("-ac_coda",0);
ASSETCHAINS_MARMARA = GetArg("-ac_marmara",0);
if ( ASSETCHAINS_COMMISSION != 0 && ASSETCHAINS_FOUNDERS_REWARD != 0 )
{
fprintf(stderr,"cannot use founders reward and commission on the same chain.\n");
exit(0);
}
if ( ASSETCHAINS_CC != 0 )
{
ASSETCHAINS_CCLIB = GetArg("-ac_cclib","");
Split(GetArg("-ac_ccenable",""), ccenables, 0);
for (i=nonz=0; i<0x100; i++)
{
if ( ccenables[i] != 0 )
{
nonz++;
fprintf(stderr,"%d ",(uint8_t)(ccenables[i] & 0xff));
}
}
fprintf(stderr,"nonz.%d ccenables[]\n",nonz);
if ( nonz > 0 )
{
for (i=0; i<256; i++)
{
ASSETCHAINS_CCDISABLES[i] = 1;
SETBIT(disablebits,i);
}
for (i=0; i<256; i++)
{
CLEARBIT(disablebits,(ccenables[i] & 0xff));
ASSETCHAINS_CCDISABLES[ccenables[i] & 0xff] = 0;
}
}
}
if ( ASSETCHAINS_BEAMPORT != 0 && ASSETCHAINS_CODAPORT != 0 )
{
fprintf(stderr,"can only have one of -ac_beam or -ac_coda\n");
@ -1809,7 +1847,7 @@ void komodo_args(char *argv0)
}
// else it can be gateway coin
if ( (ASSETCHAINS_STAKED= GetArg("-ac_staked",0)) > 100 )
ASSETCHAINS_STAKED = 100;
@ -1835,13 +1873,17 @@ void komodo_args(char *argv0)
decode_hex(ASSETCHAINS_OVERRIDE_PUBKEY33,33,(char *)ASSETCHAINS_OVERRIDE_PUBKEY.c_str());
calc_rmd160_sha256(ASSETCHAINS_OVERRIDE_PUBKEYHASH,ASSETCHAINS_OVERRIDE_PUBKEY33,33);
}
if ( ASSETCHAINS_COMMISSION == 0 )
if ( ASSETCHAINS_COMMISSION == 0 && ASSETCHAINS_FOUNDERS != 0 )
{
if (ASSETCHAINS_FOUNDERS != 0 )
if ( ASSETCHAINS_FOUNDERS_REWARD == 0 )
{
ASSETCHAINS_COMMISSION = 53846154; // maps to 35%
printf("ASSETCHAINS_COMMISSION defaulted to 35%% when founders reward active\n");
}
else
{
printf("ASSETCHAINS_FOUNDERS_REWARD set to %ld\n", ASSETCHAINS_FOUNDERS_REWARD);
}
/*else if ( ASSETCHAINS_SELFIMPORT.size() == 0 )
{
//ASSETCHAINS_OVERRIDE_PUBKEY.clear();
@ -1862,7 +1904,12 @@ void komodo_args(char *argv0)
printf("ASSETCHAINS_FOUNDERS needs an ASSETCHAINS_OVERRIDE_PUBKEY or ASSETCHAINS_SCRIPTPUB\n");
}
}
if ( ASSETCHAINS_ENDSUBSIDY[0] != 0 || ASSETCHAINS_REWARD[0] != 0 || ASSETCHAINS_HALVING[0] != 0 || ASSETCHAINS_DECAY[0] != 0 || ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_PUBLIC != 0 || ASSETCHAINS_PRIVATE != 0 || ASSETCHAINS_TXPOW != 0 || ASSETCHAINS_FOUNDERS != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1 || ASSETCHAINS_SELFIMPORT.size() > 0 || ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_TIMELOCKGTE != _ASSETCHAINS_TIMELOCKOFF|| ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH || ASSETCHAINS_LWMAPOS != 0 || ASSETCHAINS_LASTERA > 0 || ASSETCHAINS_BEAMPORT != 0 || ASSETCHAINS_CODAPORT != 0 )
if ( ASSETCHAINS_SCRIPTPUB.size() > 1 && ASSETCHAINS_MARMARA != 0 )
{
fprintf(stderr,"-ac_script and -ac_marmara are mutually exclusive\n");
exit(0);
}
if ( ASSETCHAINS_ENDSUBSIDY[0] != 0 || ASSETCHAINS_REWARD[0] != 0 || ASSETCHAINS_HALVING[0] != 0 || ASSETCHAINS_DECAY[0] != 0 || ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_PUBLIC != 0 || ASSETCHAINS_PRIVATE != 0 || ASSETCHAINS_TXPOW != 0 || ASSETCHAINS_FOUNDERS != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1 || ASSETCHAINS_SELFIMPORT.size() > 0 || ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_TIMELOCKGTE != _ASSETCHAINS_TIMELOCKOFF|| ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH || ASSETCHAINS_LWMAPOS != 0 || ASSETCHAINS_LASTERA > 0 || ASSETCHAINS_BEAMPORT != 0 || ASSETCHAINS_CODAPORT != 0 || ASSETCHAINS_MARMARA != 0 || nonz > 0 || ASSETCHAINS_CCLIB.size() > 0 || ASSETCHAINS_FOUNDERS_REWARD != 0 )
{
fprintf(stderr,"perc %.4f%% ac_pub=[%02x%02x%02x...] acsize.%d\n",dstr(ASSETCHAINS_COMMISSION)*100,ASSETCHAINS_OVERRIDE_PUBKEY33[0],ASSETCHAINS_OVERRIDE_PUBKEY33[1],ASSETCHAINS_OVERRIDE_PUBKEY33[2],(int32_t)ASSETCHAINS_SCRIPTPUB.size());
extraptr = extrabuf;
@ -1908,14 +1955,19 @@ void komodo_args(char *argv0)
extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_LWMAPOS),(void *)&ASSETCHAINS_LWMAPOS);
}
val = ASSETCHAINS_COMMISSION | (((uint64_t)ASSETCHAINS_STAKED & 0xff) << 32) | (((uint64_t)ASSETCHAINS_CC & 0xffff) << 40) | ((ASSETCHAINS_PUBLIC != 0) << 7) | ((ASSETCHAINS_PRIVATE != 0) << 6) | ASSETCHAINS_TXPOW;
val = ASSETCHAINS_COMMISSION | (((int64_t)ASSETCHAINS_STAKED & 0xff) << 32) | (((uint64_t)ASSETCHAINS_CC & 0xffff) << 40) | ((ASSETCHAINS_PUBLIC != 0) << 7) | ((ASSETCHAINS_PRIVATE != 0) << 6) | ASSETCHAINS_TXPOW;
extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(val),(void *)&val);
if ( ASSETCHAINS_FOUNDERS != 0 )
{
uint8_t tmp = 1;
extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(tmp),(void *)&tmp);
if ( ASSETCHAINS_FOUNDERS > 1 )
extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_FOUNDERS),(void *)&ASSETCHAINS_FOUNDERS);
if ( ASSETCHAINS_FOUNDERS_REWARD != 0 )
{
extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_FOUNDERS_REWARD),(void *)&ASSETCHAINS_FOUNDERS_REWARD);
}
}
if ( ASSETCHAINS_SCRIPTPUB.size() > 1 )
{
@ -1936,8 +1988,24 @@ void komodo_args(char *argv0)
extraptr[extralen++] = 'b';
if ( ASSETCHAINS_CODAPORT != 0 )
extraptr[extralen++] = 'c';
if ( ASSETCHAINS_MARMARA != 0 )
extraptr[extralen++] = ASSETCHAINS_MARMARA;
if ( nonz > 0 )
{
memcpy(&extraptr[extralen],disablebits,sizeof(disablebits));
extralen += sizeof(disablebits);
}
if ( ASSETCHAINS_CCLIB.size() > 1 )
{
for (i=0; i<ASSETCHAINS_CCLIB.size(); i++)
{
extraptr[extralen++] = ASSETCHAINS_CCLIB[i];
fprintf(stderr,"%c",ASSETCHAINS_CCLIB[i]);
}
fprintf(stderr," <- CCLIB name\n");
}
}
addn = GetArg("-seednode","");
if ( strlen(addn.c_str()) > 0 )
ASSETCHAINS_SEED = 1;
@ -1987,7 +2055,7 @@ void komodo_args(char *argv0)
if ( (port= komodo_userpass(ASSETCHAINS_USERPASS,ASSETCHAINS_SYMBOL)) != 0 )
ASSETCHAINS_RPCPORT = port;
else komodo_configfile(ASSETCHAINS_SYMBOL,ASSETCHAINS_P2PPORT + 1);
if (ASSETCHAINS_LASTERA == 0)
if (ASSETCHAINS_LASTERA == 0 || is_STAKED(ASSETCHAINS_SYMBOL) != 0)
COINBASE_MATURITY = 1;
//fprintf(stderr,"ASSETCHAINS_RPCPORT (%s) %u\n",ASSETCHAINS_SYMBOL,ASSETCHAINS_RPCPORT);
}
@ -2060,7 +2128,7 @@ void komodo_args(char *argv0)
if ( strcmp("PIRATE",ASSETCHAINS_SYMBOL) == 0 && ASSETCHAINS_HALVING[0] == 77777 )
{
ASSETCHAINS_HALVING[0] *= 5;
fprintf(stderr,"PIRATE halving changed to %d %.1f days ASSETCHAINS_LASTERA.%d\n",(int32_t)ASSETCHAINS_HALVING[0],(double)ASSETCHAINS_HALVING[0]/1440,ASSETCHAINS_LASTERA);
fprintf(stderr,"PIRATE halving changed to %d %.1f days ASSETCHAINS_LASTERA.%lu\n",(int32_t)ASSETCHAINS_HALVING[0],(double)ASSETCHAINS_HALVING[0]/1440,ASSETCHAINS_LASTERA);
}
else if ( strcmp("VRSC",ASSETCHAINS_SYMBOL) == 0 )
dpowconfs = 0;

16
src/main.cpp

@ -2216,8 +2216,9 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex,bool checkPOW)
extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
extern uint64_t ASSETCHAINS_ENDSUBSIDY[ASSETCHAINS_MAX_ERAS], ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS], ASSETCHAINS_HALVING[ASSETCHAINS_MAX_ERAS];
extern uint32_t ASSETCHAINS_MAGIC;
extern uint64_t ASSETCHAINS_STAKED,ASSETCHAINS_LINEAR,ASSETCHAINS_COMMISSION,ASSETCHAINS_SUPPLY;
extern uint64_t ASSETCHAINS_LINEAR,ASSETCHAINS_COMMISSION,ASSETCHAINS_SUPPLY;
extern uint8_t ASSETCHAINS_PUBLIC,ASSETCHAINS_PRIVATE;
extern int32_t ASSETCHAINS_STAKED;
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
{
@ -3473,14 +3474,15 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs-1), nTimeConnect * 0.000001);
CAmount blockReward = nFees + GetBlockSubsidy(pindex->GetHeight(), chainparams.GetConsensus()) + sum;
if ( ASSETCHAINS_COMMISSION != 0 ) //ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 &&
if ( ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD != 0 ) //ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 &&
{
uint64_t checktoshis;
if ( (checktoshis= komodo_commission((CBlock *)&block,(int32_t)pindex->GetHeight())) != 0 )
{
if ( block.vtx[0].vout.size() >= 2 && block.vtx[0].vout[1].nValue == checktoshis )
blockReward += checktoshis;
else fprintf(stderr,"checktoshis %.8f numvouts %d\n",dstr(checktoshis),(int32_t)block.vtx[0].vout.size());
else if ( pindex->GetHeight() > 1 )
fprintf(stderr,"checktoshis %.8f vs %.8f numvouts %d\n",dstr(checktoshis),dstr(block.vtx[0].vout[1].nValue),(int32_t)block.vtx[0].vout.size());
}
}
if (ASSETCHAINS_SYMBOL[0] != 0 && pindex->GetHeight() == 1 && block.vtx[0].GetValueOut() != blockReward)
@ -3514,6 +3516,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
CDiskBlockPos pos;
if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40))
return error("ConnectBlock(): FindUndoPos failed");
if ( pindex->pprev == 0 )
fprintf(stderr,"ConnectBlock: unexpected null pprev\n");
if (!UndoWriteToDisk(blockundo, pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart()))
return AbortNode(state, "Failed to write undo data");
@ -3821,7 +3825,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);
}
@ -3853,7 +3857,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)))
{
#ifdef ENABLE_WALLET
LOCK2(cs_main, pwalletMain->cs_wallet);
@ -4753,7 +4757,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

5
src/metrics.cpp

@ -39,8 +39,7 @@
#endif
#include <unistd.h>
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()
@ -137,7 +136,7 @@ int64_t GetUptime()
double GetLocalSolPS()
{
if (ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH)
if (ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH || ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASHV2)
{
return miningTimer.rate(nHashCount);
}

207
src/miner.cpp

@ -18,6 +18,7 @@
* *
******************************************************************************/
#include "pubkey.h"
#include "miner.h"
#ifdef ENABLE_MINING
#include "pow/tromp/equi_miner.h"
@ -127,21 +128,12 @@ void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams,
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams);
}
}
#include "komodo_defs.h"
#include "cc/CCinclude.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],NOTARYADDRS[64][36];
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],NUM_NOTARIES;
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);
@ -156,19 +148,26 @@ int32_t verus_staked(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &nBits
int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33);
int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex);
int32_t komodo_is_notarytx(const CTransaction& tx);
CScript Marmara_scriptPubKey(int32_t height,CPubKey pk);
CScript MarmaraCoinbaseOpret(uint8_t funcid,int32_t height,CPubKey pk);
int32_t komodo_is_notarytx(const CTransaction& tx);
CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount, bool isStake)
CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32_t gpucount, bool isStake)
{
CScript scriptPubKeyIn(_scriptPubKeyIn);
CPubKey pk = CPubKey();
std::vector<std::vector<unsigned char>> vAddrs;
txnouttype txT;
if ( scriptPubKeyIn.size() > 0 && Solver(scriptPubKeyIn, txT, vAddrs))
CPubKey pk;
if ( _pk.size() != 33 )
{
if (txT == TX_PUBKEY)
pk = CPubKey(vAddrs[0]);
}
pk = CPubKey();
std::vector<std::vector<unsigned char>> vAddrs;
txnouttype txT;
if ( scriptPubKeyIn.size() > 0 && Solver(scriptPubKeyIn, txT, vAddrs))
{
if (txT == TX_PUBKEY)
pk = CPubKey(vAddrs[0]);
}
} else pk = _pk;
uint64_t deposits; int32_t isrealtime,kmdheight; uint32_t blocktime; const CChainParams& chainparams = Params();
//fprintf(stderr,"create new block\n");
@ -503,7 +502,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
{
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)
@ -524,6 +523,19 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
//if ( blocktime > pindexPrev->GetMedianTimePast()+60 )
// blocktime = pindexPrev->GetMedianTimePast() + 60;
siglen = komodo_staked(txStaked, pblock->nBits, &blocktime, &txtime, &utxotxid, &utxovout, &utxovalue, utxosig);
// if you skip this check it will create a block too far into the future and not pass ProcessBlock or AcceptBlock.
// This has been moved from the mining loop to save CPU, and to also make ac_staked work with the verus miner.
while ( blocktime-57 > GetAdjustedTime() )
{
sleep(1);
if ( (rand() % 100) < 1 )
fprintf(stderr, "%u seconds until elegible, waiting.\n", blocktime-((uint32_t)GetAdjustedTime()+57));
if ( chainActive.LastTip()->GetHeight() >= stakeHeight )
{
fprintf(stderr, "Block Arrived, reset staking loop.\n");
return(0);
}
}
}
if ( siglen > 0 )
@ -538,9 +550,9 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
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)));
} else return(0); //fprintf(stderr,"no utxos eligible for staking\n");
} else return(0); //fprintf(stderr,"no utxos eligible for staking\n");
}
// Create coinbase tx
CMutableTransaction txNew = CreateNewContextualCMutableTransaction(consensusParams, nHeight);
txNew.vin.resize(1);
@ -556,43 +568,25 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
if ( ASSETCHAINS_SYMBOL[0] == 0 && IS_KOMODO_NOTARY != 0 && My_notaryid >= 0 )
txNew.vout[0].nValue += 5000;
pblock->vtx[0] = txNew;
// check if coinbase transactions must be time locked at current subsidy and prepend the time lock
// to transaction if so, cast for GTE operator
if ((uint64_t)(txNew.vout[0].nValue) >= ASSETCHAINS_TIMELOCKGTE)
if ( ASSETCHAINS_MARMARA != 0 && nHeight > 0 && (nHeight & 1) == 0 )
{
int32_t opretlen, p2shlen, scriptlen;
CScriptExt opretScript = CScriptExt();
char checkaddr[64];
Getscriptaddress(checkaddr,txNew.vout[0].scriptPubKey);
//`fprintf(stderr,"set mining coinbase -> %s\n",checkaddr);
txNew.vout.resize(2);
// prepend time lock to original script unless original script is P2SH, in which case, we will leave the coins
// protected only by the time lock rather than 100% inaccessible
opretScript.AddCheckLockTimeVerify(komodo_block_unlocktime(nHeight));
if (scriptPubKeyIn.IsPayToScriptHash() || scriptPubKeyIn.IsPayToCryptoCondition())
{
fprintf(stderr,"CreateNewBlock: attempt to add timelock to pay2sh or pay2cc\n");
if ( ASSETCHAINS_SYMBOL[0] == 0 || (ASSETCHAINS_SYMBOL[0] != 0 && !isStake) )
{
LEAVE_CRITICAL_SECTION(cs_main);
LEAVE_CRITICAL_SECTION(mempool.cs);
}
return 0;
}
opretScript += scriptPubKeyIn;
txNew.vout[0].scriptPubKey = CScriptExt().PayToScriptHash(CScriptID(opretScript));
txNew.vout[1].scriptPubKey = CScriptExt().OpReturnScript(opretScript, OPRETTYPE_TIMELOCK);
txNew.vout[1].nValue = 0;
} // timelocks and commissions are currently incompatible due to validation complexity of the combination
else if ( nHeight > 1 && ASSETCHAINS_SYMBOL[0] != 0 && (ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1) && ASSETCHAINS_COMMISSION != 0 && (commission= komodo_commission((CBlock*)&pblocktemplate->block,(int32_t)nHeight)) != 0 )
txNew.vout[1].scriptPubKey = MarmaraCoinbaseOpret('C',nHeight,pk);
}
else if ( nHeight > 1 && ASSETCHAINS_SYMBOL[0] != 0 && (ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1) && (ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD != 0) && (commission= komodo_commission((CBlock*)&pblocktemplate->block,(int32_t)nHeight)) != 0 )
{
int32_t i; uint8_t *ptr;
txNew.vout.resize(2);
txNew.vout[1].nValue = commission;
if ( ASSETCHAINS_SCRIPTPUB.size() > 1 )
{
//fprintf(stderr,"mine to -ac_script\n");
//txNew.vout[1].scriptPubKey = CScript() << ParseHex();
int32_t len = strlen(ASSETCHAINS_SCRIPTPUB.c_str());
len >>= 1;
@ -606,11 +600,42 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount,
ptr = (uint8_t *)&txNew.vout[1].scriptPubKey[0];
ptr[0] = 33;
for (i=0; i<33; i++)
{
ptr[i+1] = ASSETCHAINS_OVERRIDE_PUBKEY33[i];
//fprintf(stderr,"%02x",ptr[i+1]);
}
ptr[34] = OP_CHECKSIG;
//fprintf(stderr," set ASSETCHAINS_OVERRIDE_PUBKEY33 into vout[1]\n");
}
//printf("autocreate commision vout\n");
}
else if ( (uint64_t)(txNew.vout[0].nValue) >= ASSETCHAINS_TIMELOCKGTE)
{
int32_t opretlen, p2shlen, scriptlen;
CScriptExt opretScript = CScriptExt();
txNew.vout.resize(2);
// prepend time lock to original script unless original script is P2SH, in which case, we will leave the coins
// protected only by the time lock rather than 100% inaccessible
opretScript.AddCheckLockTimeVerify(komodo_block_unlocktime(nHeight));
if (scriptPubKeyIn.IsPayToScriptHash() || scriptPubKeyIn.IsPayToCryptoCondition())
{
fprintf(stderr,"CreateNewBlock: attempt to add timelock to pay2sh or pay2cc\n");
if ( ASSETCHAINS_SYMBOL[0] == 0 || (ASSETCHAINS_SYMBOL[0] != 0 && !isStake) )
{
LEAVE_CRITICAL_SECTION(cs_main);
LEAVE_CRITICAL_SECTION(mempool.cs);
}
return 0;
}
opretScript += scriptPubKeyIn;
txNew.vout[0].scriptPubKey = CScriptExt().PayToScriptHash(CScriptID(opretScript));
txNew.vout[1].scriptPubKey = CScriptExt().OpReturnScript(opretScript, OPRETTYPE_TIMELOCK);
txNew.vout[1].nValue = 0;
} // timelocks and commissions are currently incompatible due to validation complexity of the combination
pblock->vtx[0] = txNew;
pblocktemplate->vTxFees[0] = -nFees;
@ -778,7 +803,10 @@ CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight,
if ( nHeight == 1 && ASSETCHAINS_COMMISSION != 0 )
{
if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 )
scriptPubKey = CScript() << ParseHex(ASSETCHAINS_OVERRIDE_PUBKEY) << OP_CHECKSIG;
{
pubkey = ParseHex(ASSETCHAINS_OVERRIDE_PUBKEY);
scriptPubKey = CScript() << ParseHex(HexStr(pubkey)) << OP_CHECKSIG;
}
else
{
len = strlen(ASSETCHAINS_SCRIPTPUB.c_str());
@ -791,8 +819,10 @@ CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight,
else if ( USE_EXTERNAL_PUBKEY != 0 )
{
//fprintf(stderr,"use notary pubkey\n");
scriptPubKey = CScript() << ParseHex(NOTARY_PUBKEY) << OP_CHECKSIG;
} else
pubkey = ParseHex(NOTARY_PUBKEY);
scriptPubKey = CScript() << ParseHex(HexStr(pubkey)) << OP_CHECKSIG;
}
else
{
//if ( !isStake || ASSETCHAINS_STAKED != 0 )
{
@ -809,7 +839,11 @@ CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight,
scriptPubKey[34] = OP_CHECKSIG;
}
}
return CreateNewBlock(scriptPubKey, gpucount, isStake);
if ( ASSETCHAINS_MARMARA != 0 && nHeight > 0 && (nHeight & 1) == 0 )
scriptPubKey = Marmara_scriptPubKey(nHeight,pubkey);
if ( ASSETCHAINS_STAKED != 0 && KOMODO_MININGTHREADS == 0 )
isStake = true;
return CreateNewBlock(pubkey, scriptPubKey, gpucount, isStake);
}
void komodo_broadcast(CBlock *pblock,int32_t limit)
@ -1205,14 +1239,14 @@ void static BitcoinMiner_noeq()
miningTimer.start();
#ifdef ENABLE_WALLET
CBlockTemplate *ptr = CreateNewBlockWithKey(reservekey, Mining_height, 0);
CBlockTemplate *ptr = CreateNewBlockWithKey(reservekey, Mining_height, ASSETCHAINS_STAKED != 0 && KOMODO_MININGTHREADS == 0);
#else
CBlockTemplate *ptr = CreateNewBlockWithKey();
#endif
if ( ptr == 0 )
{
static uint32_t counter;
if ( counter++ < 100 )
if ( ASSETCHAINS_STAKED == 0 && counter++ < 10 )
fprintf(stderr,"created illegal block, retry\n");
continue;
}
@ -1255,6 +1289,7 @@ void static BitcoinMiner_noeq()
pblock->nSolution = solnPlaceholder;
savebits = pblock->nBits;
arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
HASHTarget = arith_uint256().SetCompact(savebits);
arith_uint256 mask(ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO]);
Mining_start = 0;
@ -1272,31 +1307,53 @@ void static BitcoinMiner_noeq()
if ( ASSETCHAINS_STAKED != 0 )
{
int32_t percPoS,z;
hashTarget = komodo_PoWtarget(&percPoS,hashTarget,Mining_height,ASSETCHAINS_STAKED);
for (z=31; z>=0; z--)
fprintf(stderr,"%02x",((uint8_t *)&hashTarget)[z]);
fprintf(stderr," PoW for staked coin PoS %d%% vs target %d%%\n",percPoS,(int32_t)ASSETCHAINS_STAKED);
int32_t percPoS,z; bool fNegative,fOverflow;
HASHTarget_POW = komodo_PoWtarget(&percPoS,HASHTarget,Mining_height,ASSETCHAINS_STAKED);
HASHTarget.SetCompact(KOMODO_MINDIFF_NBITS,&fNegative,&fOverflow);
LogPrintf("Block %d : PoS %d%% vs target %d%%\n", Mining_height, percPoS, (int32_t)ASSETCHAINS_STAKED);
}
while (true)
{
arith_uint256 arNonce = UintToArith256(pblock->nNonce);
int64_t *extraPtr;
// This seems to be a really bad way to do this, but its better than copy pasting the entire miner function at this stage.
CVerusHashWriter ss = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION);
ss << *((CBlockHeader *)pblock);
int64_t *extraPtr = ss.xI64p();
if ( ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH )
extraPtr = ss.xI64p();
CVerusHash &vh = ss.GetState();
uint256 hashResult = uint256();
vh.ClearExtra();
CVerusHashV2Writer ss2 = CVerusHashV2Writer(SER_GETHASH, PROTOCOL_VERSION);
ss2 << *((CBlockHeader *)pblock);
if ( ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASHV2 )
extraPtr = ss2.xI64p();
CVerusHashV2 &vh2 = ss2.GetState();
vh2.ClearExtra();
int64_t i, count = ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO] + 1;
int64_t hashesToGo = ASSETCHAINS_HASHESPERROUND[ASSETCHAINS_ALGO];
if ( ASSETCHAINS_STAKED > 0 && ASSETCHAINS_STAKED < 100 )
{
if ( KOMODO_MININGTHREADS > 0 )
hashTarget = HASHTarget_POW;
else
hashTarget = HASHTarget;
}
else if ( ASSETCHAINS_STAKED == 100 && Mining_height > 100 )
hashTarget = HASHTarget;
// for speed check NONCEMASK at a time
for (i = 0; i < count; i++)
{
*extraPtr = i;
vh.ExtraHash((unsigned char *)&hashResult);
if ( ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH )
vh.ExtraHash((unsigned char *)&hashResult);
else if ( ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASHV2 )
vh2.ExtraHash((unsigned char *)&hashResult);
if ( UintToArith256(hashResult) <= hashTarget )
{
@ -1385,8 +1442,8 @@ void static BitcoinMiner_noeq()
#else
printf("%lu mega hashes complete - working\n", (ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO] + 1) / 1048576);
#endif
pblock->nBits = savebits;
break;
}
}
}
@ -1500,15 +1557,15 @@ void static BitcoinMiner()
#ifdef ENABLE_WALLET
// notaries always default to staking
CBlockTemplate *ptr = CreateNewBlockWithKey(reservekey, pindexPrev->GetHeight()+1, gpucount, ASSETCHAINS_STAKED != 0 && GetArg("-genproclimit", -1) == 0);
CBlockTemplate *ptr = CreateNewBlockWithKey(reservekey, pindexPrev->GetHeight()+1, gpucount, ASSETCHAINS_STAKED != 0 && KOMODO_MININGTHREADS == 0);
#else
CBlockTemplate *ptr = CreateNewBlockWithKey();
#endif
if ( ptr == 0 )
{
static uint32_t counter;
if ( counter++ < 100 && ASSETCHAINS_STAKED == 0 )
fprintf(stderr,"created illegal block, retry\n");
if ( counter++ < 10 && ASSETCHAINS_STAKED == 0 )
fprintf(stderr,"created illegal blockB, retry\n");
sleep(1);
continue;
}
@ -1601,18 +1658,16 @@ void static BitcoinMiner()
} //else fprintf(stderr,"duplicate at j.%d\n",j);
} else Mining_start = 0;
} else Mining_start = 0;
if ( ASSETCHAINS_STAKED > 0 )
{
int32_t percPoS,z; bool fNegative,fOverflow;
HASHTarget_POW = komodo_PoWtarget(&percPoS,HASHTarget,Mining_height,ASSETCHAINS_STAKED);
HASHTarget.SetCompact(KOMODO_MINDIFF_NBITS,&fNegative,&fOverflow);
if ( ASSETCHAINS_STAKED < 100 )
{
//for (z=31; z>=0; z--)
// fprintf(stderr,"%02x",((uint8_t *)&HASHTarget_POW)[z]);
LogPrintf("Block %d : PoS %d%% vs target %d%% \n",Mining_height,percPoS,(int32_t)ASSETCHAINS_STAKED);
}
}
while (true)
{
/*if ( KOMODO_INSYNC == 0 )
@ -1698,12 +1753,6 @@ void static BitcoinMiner()
}
else
{
while ( B.nTime-57 > GetAdjustedTime() )
{
sleep(1);
if ( chainActive.LastTip()->GetHeight() >= Mining_height )
return(false);
}
uint256 tmp = B.GetHash();
int32_t z; for (z=31; z>=0; z--)
fprintf(stderr,"%02x",((uint8_t *)&tmp)[z]);
@ -1912,12 +1961,12 @@ void static BitcoinMiner()
for (int i = 0; i < nThreads; i++) {
#ifdef ENABLE_WALLET
if (ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH)
if ( ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH )
minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet));
else
minerThreads->create_thread(boost::bind(&BitcoinMiner_noeq, pwallet));
#else
if (ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH)
if (ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH )
minerThreads->create_thread(&BitcoinMiner);
else
minerThreads->create_thread(&BitcoinMiner_noeq);

2
src/miner.h

@ -43,7 +43,7 @@ struct CBlockTemplate
#define KOMODO_MAXGPUCOUNT 65
/** Generate a new block, without valid proof-of-work */
CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, int32_t gpucount, bool isStake = false);
CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& scriptPubKeyIn, int32_t gpucount, bool isStake = false);
#ifdef ENABLE_WALLET
boost::optional<CScript> GetMinerScriptPubKey(CReserveKey& reservekey);
CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight, int32_t gpucount, bool isStake = false);

2
src/notaries_staked.cpp

@ -16,7 +16,7 @@ int8_t is_STAKED(const char *chain_name) {
return(STAKED);
if ( (strcmp(chain_name, "LABS") == 0) || (strncmp(chain_name, "LABS", 4) == 0) )
STAKED = 1;
else if ( (strcmp(chain_name, "LAB") == 0) || (strncmp(chain_name, "LAB", 3) == 0) )
else if ( (strcmp(chain_name, "LABT2") == 0) || (strncmp(chain_name, "LABT2", 3) == 0) )
STAKED = 2;
else if ( (strcmp(chain_name, "CFEK") == 0) || (strncmp(chain_name, "CFEK", 4) == 0) )
STAKED = 3;

7
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);
@ -134,6 +133,8 @@ unsigned int lwmaCalculateNextWorkRequired(const CBlockIndex* pindexLast, const
bnLimit = UintToArith256(params.powAlternate);
unsigned int nProofOfWorkLimit = bnLimit.GetCompact();
//printf("PoWLimit: %u\n", nProofOfWorkLimit);
// Find the first block in the averaging interval as we total the linearly weighted average
const CBlockIndex* pindexFirst = pindexLast;

14
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;
@ -46,8 +46,11 @@ uint256 CBlockHeader::GetVerusHash() const
uint256 CBlockHeader::GetVerusV2Hash() const
{
// no check for genesis block and use the optimized hash
return SerializeVerusHashV2(*this);
if (hashPrevBlock.IsNull())
// always use SHA256D for genesis block
return SerializeHash(*this);
else
return SerializeVerusHashV2(*this);
}
void CBlockHeader::SetSHA256DHash()
@ -60,6 +63,11 @@ void CBlockHeader::SetVerusHash()
CBlockHeader::hashFunction = &CBlockHeader::GetVerusHash;
}
void CBlockHeader::SetVerusHashV2()
{
CBlockHeader::hashFunction = &CBlockHeader::GetVerusV2Hash;
}
// returns false if unable to fast calculate the VerusPOSHash from the header.
// if it returns false, value is set to 0, but it can still be calculated from the full block
// in that case. the only difference between this and the POS hash for the contest is that it is not divided by the value out

1
src/primitives/block.h

@ -106,6 +106,7 @@ public:
uint256 GetVerusEntropyHash(int32_t nHeight) const;
uint256 GetVerusV2Hash() const;
static void SetVerusHashV2();
int64_t GetBlockTime() const
{

2
src/rpc/blockchain.cpp

@ -622,7 +622,7 @@ UniValue getblockhash(const UniValue& params, bool fHelp)
return pblockindex->GetBlockHash().GetHex();
}
extern uint64_t ASSETCHAINS_STAKED;
extern int32_t ASSETCHAINS_STAKED;
UniValue getlastsegidstakes(const UniValue& params, bool fHelp)
{

13
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);
/**
@ -438,8 +436,11 @@ UniValue getmininginfo(const UniValue& params, bool fHelp)
obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC()));
obj.push_back(Pair("chain", Params().NetworkIDString()));
#ifdef ENABLE_MINING
obj.push_back(Pair("staking", VERUS_MINTBLOCKS));
obj.push_back(Pair("generate", GetBoolArg("-gen", false)));
bool staking = VERUS_MINTBLOCKS;
if ( ASSETCHAINS_STAKED != 0 && GetBoolArg("-gen", false) && GetBoolArg("-genproclimit", -1) == 0 )
staking = true;
obj.push_back(Pair("staking", staking));
obj.push_back(Pair("generate", GetBoolArg("-gen", false) && GetBoolArg("-genproclimit", -1) != 0 ));
obj.push_back(Pair("numthreads", (int64_t)KOMODO_MININGTHREADS));
#endif
return obj;

6
src/rpc/misc.cpp

@ -80,8 +80,8 @@ int8_t StakedNotaryID(std::string &notaryname, char *Raddress);
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 int32_t ASSETCHAINS_LWMAPOS,ASSETCHAINS_SAPLING;
extern uint64_t ASSETCHAINS_COMMISSION,ASSETCHAINS_SUPPLY;
extern int32_t ASSETCHAINS_LWMAPOS,ASSETCHAINS_SAPLING,ASSETCHAINS_STAKED;
extern uint64_t ASSETCHAINS_ENDSUBSIDY[],ASSETCHAINS_REWARD[],ASSETCHAINS_HALVING[],ASSETCHAINS_DECAY[];
extern std::string NOTARY_PUBKEY,NOTARY_ADDRESS; extern uint8_t NOTARY_PUBKEY33[];
@ -306,7 +306,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));

32
src/rpc/server.cpp

@ -409,8 +409,16 @@ static const CRPCCommand vRPCCommands[] =
{ "faucet", "faucetget", &faucetget, true },
{ "faucet", "faucetaddress", &faucetaddress, true },
// Heir
{ "heir", "heiraddress", &heiraddress, true },
// Heir
{ "heir", "heiraddress", &heiraddress, true },
{ "heir", "heirfund", &heirfund, true },
{ "heir", "heiradd", &heiradd, true },
{ "heir", "heirclaim", &heirclaim, true },
/* { "heir", "heirfundtokens", &heirfundtokens, true },
{ "heir", "heiraddtokens", &heiraddtokens, true },
{ "heir", "heirclaimtokens", &heirclaimtokens, true },*/
{ "heir", "heirinfo", &heirinfo, true },
{ "heir", "heirlist", &heirlist, true },
// Channels
{ "channels", "channelsaddress", &channelsaddress, true },
@ -445,11 +453,23 @@ static const CRPCCommand vRPCCommands[] =
{ "pegs", "pegsaddress", &pegsaddress, true },
// Marmara
{ "marmara", "Marmaraaddress", &marmaraaddress, true },
{ "marmara", "marmaraaddress", &marmaraaddress, true },
{ "marmara", "marmarapoolpayout", &marmara_poolpayout, true },
{ "marmara", "marmarareceive", &marmara_receive, true },
{ "marmara", "marmaraissue", &marmara_issue, true },
{ "marmara", "marmaratransfer", &marmara_transfer, true },
{ "marmara", "marmarainfo", &marmara_info, true },
{ "marmara", "marmaracreditloop", &marmara_creditloop, true },
{ "marmara", "marmarasettlement", &marmara_settlement, true },
{ "marmara", "marmaralock", &marmara_lock, true },
// 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 },
@ -475,7 +495,8 @@ static const CRPCCommand vRPCCommands[] =
{ "dice", "dicestatus", &dicestatus, true },
{ "dice", "diceaddress", &diceaddress, true },
// tokens
// tokens & assets
{ "tokens", "assetsaddress", &assetsaddress, true },
{ "tokens", "tokeninfo", &tokeninfo, true },
{ "tokens", "tokenlist", &tokenlist, true },
{ "tokens", "tokenorders", &tokenorders, true },
@ -519,6 +540,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},

21
src/rpc/server.h

@ -242,6 +242,7 @@ extern UniValue tokeninfo(const UniValue& params, bool fHelp);
extern UniValue tokenlist(const UniValue& params, bool fHelp);
extern UniValue tokenorders(const UniValue& params, bool fHelp);
extern UniValue tokenbalance(const UniValue& params, bool fHelp);
extern UniValue assetsaddress(const UniValue& params, bool fHelp);
extern UniValue tokenaddress(const UniValue& params, bool fHelp);
extern UniValue tokencreate(const UniValue& params, bool fHelp);
extern UniValue tokentransfer(const UniValue& params, bool fHelp);
@ -253,6 +254,11 @@ extern UniValue tokencancelask(const UniValue& params, bool fHelp);
extern UniValue tokenfillask(const UniValue& params, bool fHelp);
extern UniValue tokenconvert(const UniValue& params, bool fHelp);
extern UniValue heiraddress(const UniValue& params, bool fHelp);
extern UniValue heirfund(const UniValue& params, bool fHelp);
extern UniValue heiradd(const UniValue& params, bool fHelp);
extern UniValue heirclaim(const UniValue& params, bool fHelp);
extern UniValue heirinfo(const UniValue& params, bool fHelp);
extern UniValue heirlist(const UniValue& params, bool fHelp);
extern UniValue channelsaddress(const UniValue& params, bool fHelp);
extern UniValue oraclesaddress(const UniValue& params, bool fHelp);
extern UniValue oracleslist(const UniValue& params, bool fHelp);
@ -272,7 +278,18 @@ extern UniValue pricesstatus(const UniValue& params, bool fHelp);
extern UniValue pricesfinish(const UniValue& params, bool fHelp);
extern UniValue pegsaddress(const UniValue& params, bool fHelp);
extern UniValue marmaraaddress(const UniValue& params, bool fHelp);
extern UniValue marmara_poolpayout(const UniValue& params, bool fHelp);
extern UniValue marmara_receive(const UniValue& params, bool fHelp);
extern UniValue marmara_issue(const UniValue& params, bool fHelp);
extern UniValue marmara_transfer(const UniValue& params, bool fHelp);
extern UniValue marmara_info(const UniValue& params, bool fHelp);
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);
@ -458,4 +475,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

327
src/script/sign.cpp

@ -45,14 +45,14 @@ bool TransactionSignatureCreator::CreateSig(std::vector<unsigned char>& 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<unsigned char>& vchSig,
return false;
}
}
vchSig.push_back((unsigned char)nHashType);
return true;
}
@ -128,7 +128,7 @@ std::vector<CCcontract_info> &GetCryptoConditions()
static bool initialized = false;
static std::vector<CCcontract_info> vCC = std::vector<CCcontract_info>();
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<CCcontract_info> &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<CCcontract_info> &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<std::vector<unsigned char>> vvch = std::vector<std::vector<unsigned char>>();
if (Solver(scriptPubKey, whichType, vvch) && vvch[0].size() == 20)
@ -199,10 +199,10 @@ static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scrip
vector<CPubKey> vPK;
vector<valtype> vParams = vector<valtype>();
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<unsigned char> 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<unsigned char> 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<unsigned char> 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<valtype> 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<unsigned char>(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<unsigned char>(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<unsigned char>(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<valtype> CombineMultisig(const CScript& scriptPubKey, const BaseSignatureChecker& checker,
const vector<valtype>& vSolutions,
const vector<valtype>& sigs1, const vector<valtype>& sigs2, uint32_t consensusBranchId)
const vector<valtype>& vSolutions,
const vector<valtype>& sigs1, const vector<valtype>& sigs2, uint32_t consensusBranchId)
{
// Combine all the signatures we've got:
set<valtype> allsigs;
@ -482,7 +482,7 @@ static vector<valtype> 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<valtype> 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<valtype> 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<valtype> script;
Stacks() {}
explicit Stacks(const std::vector<valtype>& 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<valtype> script;
Stacks() {}
explicit Stacks(const std::vector<valtype>& 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<valtype>& vSolutions,
Stacks sigs1, Stacks sigs2, uint32_t consensusBranchId)
const txnouttype txType, const vector<valtype>& 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())
return sigs2;
return sigs1;
case TX_SCRIPTHASH:
if (sigs1.script.empty() || sigs1.script.back().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;
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<vector<unsigned char> > 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<vector<unsigned char> > 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<vector<unsigned char> > 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<unsigned char>& scriptSig,
const std::vector<unsigned char>& 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<unsigned char>& scriptSig,
const std::vector<unsigned char>& 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<unsigned char>& vchSig,
const CKeyID& keyid,
const CScript& scriptCode,
uint32_t consensusBranchId,
CKey *key,
void *extraData) const
std::vector<unsigned char>& 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;
}

26
src/test/miner_tests.cpp

@ -158,7 +158,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
for (unsigned int i = 0; i < sizeof(blockinfo)/sizeof(*blockinfo); ++i)
{
// Simple block creation, nothing special yet:
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey,-1));
BOOST_CHECK(pblocktemplate = CreateNewBlock(CPubKey(),scriptPubKey,-1));
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
pblock->nVersion = 4;
@ -273,7 +273,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
}
// Just to make sure we can still make simple blocks
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey,-1));
BOOST_CHECK(pblocktemplate = CreateNewBlock(CPubKey(),scriptPubKey,-1));
delete pblocktemplate;
// block sigops > limit: 1000 CHECKMULTISIG + 1
@ -292,7 +292,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
tx.vin[0].prevout.hash = hash;
}
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey,-1));
BOOST_CHECK(pblocktemplate = CreateNewBlock(CPubKey(),scriptPubKey,-1));
delete pblocktemplate;
mempool.clear();
@ -313,14 +313,14 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
tx.vin[0].prevout.hash = hash;
}
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey,-1));
BOOST_CHECK(pblocktemplate = CreateNewBlock(CPubKey(),scriptPubKey,-1));
delete pblocktemplate;
mempool.clear();
// orphan in mempool
hash = tx.GetHash();
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey,-1));
BOOST_CHECK(pblocktemplate = CreateNewBlock(CPubKey(),scriptPubKey,-1));
delete pblocktemplate;
mempool.clear();
@ -338,7 +338,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vout[0].nValue = 49000LL;
hash = tx.GetHash();
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey,-1));
BOOST_CHECK(pblocktemplate = CreateNewBlock(CPubKey(),scriptPubKey,-1));
delete pblocktemplate;
mempool.clear();
@ -349,7 +349,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vout[0].nValue = 0;
hash = tx.GetHash();
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey,-1));
BOOST_CHECK(pblocktemplate = CreateNewBlock(CPubKey(),scriptPubKey,-1));
delete pblocktemplate;
mempool.clear();
@ -367,7 +367,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vout[0].nValue -= 10000;
hash = tx.GetHash();
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey,-1));
BOOST_CHECK(pblocktemplate = CreateNewBlock(CPubKey(),scriptPubKey,-1));
delete pblocktemplate;
mempool.clear();
@ -381,17 +381,17 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vout[0].scriptPubKey = CScript() << OP_2;
hash = tx.GetHash();
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey,-1));
BOOST_CHECK(pblocktemplate = CreateNewBlock(CPubKey(),scriptPubKey,-1));
delete pblocktemplate;
mempool.clear();
// subsidy changing
int nHeight = chainActive.Height();
chainActive.Tip()->SetHeight(209999);
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey,-1));
BOOST_CHECK(pblocktemplate = CreateNewBlock(CPubKey(),scriptPubKey,-1));
delete pblocktemplate;
chainActive.Tip()->SetHeight(210000);
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey,-1));
BOOST_CHECK(pblocktemplate = CreateNewBlock(CPubKey(),scriptPubKey,-1));
delete pblocktemplate;
chainActive.Tip()->SetHeight(nHeight);
@ -423,7 +423,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
mempool.addUnchecked(hash, entry.Time(GetTime()).SpendsCoinbase(true).FromTx(tx2));
BOOST_CHECK(!CheckFinalTx(tx2, LOCKTIME_MEDIAN_TIME_PAST));
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey,-1));
BOOST_CHECK(pblocktemplate = CreateNewBlock(CPubKey(),scriptPubKey,-1));
// Neither tx should have made it into the template.
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1);
@ -438,7 +438,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
//BOOST_CHECK(CheckFinalTx(tx));
//BOOST_CHECK(CheckFinalTx(tx2));
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey,-1));
BOOST_CHECK(pblocktemplate = CreateNewBlock(CPubKey(),scriptPubKey,-1));
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 2);
delete pblocktemplate;

1
src/wallet-utility.cpp

@ -18,6 +18,7 @@ uint32_t ASSETCHAIN_INIT,ASSETCHAINS_CC;
uint32_t ASSETCHAINS_MAGIC = 2387029918;
uint32_t ASSETCHAINS_EQUIHASH = 0;
uint32_t ASSETCHAINS_VERUSHASH = 1;
uint32_t ASSETCHAINS_VERUSHASHV2 = 2;
uint32_t ASSETCHAINS_ALGO = 0;
int32_t ASSETCHAINS_LWMAPOS = 0;
int32_t VERUS_BLOCK_POSUNITS = 1000;

2
src/wallet/db.h

@ -33,7 +33,7 @@
#include <boost/filesystem/path.hpp>
#include <db_cxx.h>
#include "../depends/x86_64-unknown-linux-gnu/include/db_cxx.h"
extern unsigned int nWalletDBUpdated;

566
src/wallet/rpcwallet.cpp

@ -58,6 +58,7 @@
#include <numeric>
#include "komodo_defs.h"
using namespace std;
@ -68,8 +69,6 @@ extern std::string ASSETCHAINS_OVERRIDE_PUBKEY;
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
@ -3085,7 +3084,7 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
BlockMap::const_iterator it = mapBlockIndex.find(hashBlock);
if (it != mapBlockIndex.end()) {
nHeight = it->second->GetHeight();
fprintf(stderr,"blockHash %s height %d\n",hashBlock.ToString().c_str(), nHeight);
//fprintf(stderr,"blockHash %s height %d\n",hashBlock.ToString().c_str(), nHeight);
} else {
// TODO: should we throw JSONRPCError ?
fprintf(stderr,"block hash %s does not exist!\n", hashBlock.ToString().c_str() );
@ -3119,7 +3118,7 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
BlockMap::const_iterator it = mapBlockIndex.find(hashBlock);
if (it != mapBlockIndex.end()) {
nHeight = it->second->GetHeight();
fprintf(stderr,"blockHash %s height %d\n",hashBlock.ToString().c_str(), nHeight);
//fprintf(stderr,"blockHash %s height %d\n",hashBlock.ToString().c_str(), nHeight);
} else {
// TODO: should we throw JSONRPCError ?
fprintf(stderr,"block hash %s does not exist!\n", hashBlock.ToString().c_str() );
@ -3910,7 +3909,7 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
BlockMap::const_iterator it = mapBlockIndex.find(hashBlock);
if (it != mapBlockIndex.end()) {
nHeight = it->second->GetHeight();
fprintf(stderr,"blockHash %s height %d\n",hashBlock.ToString().c_str(), nHeight);
//fprintf(stderr,"blockHash %s height %d\n",hashBlock.ToString().c_str(), nHeight);
} else {
fprintf(stderr,"block hash %s does not exist!\n", hashBlock.ToString().c_str() );
}
@ -3942,7 +3941,7 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
BlockMap::const_iterator it = mapBlockIndex.find(hashBlock);
if (it != mapBlockIndex.end()) {
nHeight = it->second->GetHeight();
fprintf(stderr,"blockHash %s height %d\n",hashBlock.ToString().c_str(), nHeight);
//fprintf(stderr,"blockHash %s height %d\n",hashBlock.ToString().c_str(), nHeight);
} else {
fprintf(stderr,"block hash %s does not exist!\n", hashBlock.ToString().c_str() );
}
@ -5219,8 +5218,8 @@ int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33)
if (!EnsureWalletIsAvailable(0))
return 0;
const CKeyStore& keystore = *pwalletMain;
assert(pwalletMain != NULL);
const CKeyStore& keystore = *pwalletMain;
LOCK2(cs_main, pwalletMain->cs_wallet);
utxovalue = 0;
memset(&utxotxid,0,sizeof(utxotxid));
@ -5297,7 +5296,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);
@ -5461,6 +5459,42 @@ UniValue channelsaddress(const UniValue& params, bool fHelp)
return(result);
}
UniValue cclibaddress(const UniValue& params, bool fHelp)
{
struct CCcontract_info *cp,C; std::vector<unsigned char> 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)
{
@ -5552,26 +5586,18 @@ UniValue gatewaysaddress(const UniValue& params, bool fHelp)
UniValue heiraddress(const UniValue& params, bool fHelp)
{
struct CCcontract_info *cp,C; std::vector<unsigned char> destPubkey;
cp = CCinit(&C,EVAL_HEIR);
if ( fHelp || (params.size() != 4 && params.size() != 3))
throw runtime_error("heiraddress func txid amount [destpubkey]\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());
char funcid = ((char *)params[0].get_str().c_str())[0];
uint256 assetid = Parseuint256((char *)params[1].get_str().c_str());
int64_t funds = atof(params[2].get_str().c_str()) * COIN ;
if(params.size() == 4)
destPubkey = ParseHex(params[3].get_str().c_str());
struct CCcontract_info *cp,C; std::vector<unsigned char> pubkey;
cp = CCinit(&C,EVAL_HEIR);
if ( fHelp || params.size() > 1 )
throw runtime_error("heiraddress [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 *)"Heir",pubkey));
}
//return HeirFundBad(funcid, assetid, funds, destPubkey);
return(CCaddress(cp,(char *)"Heir",destPubkey));
}
UniValue lottoaddress(const UniValue& params, bool fHelp)
{
@ -5653,17 +5679,200 @@ UniValue rewardsaddress(const UniValue& params, bool fHelp)
return(CCaddress(cp,(char *)"Rewards",pubkey));
}
UniValue assetsaddress(const UniValue& params, bool fHelp)
{
struct CCcontract_info *cp, C; std::vector<unsigned char> pubkey;
cp = CCinit(&C, EVAL_ASSETS);
if (fHelp || params.size() > 1)
throw runtime_error("assetsaddress [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 *)"Assets", pubkey));
}
UniValue tokenaddress(const UniValue& params, bool fHelp)
{
struct CCcontract_info *cp,C; std::vector<unsigned char> pubkey;
cp = CCinit(&C,EVAL_ASSETS);
cp = CCinit(&C,EVAL_TOKENS);
if ( fHelp || params.size() > 1 )
throw runtime_error("tokenaddress [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 *)"Assets",pubkey));
return(CCaddress(cp,(char *)"Tokens", pubkey));
}
UniValue marmara_poolpayout(const UniValue& params, bool fHelp)
{
int32_t firstheight; double perc; char *jsonstr;
if ( fHelp || params.size() != 3 )
{
// marmarapoolpayout 0.5 2 '[["024131032ed90941e714db8e6dd176fe5a86c9d873d279edecf005c06f773da686",1000],["02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92",100]]';
//marmarapoolpayout 0 2 '[["024131032ed90941e714db8e6dd176fe5a86c9d873d279edecf005c06f773da686",1000]]'
throw runtime_error("marmarapoolpayout perc firstheight \"[[\\\"pubkey\\\":shares], ...]\"\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");
perc = atof(params[0].get_str().c_str()) / 100.;
firstheight = atol(params[1].get_str().c_str());
jsonstr = (char *)params[2].get_str().c_str();
return(MarmaraPoolPayout(0,firstheight,perc,jsonstr)); // [[pk0, shares0], [pk1, shares1], ...]
}
UniValue marmara_receive(const UniValue& params, bool fHelp)
{
UniValue result(UniValue::VOBJ); uint256 batontxid; std::vector<uint8_t> senderpub; int64_t amount; int32_t matures; std::string currency;
if ( fHelp || (params.size() != 5 && params.size() != 4) )
{
// automatic flag -> lsb of matures
// 1st marmarareceive 028076d42eb20efc10007fafb5ca66a2052523c0d2221e607adf958d1a332159f6 7.5 MARMARA 1440
// after marmarareceive 039433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775 7.5 MARMARA 1168 d72d87aa0d50436de695c93e2bf3d7273c63c92ef6307913aa01a6ee6a16548b
throw runtime_error("marmarareceive senderpk amount currency matures batontxid\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");
memset(&batontxid,0,sizeof(batontxid));
senderpub = ParseHex(params[0].get_str().c_str());
if (senderpub.size()!= 33)
{
ERR_RESULT("invalid sender pubkey");
return result;
}
amount = atof(params[1].get_str().c_str()) * COIN + 0.00000000499999;
currency = params[2].get_str();
if ( params.size() == 5 )
{
matures = atol(params[3].get_str().c_str());
batontxid = Parseuint256((char *)params[4].get_str().c_str());
} else matures = atol(params[3].get_str().c_str()) + chainActive.LastTip()->GetHeight() + 1;
return(MarmaraReceive(0,pubkey2pk(senderpub),amount,currency,matures,batontxid,true));
}
UniValue marmara_issue(const UniValue& params, bool fHelp)
{
UniValue result(UniValue::VOBJ); uint256 approvaltxid; std::vector<uint8_t> receiverpub; int64_t amount; int32_t matures; std::string currency;
if ( fHelp || params.size() != 5 )
{
// marmaraissue 039433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775 7.5 MARMARA 1168 32da4cb3e886ee42de90b4a15042d71169077306badf909099c5c5c692df3f27
// marmaraissue 039433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775 700 MARMARA 2629 11fe8bf1de80c2ef69124d08907f259aef7f41e3a632ca2d48ad072a8c8f3078 -> 335df3a5dd6b92a3d020c9465d4d76e0d8242126106b83756dcecbad9813fdf3
throw runtime_error("marmaraissue receiverpk amount currency matures approvaltxid\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");
receiverpub = ParseHex(params[0].get_str().c_str());
if (receiverpub.size()!= 33)
{
ERR_RESULT("invalid receiverpub pubkey");
return result;
}
amount = atof(params[1].get_str().c_str()) * COIN + 0.00000000499999;
currency = params[2].get_str();
matures = atol(params[3].get_str().c_str());
approvaltxid = Parseuint256((char *)params[4].get_str().c_str());
return(MarmaraIssue(0,'I',pubkey2pk(receiverpub),amount,currency,matures,approvaltxid,zeroid));
}
UniValue marmara_transfer(const UniValue& params, bool fHelp)
{
UniValue result(UniValue::VOBJ); uint256 approvaltxid,batontxid; std::vector<uint8_t> receiverpub; int64_t amount; int32_t matures; std::string currency; std::vector<uint256> creditloop;
if ( fHelp || params.size() != 5 )
{
// marmaratransfer 028076d42eb20efc10007fafb5ca66a2052523c0d2221e607adf958d1a332159f6 7.5 MARMARA 1168 1506c774e4b2804a6e25260920840f4cfca8d1fb400e69fe6b74b8e593dbedc5
throw runtime_error("marmaratransfer receiverpk amount currency matures approvaltxid\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");
receiverpub = ParseHex(params[0].get_str().c_str());
if (receiverpub.size()!= 33)
{
ERR_RESULT("invalid receiverpub pubkey");
return result;
}
amount = atof(params[1].get_str().c_str()) * COIN + 0.00000000499999;
currency = params[2].get_str();
matures = atol(params[3].get_str().c_str());
approvaltxid = Parseuint256((char *)params[4].get_str().c_str());
if ( MarmaraGetbatontxid(creditloop,batontxid,approvaltxid) < 0 )
throw runtime_error("couldnt find batontxid\n");
return(MarmaraIssue(0,'T',pubkey2pk(receiverpub),amount,currency,matures,approvaltxid,batontxid));
}
UniValue marmara_info(const UniValue& params, bool fHelp)
{
UniValue result(UniValue::VOBJ); CPubKey issuerpk; std::vector<uint8_t> issuerpub; int64_t minamount,maxamount; int32_t firstheight,lastheight; std::string currency;
if ( fHelp || params.size() < 4 || params.size() > 6 )
{
throw runtime_error("marmarainfo firstheight lastheight minamount maxamount [currency issuerpk]\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");
firstheight = atol(params[0].get_str().c_str());
lastheight = atol(params[1].get_str().c_str());
minamount = atof(params[2].get_str().c_str()) * COIN + 0.00000000499999;
maxamount = atof(params[3].get_str().c_str()) * COIN + 0.00000000499999;
if ( params.size() >= 5 )
currency = params[4].get_str();
if ( params.size() == 6 )
{
issuerpub = ParseHex(params[5].get_str().c_str());
if ( issuerpub.size()!= 33 )
{
ERR_RESULT("invalid issuer pubkey");
return result;
}
issuerpk = pubkey2pk(issuerpub);
}
result = MarmaraInfo(issuerpk,firstheight,lastheight,minamount,maxamount,currency);
return(result);
}
UniValue marmara_creditloop(const UniValue& params, bool fHelp)
{
UniValue result(UniValue::VOBJ); uint256 txid;
if ( fHelp || params.size() != 1 )
{
// marmaracreditloop 010ff7f9256cefe3b5dee3d72c0eeae9fc6f34884e6f32ffe5b60916df54a9be
throw runtime_error("marmaracreditloop txid\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");
txid = Parseuint256((char *)params[0].get_str().c_str());
result = MarmaraCreditloop(txid);
return(result);
}
UniValue marmara_settlement(const UniValue& params, bool fHelp)
{
UniValue result(UniValue::VOBJ); uint256 batontxid;
if ( fHelp || params.size() != 1 )
{
// marmarasettlement 010ff7f9256cefe3b5dee3d72c0eeae9fc6f34884e6f32ffe5b60916df54a9be
// marmarasettlement ff3e259869196f3da9b5ea3f9e088a76c4fc063cf36ab586b652e121d441a603
throw runtime_error("marmarasettlement batontxid\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");
batontxid = Parseuint256((char *)params[0].get_str().c_str());
result = MarmaraSettlement(0,batontxid);
return(result);
}
UniValue marmara_lock(const UniValue& params, bool fHelp)
{
UniValue result(UniValue::VOBJ); int64_t amount; int32_t height;
if ( fHelp || params.size() > 2 || params.size() == 0 )
{
throw runtime_error("marmaralock amount unlockht\n");
}
amount = atof(params[0].get_str().c_str()) * COIN + 0.00000000499999;
if ( params.size() == 2 )
height = atol(params[1].get_str().c_str());
else height = chainActive.LastTip()->GetHeight() + 1;
return(MarmaraLock(0,amount,height));
}
UniValue channelslist(const UniValue& params, bool fHelp)
@ -5691,8 +5900,10 @@ UniValue channelsinfo(const UniValue& params, bool fHelp)
UniValue channelsopen(const UniValue& params, bool fHelp)
{
UniValue result(UniValue::VOBJ); int32_t numpayments; int64_t payment; std::vector<unsigned char> destpub; struct CCcontract_info *cp,C; std::string hex;
uint256 tokenid=zeroid;
cp = CCinit(&C,EVAL_CHANNELS);
if ( fHelp || params.size() != 3 )
if ( fHelp || params.size() < 3 || params.size() > 4)
throw runtime_error("channelsopen destpubkey numpayments payment\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");
@ -5716,7 +5927,11 @@ UniValue channelsopen(const UniValue& params, bool fHelp)
ERR_RESULT("invalid payment amount, must be greater than 0");
return result;
}
hex = ChannelOpen(0,pubkey2pk(destpub),numpayments,payment);
if (params.size()==4)
{
tokenid=Parseuint256((char *)params[3].get_str().c_str());
}
hex = ChannelOpen(0,pubkey2pk(destpub),numpayments,payment,tokenid);
if ( hex.size() > 0 )
{
result.push_back(Pair("result", "success"));
@ -5729,7 +5944,7 @@ UniValue channelspayment(const UniValue& params, bool fHelp)
{
UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; std::string hex; uint256 opentxid,secret=zeroid; int32_t n; int64_t amount;
cp = CCinit(&C,EVAL_CHANNELS);
if ( fHelp || params.size() != 2 )
if ( fHelp || params.size() < 2 || params.size() >3 )
throw runtime_error("channelspayment opentxid amount [secret]\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");
@ -6798,7 +7013,7 @@ UniValue tokenlist(const UniValue& params, bool fHelp)
throw runtime_error("tokenlist\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(AssetList());
return(TokenList());
}
UniValue tokeninfo(const UniValue& params, bool fHelp)
@ -6809,7 +7024,7 @@ UniValue tokeninfo(const UniValue& params, bool fHelp)
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");
tokenid = Parseuint256((char *)params[0].get_str().c_str());
return(AssetInfo(tokenid));
return(TokenInfo(tokenid));
}
UniValue tokenorders(const UniValue& params, bool fHelp)
@ -6819,31 +7034,51 @@ UniValue tokenorders(const UniValue& params, bool fHelp)
throw runtime_error("tokenorders [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");
if ( params.size() == 1 )
tokenid = Parseuint256((char *)params[0].get_str().c_str());
else memset(&tokenid,0,sizeof(tokenid));
if (params.size() == 1) {
tokenid = Parseuint256((char *)params[0].get_str().c_str());
if (tokenid == zeroid)
throw runtime_error("incorrect tokenid\n");
}
else
memset(&tokenid,0,sizeof(tokenid));
return(AssetOrders(tokenid));
}
UniValue tokenbalance(const UniValue& params, bool fHelp)
{
UniValue result(UniValue::VOBJ); char destaddr[64]; uint256 tokenid; uint64_t balance; std::vector<unsigned char> pubkey; struct CCcontract_info *cp,C;
cp = CCinit(&C,EVAL_ASSETS);
UniValue result(UniValue::VOBJ); uint256 tokenid; uint64_t balance; std::vector<unsigned char> pubkey; struct CCcontract_info *cp,C;
CCerror.clear();
if ( fHelp || params.size() > 2 )
throw runtime_error("tokenbalance tokenid [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");
LOCK(cs_main);
LOCK(cs_main);
tokenid = Parseuint256((char *)params[0].get_str().c_str());
if ( params.size() == 2 )
pubkey = ParseHex(params[1].get_str().c_str());
else pubkey = Mypubkey();
result.push_back(Pair("result", "success"));
if ( GetCCaddress(cp,destaddr,pubkey2pk(pubkey)) != 0 )
result.push_back(Pair("CCaddress",destaddr));
balance = GetAssetBalance(pubkey2pk(pubkey),tokenid);
result.push_back(Pair("tokenid", params[0].get_str()));
result.push_back(Pair("balance", (int64_t)balance));
else
pubkey = Mypubkey();
balance = GetTokenBalance(pubkey2pk(pubkey),tokenid);
if (CCerror.empty()) {
char destaddr[64];
result.push_back(Pair("result", "success"));
cp = CCinit(&C,EVAL_TOKENS);
if (GetCCaddress(cp, destaddr, pubkey2pk(pubkey)) != 0)
result.push_back(Pair("CCaddress", destaddr));
result.push_back(Pair("tokenid", params[0].get_str()));
result.push_back(Pair("balance", (int64_t)balance));
}
else {
ERR_RESULT(CCerror);
}
return(result);
}
@ -6877,7 +7112,7 @@ UniValue tokencreate(const UniValue& params, bool fHelp)
return(result);
}
}
hex = CreateAsset(0,supply,name,description);
hex = CreateToken(0,supply,name,description);
if ( hex.size() > 0 )
{
result.push_back(Pair("result", "success"));
@ -6909,7 +7144,7 @@ UniValue tokentransfer(const UniValue& params, bool fHelp)
ERR_RESULT("amount must be positive");
return(result);
}
hex = AssetTransfer(0,tokenid,pubkey,amount);
hex = TokenTransfer(0,tokenid,pubkey,amount);
if (amount > 0) {
if ( hex.size() > 0 )
{
@ -6946,7 +7181,11 @@ UniValue tokenconvert(const UniValue& params, bool fHelp)
ERR_RESULT("amount must be positive");
return(result);
}
hex = AssetConvert(0,tokenid,pubkey,amount,evalcode);
ERR_RESULT("deprecated");
return(result);
/* hex = AssetConvert(0,tokenid,pubkey,amount,evalcode);
if (amount > 0) {
if ( hex.size() > 0 )
{
@ -6956,7 +7195,7 @@ UniValue tokenconvert(const UniValue& params, bool fHelp)
} else {
ERR_RESULT("amount must be positive");
}
return(result);
return(result); */
}
UniValue tokenbid(const UniValue& params, bool fHelp)
@ -7176,7 +7415,7 @@ UniValue tokenfillask(const UniValue& params, bool fHelp)
result.push_back(Pair("result", "success"));
result.push_back(Pair("hex", hex));
} else {
ERR_RESULT("couldnt fill bid");
ERR_RESULT("couldnt fill ask");
}
} else {
ERR_RESULT("fillunits must be positive");
@ -7253,6 +7492,155 @@ UniValue getbalance64(const UniValue& params, bool fHelp)
return ret;
}
// heir contract functions for coins and tokens
UniValue heirfund(const UniValue& params, bool fHelp)
{
UniValue result(UniValue::VOBJ);
uint256 tokenid = zeroid;
int64_t txfee;
int64_t amount;
int64_t inactivitytime;
std::string hex;
std::vector<unsigned char> pubkey;
std::string name;
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() != 5 && params.size() != 6)
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(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 = 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)
throw runtime_error("incorrect tokenid\n");
}
if( tokenid == zeroid )
result = HeirFundCoinCaller(txfee, amount, name, pubkey2pk(pubkey), inactivitytime, zeroid);
else
result = HeirFundTokenCaller(txfee, amount, name, pubkey2pk(pubkey), inactivitytime, tokenid);
return result;
}
UniValue heiradd(const UniValue& params, bool fHelp)
{
UniValue result;
uint256 fundingtxid;
int64_t txfee;
int64_t amount;
int64_t inactivitytime;
std::string hex;
std::vector<unsigned char> pubkey;
std::string name;
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() != 3)
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(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, params[1].get_str());
return result;
}
UniValue heirclaim(const UniValue& params, bool fHelp)
{
UniValue result; // result(UniValue::VOBJ);
uint256 fundingtxid;
int64_t txfee;
int64_t inactivitytime;
std::string hex;
std::vector<unsigned char> pubkey;
std::string name;
// do we need this?
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() != 3)
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(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, params[1].get_str());
return result;
}
UniValue heirinfo(const UniValue& params, bool fHelp)
{
uint256 fundingtxid;
if (fHelp || params.size() != 1) // or 0?
throw runtime_error("heirinfo 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");
fundingtxid = Parseuint256((char*)params[0].get_str().c_str());
return (HeirInfo(fundingtxid));
}
UniValue heirlist(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 0) // or 0?
throw runtime_error("heirlist\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 (HeirList());
}
extern UniValue dumpprivkey(const UniValue& params, bool fHelp); // in rpcdump.cpp
extern UniValue importprivkey(const UniValue& params, bool fHelp);
extern UniValue importaddress(const UniValue& params, bool fHelp);
@ -7345,3 +7733,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<unsigned char> pubkey1;
std::vector<unsigned char> 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));
}

8
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)
@ -1180,7 +1174,7 @@ bool DecrementNoteWitnesses(NoteDataMap& noteDataMap, int indexHeight, int64_t n
assert((nWitnessCacheSize - 1) >= nd->witnesses.size());
}
}
assert(KOMODO_REWIND != 0 || nWitnessCacheSize > 0);
assert(KOMODO_REWIND != 0 || nWitnessCacheSize > 0 || WITNESS_CACHE_SIZE != _COINBASE_MATURITY+10);
return true;
}

18
src/wallet/walletdb.cpp

@ -969,6 +969,24 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
deadTxns.clear();
}
if (!deadTxns.empty())
{
int32_t reAdded = 0;
BOOST_FOREACH (uint256& hash, deadTxns) {
if (!EraseTx(hash))
fprintf(stderr, "could not delete tx.%s\n",hash.ToString().c_str());
uint256 blockhash; CTransaction tx;
if (GetTransaction(hash,tx,blockhash,true))
{
CWalletTx wtx(pwallet,tx);
pwallet->AddToWallet(wtx, true, NULL);
reAdded++;
}
}
fprintf(stderr, "Cleared %lu corrupted transactions from wallet. Readded %i known transactions.\n",deadTxns.size(),reAdded);
deadTxns.clear();
}
if (fNoncriticalErrors && result == DB_LOAD_OK)
result = DB_NONCRITICAL_ERROR;

15
zcutil/build-mac.sh

@ -42,6 +42,21 @@ PREFIX="$(pwd)/depends/$TRIPLET"
make "$@" -C ./depends/ V=1 NO_QT=1 NO_PROTON=1
#BUILD CCLIB
WD=$PWD
cd src/cc
echo $PWD
if make "$@"; then
echo CCLIB BUILD SUCCESSFUL
else
echo CCLIB BUILD FAILED
exit 1
fi
cd $WD
./autogen.sh
CPPFLAGS="-I$PREFIX/include -arch x86_64" LDFLAGS="-L$PREFIX/lib -arch x86_64 -Wl,-no_pie" \
CXXFLAGS='-arch x86_64 -I/usr/local/Cellar/gcc\@6/6.4.0_2/include/c++/6.4.0/ -I$PREFIX/include -fwrapv -fno-strict-aliasing -Werror -g -Wl,-undefined -Wl,dynamic_lookup' \

4
zcutil/build.sh

@ -105,4 +105,8 @@ HOST="$HOST" BUILD="$BUILD" NO_PROTON="$PROTON_ARG" "$MAKE" "$@" -C ./depends/ V
./autogen.sh
CONFIG_SITE="$PWD/depends/$HOST/share/config.site" ./configure "$HARDENING_ARG" "$LCOV_ARG" "$TEST_ARG" "$MINING_ARG" "$PROTON_ARG" $CONFIGURE_FLAGS CXXFLAGS='-g'
#BUILD CCLIB
cd src/cc/
./makecclib
cd ../../
"$MAKE" "$@" V=1

Loading…
Cancel
Save