diff --git a/README.md b/README.md index df152ff29..bb60c2e6e 100644 --- a/README.md +++ b/README.md @@ -12,17 +12,16 @@ This version of Komodo contains Bitcore support for komodo and all its assetchai This software is the VerusCoin enhanced Komodo client. Generally, you will use this if you want to mine VRSC or setup a full node. When you run the wallet it launches komodod automatically. On first launch it downloads Zcash parameters, roughly 1GB, which is mildly slow. The wallet downloads and stores the block chain or asset chain of the coin you select. It downloads and stores the entire history of the coins transactions; depending on the speed of your computer and network connection, the synchronization process could take a day or more once the blockchain has reached a significant size. -## Development Resources -- VerusCoin:[https://veruscoin.io/](https://veruscoin.io/) Wallets and CLI tools -- Komodo Web: [https://komodoplatform.com/](https://komodoplatform.com/) -- Organization web: [https://komodoplatform.com/](https://komodoplatform.com/) -- Forum: [https://forum.komodoplatform.com/](https://forum.komodoplatform.com/) +- Komodo Website: [https://komodoplatform.com](https://komodoplatform.com/) +- Komodo Blockexplorer: [https://kmdexplorer.io](https://kmdexplorer.io/) +- Komodo Discord: [https://komodoplatform.com/discord](https://komodoplatform.com/discord) +- Forum: [https://forum.komodoplatform.com](https://forum.komodoplatform.com/) - Mail: [info@komodoplatform.com](mailto:info@komodoplatform.com) - Support: [https://support.komodoplatform.com/support/home](https://support.komodoplatform.com/support/home) -- Knowledgebase & How-to: [https://komodoplatform.atlassian.net/wiki/spaces/KPSD/pages](https://komodoplatform.atlassian.net/wiki/spaces/KPSD/pages) -- API references: [http://docs.komodoplatform.com/](http://docs.komodoplatform.com/) -- Blog: [http://blog.komodoplatform.com/](http://blog.komodoplatform.com/) -- Whitepaper: [Komodo Whitepaper](https://komodoplatform.com/wp-content/uploads/2018/03/2018-03-12-Komodo-White-Paper-Full.pdf) +- Knowledgebase & How-to: [https://support.komodoplatform.com/en/support/solutions](https://support.komodoplatform.com/en/support/solutions) +- API references & Dev Documentation: [https://docs.komodoplatform.com](https://docs.komodoplatform.com/) +- Blog: [https://blog.komodoplatform.com](https://blog.komodoplatform.com/) +- Whitepaper: [Komodo Whitepaper](https://komodoplatform.com/whitepaper) - Komodo Platform public material: [Komodo Platform public material](https://docs.google.com/document/d/1AbhWrtagu4vYdkl-vsWz-HSNyNvK-W-ZasHCqe7CZy0) ## List of Komodo Platform Technologies diff --git a/qa/rpc-tests/cryptoconditions.py b/qa/rpc-tests/cryptoconditions.py index e7d3065cc..1d2f68777 100755 --- a/qa/rpc-tests/cryptoconditions.py +++ b/qa/rpc-tests/cryptoconditions.py @@ -3,6 +3,7 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. + from test_framework.test_framework import BitcoinTestFramework from test_framework.authproxy import JSONRPCException from test_framework.util import assert_equal, assert_greater_than, \ @@ -11,6 +12,8 @@ from test_framework.util import assert_equal, assert_greater_than, \ import time from decimal import Decimal +from random import choice +from string import ascii_uppercase def assert_success(result): assert_equal(result['result'], 'success') @@ -18,6 +21,11 @@ def assert_success(result): def assert_error(result): assert_equal(result['result'], 'error') +def generate_random_string(length): + random_string = ''.join(choice(ascii_uppercase) for i in range(length)) + return random_string + + class CryptoConditionsTest (BitcoinTestFramework): def setup_chain(self): @@ -470,7 +478,6 @@ class CryptoConditionsTest (BitcoinTestFramework): result = rpc.tokenbalance(tokenid,randompubkey) assert_equal(result["balance"], 1) - def run_rewards_tests(self): rpc = self.nodes[0] result = rpc.rewardsaddress() @@ -581,6 +588,51 @@ class CryptoConditionsTest (BitcoinTestFramework): result = rpc.rewardsunlock("STUFF", fundingtxid, locktxid) assert_error(result) + def run_oracles_tests(self): + rpc = self.nodes[0] + result = rpc.oraclesaddress() + assert_success(result) + for x in ['OraclesCCaddress', 'Oraclesmarker', 'myCCaddress', 'myaddress']: + assert_equal(result[x][0], 'R') + + result = rpc.oraclesaddress(self.pubkey) + assert_success(result) + for x in ['OraclesCCaddress', 'Oraclesmarker', 'myCCaddress', 'myaddress']: + assert_equal(result[x][0], 'R') + + # there are no oracles created yet + result = rpc.oracleslist() + assert_equal(result, []) + + # looking up non-existent oracle should return error. + result = rpc.oraclesinfo("none") + assert_error(result) + + # attempt to create oracle with not valid data type should return error + result = rpc.oraclescreate("Test", "Test", "Test") + assert_error(result) + + # attempt to create oracle with description > 32 symbols should return error + too_long_name = generate_random_string(33) + result = rpc.oraclescreate(too_long_name, "Test", "s") + + + # attempt to create oracle with description > 4096 symbols should return error + too_long_description = generate_random_string(4100) + result = rpc.oraclescreate("Test", too_long_description, "s") + assert_error(result) + + # valid creating oracles of different types + # using such naming to re-use it for data publishing / reading (e.g. oracle_s for s type) + valid_formats = ["s", "S", "d", "D", "c", "C", "t", "T", "i", "I", "l", "L", "h", "Ihh"] + for f in valid_formats: + result = rpc.oraclescreate("Test", "Test", f) + assert_success(result) + globals()["oracle_{}".format(f)] = self.send_and_mine(result['hex']) + + + + def run_test (self): print("Mining blocks...") @@ -594,12 +646,13 @@ class CryptoConditionsTest (BitcoinTestFramework): print("Importing privkey") rpc.importprivkey(self.privkey) -# self.run_faucet_tests() + #self.run_faucet_tests() self.run_rewards_tests() self.run_dice_tests() self.run_token_tests() self.run_faucet_tests() + self.run_oracles_tests() if __name__ == '__main__': - CryptoConditionsTest ().main () + CryptoConditionsTest ().main() diff --git a/resolve.sh b/resolve.sh new file mode 100755 index 000000000..548e71dee --- /dev/null +++ b/resolve.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +for f in $(git diff --name-only --diff-filter=U | cat); do + echo "Resolve conflict in $f ..." + git checkout --theirs $f +done + +for f in $(git diff --name-only --diff-filter=U | cat); do + echo "Adding file $f ..." + git add $f +done + diff --git a/src/Makefile.am b/src/Makefile.am index 508ccecf9..2f69758d3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -291,15 +291,13 @@ libbitcoin_server_a_SOURCES = \ cc/CCcustom.cpp \ cc/CCtx.cpp \ cc/CCutils.cpp \ - cc/StakeGuard.cpp \ - cc/StakeGuard.h \ cc/assets.cpp \ cc/faucet.cpp \ cc/rewards.cpp \ cc/dice.cpp \ cc/lotto.cpp \ cc/fsm.cpp \ - cc/MofN.cpp \ + cc/heir.cpp \ cc/oracles.cpp \ cc/prices.cpp \ cc/pegs.cpp \ @@ -310,8 +308,6 @@ libbitcoin_server_a_SOURCES = \ cc/auction.cpp \ cc/betprotocol.cpp \ chain.cpp \ - cheatcatcher.h \ - cheatcatcher.cpp \ checkpoints.cpp \ crosschain.cpp \ crypto/haraka.h \ diff --git a/src/ac/mgnx b/src/ac/mgnx new file mode 100755 index 000000000..9bd85336e --- /dev/null +++ b/src/ac/mgnx @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=MGNX $1 $2 $3 $4 $5 $6 diff --git a/src/ac/pgt b/src/ac/pgt new file mode 100755 index 000000000..8e124bbd1 --- /dev/null +++ b/src/ac/pgt @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=PGT $1 $2 $3 $4 $5 $6 diff --git a/src/ac/pirate b/src/ac/pirate new file mode 100755 index 000000000..9314209da --- /dev/null +++ b/src/ac/pirate @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=PIRATE $1 $2 $3 $4 $5 $6 diff --git a/src/assetchains.json b/src/assetchains.json index ceda4cce1..fb20b2cba 100644 --- a/src/assetchains.json +++ b/src/assetchains.json @@ -145,5 +145,35 @@ "142.93.136.89", "195.201.22.89" ] + }, + { + "ac_name": "PIRATE", + "ac_supply": "0", + "ac_reward": "25600000000", + "ac_halving": "77777", + "ac_private": "1", + "addnode": [ + "136.243.102.225" + ] + }, + { + "ac_name": "MGNX", + "ac_supply": "12465003", + "ac_staked": "90", + "ac_reward": "2000000000", + "ac_halving": "525960", + "ac_cc": "2", + "ac_end": "2629800", + "addnode": [ + "142.93.27.180" + ] + }, +{ + "ac_name": "PGT", + "ac_supply": "10000000", + "ac_end": "1", + "addnode": [ + "190.114.254.104" + ] } ] diff --git a/src/assetchains.old b/src/assetchains.old index 158e62cad..12c743d1d 100755 --- a/src/assetchains.old +++ b/src/assetchains.old @@ -39,3 +39,6 @@ echo $pubkey ~/VerusCoin/src/komodod -pubkey=$pubkey -ac_name=VRSC -ac_algo=verushash -ac_cc=1 -ac_veruspos=50 -ac_supply=0 -ac_eras=3 -ac_reward=0,38400000000,2400000000 -ac_halving=1,43200,1051920 -ac_decay=100000000,0,0 -ac_end=10080,226080,0 -ac_timelockgte=19200000000 -ac_timeunlockfrom=129600 -ac_timeunlockto=1180800 -addnode=185.25.48.236 -addnode=185.64.105.111 & ./komodod -pubkey=$pubkey -ac_name=SEC -ac_cc=333 -ac_supply=1000000000 -addnode=185.148.145.43 & ./komodod -pubkey=$pubkey -ac_name=CCL -ac_supply=200000000 -ac_end=1 -ac_cc=2 -addressindex=1 -spentindex=1 -addnode=142.93.136.89 -addnode=195.201.22.89 & +./komodod -pubkey=$pubkey -ac_name=PIRATE -ac_supply=0 -ac_reward=25600000000 -ac_halving=77777 -ac_private=1 -addnode=136.243.102.225 & +./komodod -pubkey=$pubkey -ac_name=MGNX -ac_supply=12465003 -ac_staked=90 -ac_reward=2000000000 -ac_halving=525960 -ac_cc=2 -ac_end=2629800 -addnode=142.93.27.180 & +./komodod -pubkey=$pubkey -ac_name=PGT -ac_supply=10000000 -ac_end=1 -addnode=190.114.254.104 & diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 793697631..2b495c73d 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -61,8 +61,8 @@ void WaitForShutdown(boost::thread_group* threadGroup) } else { - komodo_interestsum(); - komodo_longestchain(); + //komodo_interestsum(); + //komodo_longestchain(); MilliSleep(20000); } fShutdown = ShutdownRequested(); diff --git a/src/cc/CC made easy.md b/src/cc/CC made easy.md index 4158e061e..3f15a6b6b 100644 --- a/src/cc/CC made easy.md +++ b/src/cc/CC made easy.md @@ -95,7 +95,7 @@ EVAL(EVAL_DICE, 0xe6) \ EVAL(EVAL_FSM, 0xe7) \ EVAL(EVAL_AUCTION, 0xe8) \ EVAL(EVAL_LOTTO, 0xe9) \ -EVAL(EVAL_MOFN, 0xea) \ +EVAL(EVAL_HEIR, 0xea) \ EVAL(EVAL_CHANNELS, 0xeb) \ EVAL(EVAL_ORACLES, 0xec) \ EVAL(EVAL_PRICES, 0xed) \ @@ -447,7 +447,104 @@ WARNING: there is an attack vector that precludes betting any large amounts, it In order to mitigate this, the disclosure of the house entropy needs to be delayed beyond a reasonable reorg depth (notarization). It is recommended for production dice game with significant amounts of money to use such a delayed disclosure method. -Chapter 10 - lotto example +Chapter 10 - channels example +It might be hard to believe, but channels CC implements an instant payment mechanism that is secured by dPoW in a way that is backward compatible with the existing wallets, explorers, etc. and channels CC does not require both nodes to be online. Its usecases are all the usecases for Lightning Network, it is just more secure, less expensive and backward compatible! The one aspect which some might consider a downside (and others another benefit) is that all payments are onchain. This means it would increase blockchain size, but the idea is for channels CC to be used on blockchains with relatively lower value coins, so a txfee of 0.0001 is not anything significant. + +Warning: very confusing blockchain reorganization issues described below. Will be confusing to most people + +From a distance, the blockchain is a chain of blocks. One block after the next, each referencing all the prior blocks. Each block containing a group of transactions. Prior to getting into a block, the transactions are broadcast to the network and if it is valid, it enters the memory pool. Each miner then constructs a valid block from these memory pool transactions and when a transaction gets mined (confirmed), it is removed from the memory pool. + +That is the simple version! + +The reality is quite a bit more complex, but the critical aspect is that the blockchain can (and is) reorganized as part of the expected protocol. This can happen even when there is no 51% attack happening and it is important to understand this process in detail, so here goes. + +What happens if two miners find a valid block at the same time? In this case the "same time" means within the time it takes for a block to propagate to the network. When a miner finds a new block, it is broadcast to the network and nodes update and start waiting for the next block. When there are two different (and valid) blocks propagating at the same time, some nodes update with one of the blocks and some the other, lets call it blockA and blockB. Now the nodes will know about both blockA and blockB, but some will consider blockA to be the chaintip and others will consider blockB to be the chaintip. + +This is where it gets confusing. Which is the correct chaintip (latest block?). It turns out that both blockA and blockB are valid at this moment in time. So there are actuall two blockchains. We have what is called a small fork! Now dont worry, the protocol will help us converge to a single chain, but in order to do that, we need the next block. + +Some miners will be mining from blockA and others from blockB. In most all cases, when the next block is found, it wont be at the "same time" again. So we will end up with a chain that is blockA+blockA2 or blockB+blockB2. Here comes the small reorg! Let's assuming blockA2 was found before blockB2, so that means all nodes that had blockB as the chaintip now see a longer chain blockA+blockA2, which trumps blockB. When that happens, it reorgs the chain so it is on blockA+blockA2. To do this properly, all the transactions that were in blockB are put back into the mempool and blockA is added, then blockA2. + +Of course, when blockB2 arrives, the nodes see it but blockB+blockB2 is the same length as blockA+blockA2, so no reorg happens. Since we postulated that blockAs arrived "before" blockB2, that means all nodes are on the same chaintip, including all the miners and the next block found would be blockA3, without any complications. + +Believe it or not, this sort of thing is happening all the time, one all blockchains. The chaintip is a volatile thing and that is why more than one confirmation is needed to avoid the small reorgs invalidating blockhash. However, it is possible for more than just the blockhash to change. When the reorg happens, all the transactions in the block are put back into the mempool and then the new blocks are processed in order. So what happens if one of the inputs to a transaction that happened in blockB, gets spent in blockA2? Based on random utxo allocation by wallets this is not impossible if an address has a lot of activity, but if it is part of a 51% attack, then this remote chance of an utxo being spent becomes a certainity! In fact, that is what a 51% attack is. + +The attack can go much deeper than just one block. For chains that use the longest chain rule, it can go quite deep indeed. So as all the reorged transactions are put back into the mempool, we feel good that it will get confirmed again. Unfortunately, there is no enforcement of a miner needing to mine any specific transaction in the mempool. And the 51% attacker is intent on mining the transaction that spends an already spent utxo in the reorganized chain. it is called a double spend, but in the reorganized chain, it is spent only once. So it is a bit of a misnomer. + +The important thing to understand is that if any transaction has inputs that are signed by a node, it is possible when the chain reorganizes for that transaction to become invalid. This is why dPoW is important as it doesnt strictly use the longest chain rule, but rather the longest notarized chain rule. Once a block is notarized, then it will refuse to reorganize that block (or any block before). So the risk is still there, but only until a notarization. Please see more detailed information about dPoW . + +Given the above, if you are wondering how can it be possible to have a mempool payment be secured by dPoW. Since it is explained how the reorgs can make valid transactions disappear, it seems unlikely any such solution is possible. However, the CC is very powerful and it can make unlikely things possible. + +The following describes how. + +We know that any payment that is utxo based can be invalidated via 51% attack, or even an unlikely but not impossible random utxo allocation from a busy wallet. Which means the payment cant be via a utxo. Since the CC system is utxo based, you might think that it means CC cant solve this. However, CC is very powerful and can implement payments that are not utxo based. But before this non-utxo payment method is explained, first we need to solve the mechanics of payment. + +At a high level, we want to lock funds into a channel, have this lock notarized so it cant be reorganized. Then payments can unlock funds. Additionally, if we are restricting the payment to just one destination, we also need a way for the sender to reclaim the unused funds. So there needs a way for a close channel notification, which when notarized allows the sender to reclaim all funds. After the channel close is notarized, then the only action possible should be a reclaim of sender funds. + +We need to assume that any payment, channel close, reclaim can be reorganized until it is notarized so great care needs to be made that a payment that is made will always be valid. With some allowances for blocks after a channelclose is notarized, we can protect the payments using the logic of "stop accepting payments after a channelclose is seen". It might be that a full notarization of wait time after the channelclose is notarized is needed to provide sufficient time for all the payments to be reprocessed. + +Now we can finally describe the requirements for the CC. The locked funds need to be able to be spent by either the sender or receiver, the former only after sufficient time after a channelclose and the latter only after a payment is seen (not just confirmed, but just seeing it should be enough). The protection from reorgs is that the payment itself reveals a secret that is needed for the payment and only the secret would be needed, so it wont matter what utxo is used. To lock funds into a CC address that can handle this we need a 1of2 CC address, which can accept a signature from either of two pubkeys. The additional CC constraints would be enforced to make sure payments are made until the channel is closed. + +A hashchain has the nice property of being able to encode a lot of secrets with a single hash. You can hash the hash, over and over and the final hash is the public value. By revealing the next to last hash, it can be verified that it hashes to the final hash. There is a restriction that a hashchain needs to be of reasonable maximum depth, say 1000. That means each iteration of the hashchain that is revealed is worth 1/1000th the total channelfunds. In fact, if the 500th hash value is revealed, half the channelfunds are released. this allows 1/1000th resolution that can be released with a single hash value. + +Now we can make the payment based on the hashvalue revealed at a specified depth before the prior released hashchain value. Both the sender and receiver can make a payment to the destination by attaching a hashchain secret. This means even if the sender's payment is reorganized, if the destination has the revealed secret, a replacement payment can be made that is valid. If the destination account isnt monitoring the blockchain, then it wont see the revealed secret, but in this case there shouldnt be value released for the payments that are reorganized. So it would be a case of no harm, no foul. In any event, all the payments end up verifiable on the blockchain to provide verifiability. + +Payments at the speed of the mempool, protected by dPoW! + +RPC calls +channelsopen: + Used to open channel between two pub keys (sender and receiver). Parameters: destination_pubkey, total_number_of_payments, payment_denomination. + Example - channelsopen 03a8fe537de2ace0d9c210b0ff945085c9192c9abf56ea22f22ce7998f289bb7bb 10 10000000 +channelspayment: + Sending payment to receiver. Condition is that the channel open tx is confirmed/notarised. Parameters: open_tx_id, payment_amount, [secret] (optional, used when receiver needs to make a payment which secret has already been revealed by sender). + Example - channelspayment b9c141facc8cb71306d0de8e525b3de1450e93e17fc8799c8fda5ed52fd14440 20000000 +channelsclose: + Marking channel as closed. This RPC only creates a tx which says that the channel is closed and will be used in refund RPC to withdraw funds from closed channel. This also notifies receiver that channel fund could be withdrawn, but the payment RPC is still available until all funds are withdrawn. Parameters: open_tx_id. + Example - channelsclose b9c141facc8cb71306d0de8e525b3de1450e93e17fc8799c8fda5ed52fd14440 +channelsrefund: + Withdrawing funds back to senders address. Refund can be issued only when channel close tx is confirmed/notarised. Parameters: open_tx_id, close_tx_id + Example - channelsrefund b9c141facc8cb71306d0de8e525b3de1450e93e17fc8799c8fda5ed52fd14440 bb0ea34f846247642684c7c541c435b06ee79e47893640e5d2e51023841677fd +channelsinfo: + Getting info about channels in which the issuer is involved, either as sender or receiver. Call without parameters give the list of available channels. Parameters: [open_tx_id] (optional - used to get info about specific channel) + +VIN/VOUT allocation +Open: + vin.0: normal input + vout.0: CC vout for channel funding on CC1of2 pubkey + vout.1: CC vout marker to senders pubKey + vout.2: CC vout marker to receiver pubkey + vout.n-2: normal change + vout.n-1: opreturn - 'O' zerotxid senderspubkey receiverspubkey totalnumberofpayments paymentamount hashchain + +Payment + vin.0: normal input + vin.1: CC input from channel funding + vin.2: CC input from src marker + vout.0: CC vout change to channel funding on CC1of2 pubkey + vout.1: CC vout marker to senders pubKey + vout.2: CC vout marker to receiver pubkey + vout.3: normal output of payment amount to receiver pubkey + vout.n-2: normal change + vout.n-1: opreturn - 'P' opentxid senderspubkey receiverspubkey depth numpayments secret + +Close: + vin.0: normal input + vin.1: CC input from channel funding + vin.2: CC input from src marker + vout.0: CC vout for channel funding + vout.1: CC vout marker to senders pubKey + vout.2: CC vout marker to receiver pubkey + vout.n-2: normal change + vout.n-1: opreturn - 'C' opentxid senderspubkey receiverspubkey 0 0 0 + +Refund: + vin.0: normal input + vin.1: CC input from channel funding + vin.2: CC input from src marker + vout.0: CC vout marker to senders pubKey + vout.1: CC vout marker to receiver pubKey + vout.2: normal output of CC input to senders pubkey + vout.n-2: normal change + vout.n-1: opreturn - 'R' opentxid senderspubkey receiverspubkey numpayments payment closetxid Chapter 11 - oracles example Oracles CC is an example where it ended up being simpler than I first expected, but at the same time a lot more powerful. It is one of the smaller CC, but it enables creation of an arbitrary number of data markets, in a performant way. @@ -582,6 +679,39 @@ Conclusion I hope this document has helped you understand what a Komodo utxo based CC contract is and how it is different from the other smart contracts. If you are now able to dive into the cc directory and start making your own CC contract, then I am very happy! +gateways CC +gateways CC is the first CC that combines multiple CC into a single one. In order to achieve its goals, both the assets CC and the oracles CC is used, in addition to a dapp that issues regular transactions. This general approach can be used to solve quite a few different use cases, so it is important to understand how a multi-CC solution is put together. There are some tricky issues that only arise when using more than one CC at a time. + +Before the implementation details, first lets understand what the gateways CC does. At a high level it is similar to the old multigateway (from NXT AE 2014), but with improvements. The basic idea is to tokenize other crypto coins (like BTC) and then use the assets CC to transact/swap against the tokenized crypto. By enforcing a 1:1 peg between a specific token and BTC and an automated deposit/withdraw mechanism, it is possible to transact in the virtual BTC without the delay or expensive txfees. Then anybody that ends up having any of the BTC token would be able to withdraw actual BTC by redeeming the token. + +BTC -> deposit to special address -> receive token that represents BTC onchain +do onchain transactions with the BTC token +anybody who obtains the BTC token can redeem the token and get actual BTC in the withdraw address + +By bringing the operations onchain, it avoids the need for crosschain complications for each trade. The crosschain does still have to happen on the deposit and withdraw, but that is all. There is just one aspect that makes it not fully decentralized, which is the reliance on MofN multisig. However, with N trusted community members and a reasonable M value, it is not expected to be a big barrier. Since all operations are automated, the only trust needed is that M of the N multisig signers are running their nodes with the gateways dapp active and also that M of them wont collude to steal the funds locked in the multisig. In three years of operations, the MGW multigateway didnt have any incident of multisig signer misbehavior and it was only 2of3 multisig. + +How can the gatewaysCC work? First, it needs a dedicated token that can be used to represent the external crypto coin. In order to avoid any issues with misplaced tokens, it is simplest to require that 100% of all the tokens are all locked in the gatewaysCC address. We want to make it so that the only way the tokens can be released from the locked address is when a verified deposit is made. So, we also need a deposit address, which means there needs to be a set of pubkeys that control the deposit address. It turns out, we also need to post merkleroot data from the external coin so that the information is onchain to be able to validate the external deposit. Since we are already trusting the deposit address signers to safekeep the external coins via MofN multisig, trusting them to post the merkleroots doesnt increase the trust footprint needed. + +Now we have all the ingredients needed, a dedicated token, a set of multisig pubkeys and an oracle for merkleroots. + +gatewaysbind tokenid oracletxid coin tokensupply M N pubkey(s) + +With a gatewaysbind, a new gateway is defined. the pubkeys are for the custodians of the multisig funds and they also need to be posting merkleroots to the chain, so the oracle needs to be setup and funded and each of the signers needs to run the oraclefeed dapp. That posts each new merkleroot via oraclesdata and also responds to withdraw requests. + +The MofN pubkeys generates a deposit address and when funds are sent to that address along with a small amount to the claim address. With the txid from this external coin, along with the txproof and the rawtransaction, all is submitted with a gatewaysdeposit. This adds a special baton output which is a gateways CC output to invoke gateways validation and also prevents double claims by using the unspent status of the baton. + +gatewaysdeposit bindtxid height coin cointxid claimvout deposithex proof destpub amount +gatewaysclaim bindtxid coin deposittxid destpub amount + +Once the gatewaysdeposit is validated, it can be claimed and now the token is sent to the claim address. A 1:1 pegging of the external crypto to the token is established. And as long as one of the deposit address signers is running the oraclefeed, then the deposit/claim process is fully automatic and under the control of the depositor. Nothing needs to be signed by any other party! Also by using the utxo from the deposittxid, double claims are prevented. + +On the withdraw side, the tokens are sent back to the address where the tokens are locked and this needs to create a redemption right that can only be used once. + +gatewayswithdraw bindtxid coin withdrawpub amount + + +And with a bit more magic in the oraclefeed, this is achieved. To be continued... + diff --git a/src/cc/CCGateways.h b/src/cc/CCGateways.h index d83263af2..0ccf21f44 100644 --- a/src/cc/CCGateways.h +++ b/src/cc/CCGateways.h @@ -24,9 +24,11 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & std::string GatewaysBind(uint64_t txfee,std::string coin,uint256 tokenid,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector pubkeys); std::string GatewaysDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 cointxid,int32_t claimvout,std::string deposithex,std::vectorproof,CPubKey destpub,int64_t amount); std::string GatewaysClaim(uint64_t txfee,uint256 bindtxid,std::string refcoin,uint256 deposittxid,CPubKey destpub,int64_t amount); -std::string GatewaysWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin,std::vector withdrawpub,int64_t amount); +std::string GatewaysWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount); UniValue GatewaysPendingWithdraws(uint256 bindtxid,std::string refcoin); -std::string GatewaysMarkdone(uint64_t txfee,uint256 withdrawtxid); +std::string GatewaysMarkdone(uint64_t txfee,uint256 withdrawtxid,std::string refcoin,uint256 cointxid); +UniValue GatewaysMultisig(char *txidaddr); +std::string GatewaysPartialSign(uint64_t txfee,uint256 txidaddr,std::string refcoin, std::string hex); // CCcustom UniValue GatewaysInfo(uint256 bindtxid); diff --git a/src/cc/CCMofN.h b/src/cc/CCHeir.h similarity index 89% rename from src/cc/CCMofN.h rename to src/cc/CCHeir.h index 9b6c56b6f..9a8193428 100644 --- a/src/cc/CCMofN.h +++ b/src/cc/CCHeir.h @@ -14,16 +14,16 @@ ******************************************************************************/ -#ifndef CC_MOFN_H -#define CC_MOFN_H +#ifndef CC_HEIR_H +#define CC_HEIR_H #include "CCinclude.h" -#define EVAL_MOFN 0xea +#define EVAL_HEIR 0xea -bool MofNValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); +bool HeirValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCcustom -UniValue MofNInfo(); +UniValue HeirInfo(); #endif diff --git a/src/cc/CCPrices.h b/src/cc/CCPrices.h index 31b13e87b..55877cce0 100644 --- a/src/cc/CCPrices.h +++ b/src/cc/CCPrices.h @@ -22,6 +22,13 @@ bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCcustom -UniValue PricesInfo(); +UniValue PricesList(); +UniValue PricesInfo(uint256 fundingtxid); +UniValue PricesStatus(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,uint256 bettxid); +std::string PricesCreateFunding(uint64_t txfee,uint256 bettoken,uint256 oracletxid,uint64_t margin,uint64_t mode,uint256 longtoken,uint256 shorttoken,int32_t maxleverage,int64_t funding,std::vector pubkeys); +std::string PricesAddFunding(uint64_t txfee,uint256 bettoken,uint256 fundingtxid,int64_t amount); +std::string PricesBet(uint64_t txfee,uint256 bettoken,uint256 fundingtxid,int64_t amount,int32_t leverage); +std::string PricesFinish(uint64_t txfee,uint256 bettoken,uint256 fundingtxid,uint256 bettxid); + #endif diff --git a/src/cc/CCassets.h b/src/cc/CCassets.h index 9ed16ad02..a48195970 100644 --- a/src/cc/CCassets.h +++ b/src/cc/CCassets.h @@ -54,6 +54,8 @@ 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 destpubkey,int64_t total); +std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector 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); std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t fillamount); diff --git a/src/cc/CCassetsCore.cpp b/src/cc/CCassetsCore.cpp index 55a4ccdcb..c9b975c3c 100644 --- a/src/cc/CCassetsCore.cpp +++ b/src/cc/CCassetsCore.cpp @@ -367,7 +367,10 @@ int64_t IsAssetvout(int64_t &price,std::vector &origpubkey,const CTrans else if ( funcid != 'E' ) { if ( assetid == refassetid ) + { + //fprintf(stderr,"returning %.8f\n",(double)nValue/COIN); return(nValue); + } } else if ( funcid == 'E' ) { @@ -447,7 +450,7 @@ int64_t AssetValidateSellvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpp bool AssetExactAmounts(struct CCcontract_info *cp,int64_t &inputs,int32_t starti,int64_t &outputs,Eval* eval,const CTransaction &tx,uint256 assetid) { - CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; int64_t assetoshis; std::vector tmporigpubkey; int64_t tmpprice; + CTransaction vinTx; uint256 hashBlock,id,id2; int32_t i,flag,numvins,numvouts; int64_t assetoshis; std::vector tmporigpubkey; int64_t tmpprice; numvins = tx.vin.size(); numvouts = tx.vout.size(); inputs = outputs = 0; @@ -465,8 +468,20 @@ bool AssetExactAmounts(struct CCcontract_info *cp,int64_t &inputs,int32_t starti fprintf(stderr,"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,"vin%d %llu special case, ",i,(long long)assetoshis); + inputs += assetoshis; + } + } } } + if ( DecodeAssetOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,id,id2,tmpprice,tmporigpubkey) == 't' && id == assetid ) + flag = 1; + else flag = 0; for (i=0; i origpubkey; CTransaction vintx; int32_t j,vout,n = 0; + char coinaddr[64],destaddr[64]; int64_t threshold,nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t j,vout,n = 0; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); SetCCunspents(unspentOutputs,coinaddr); - + threshold = total/(maxinputs!=0?maxinputs:64); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; + if ( it->second.satoshis < threshold ) + continue; for (j=0; j des return(""); } +std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector destpubkey,int64_t total,int32_t evalcode) +{ + CMutableTransaction mtx; CPubKey mypk; int64_t CCchange=0,inputs=0; struct CCcontract_info *cp,C; + if ( total < 0 ) + { + fprintf(stderr,"negative total %lld\n",(long long)total); + return(""); + } + cp = CCinit(&C,EVAL_ASSETS); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + if ( AddNormalinputs(mtx,mypk,txfee,1) > 0 ) + { + if ( (inputs= AddAssetInputs(cp,mtx,mypk,assetid,total,60)) > 0 ) + { + if ( inputs > total ) + CCchange = (inputs - total); + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk)); + mtx.vout.push_back(MakeCC1vout(evalcode,total,pubkey2pk(destpubkey))); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeAssetOpRet('t',assetid,zeroid,0,Mypubkey()))); + } 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) { CMutableTransaction mtx; CPubKey mypk; struct CCcontract_info *cp,C; uint256 hashBlock; CTransaction vintx; std::vector origpubkey; std::string name,description; diff --git a/src/cc/CCchannels.h b/src/cc/CCchannels.h index 30365a379..7715a31b8 100644 --- a/src/cc/CCchannels.h +++ b/src/cc/CCchannels.h @@ -22,12 +22,11 @@ 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 ChannelStop(uint64_t txfee,CPubKey destpub,uint256 origtxid); -std::string ChannelPayment(uint64_t txfee,uint256 prevtxid,uint256 origtxid,int32_t n,int64_t amount); -std::string ChannelCollect(uint64_t txfee,uint256 paytxid,uint256 origtxid,int32_t n,int64_t amount); -std::string ChannelRefund(uint64_t txfee,uint256 stoptxid,uint256 origtxid); +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); // CCcustom -UniValue ChannelsInfo(); +UniValue ChannelsInfo(uint256 opentxid); #endif diff --git a/src/cc/CCcustom.cpp b/src/cc/CCcustom.cpp index ae9188eea..b17a7cd89 100644 --- a/src/cc/CCcustom.cpp +++ b/src/cc/CCcustom.cpp @@ -22,7 +22,7 @@ #include "CCauction.h" #include "CClotto.h" #include "CCfsm.h" -#include "CCMofN.h" +#include "CCHeir.h" #include "CCchannels.h" #include "CCOracles.h" #include "CCPrices.h" @@ -30,7 +30,6 @@ #include "CCTriggers.h" #include "CCPayments.h" #include "CCGateways.h" -#include "StakeGuard.h" /* CCcustom has most of the functions that need to be extended to create a new CC contract. @@ -56,11 +55,6 @@ // to create a new CCaddr, add to rpcwallet the CCaddress and start with -pubkey= with the pubkey of the new address, with its wif already imported. set normaladdr and CChexstr. run CCaddress and it will print the privkey along with autocorrect the CCaddress. which should then update the CCaddr here -// StakeGuard - nothing at stake -std::string StakeGuardAddr = "RCG8KwJNDVwpUBcdoa6AoHqHVJsA1uMYMR"; -std::string StakeGuardPubKey = "03166b7813a4855a88e9ef7340a692ef3c2decedfdc2c7563ec79537e89667d935"; -std::string StakeGuardWIF = "Uw7vRYHGKjyi1FaJ8Lv1USSuj7ntUti8fAhSDiCdbzuV6yDagaTn"; - // Assets, aka Tokens #define FUNCNAME IsAssetsInput #define EVALCODE EVAL_ASSETS @@ -140,13 +134,13 @@ uint8_t AuctionCCpriv[32] = { 0x8c, 0x1b, 0xb7, 0x8c, 0x02, 0xa3, 0x9d, 0x21, 0x #undef FUNCNAME #undef EVALCODE -// MofN -#define FUNCNAME IsMofNInput -#define EVALCODE EVAL_MOFN -const char *MofNCCaddr = "RDVHcSekmXgeYBqRupNTmqo3Rn8QRXNduy"; -const char *MofNNormaladdr = "RTPwUjKYECcGn6Y4KYChLhgaht1RSU4jwf"; -char MofNCChexstr[67] = { "03c91bef3d7cc59c3a89286833a3446b29e52a5e773f738a1ad2b09785e5f4179e" }; -uint8_t MofNCCpriv[32] = { 0x9d, 0xa1, 0xf8, 0xf7, 0xba, 0x0a, 0x91, 0x36, 0x89, 0x9a, 0x86, 0x30, 0x63, 0x20, 0xd7, 0xdf, 0xaa, 0x35, 0xe3, 0x99, 0x32, 0x2b, 0x63, 0xc0, 0x66, 0x9c, 0x93, 0xc4, 0x5e, 0x9d, 0xb9, 0xce }; +// Heir +#define FUNCNAME IsHeirInput +#define EVALCODE EVAL_HEIR +const char *HeirCCaddr = "RDVHcSekmXgeYBqRupNTmqo3Rn8QRXNduy"; +const char *HeirNormaladdr = "RTPwUjKYECcGn6Y4KYChLhgaht1RSU4jwf"; +char HeirCChexstr[67] = { "03c91bef3d7cc59c3a89286833a3446b29e52a5e773f738a1ad2b09785e5f4179e" }; +uint8_t HeirCCpriv[32] = { 0x9d, 0xa1, 0xf8, 0xf7, 0xba, 0x0a, 0x91, 0x36, 0x89, 0x9a, 0x86, 0x30, 0x63, 0x20, 0xd7, 0xdf, 0xaa, 0x35, 0xe3, 0x99, 0x32, 0x2b, 0x63, 0xc0, 0x66, 0x9c, 0x93, 0xc4, 0x5e, 0x9d, 0xb9, 0xce }; #include "CCcustom.inc" #undef FUNCNAME #undef EVALCODE @@ -221,7 +215,7 @@ uint8_t PaymentsCCpriv[32] = { 0x03, 0xc9, 0x73, 0xc2, 0xb8, 0x30, 0x3d, 0xbd, 0 #define FUNCNAME IsGatewaysInput #define EVALCODE EVAL_GATEWAYS const char *GatewaysCCaddr = "RKWpoK6vTRtq5b9qrRBodLkCzeURHeEk33"; -const char *GatewaysNormaladdr = "RGJKV97ZN1wBfunuMt1tebiiHENNEq73Yh"; +const char *GatewaysNormaladdr = "RGJKV97ZN1wBfunuMt1tebiiHENNEq73Yh"; // wif UxJFYqEvLAjWPPRvn8NN1fRWscBxQZXZB5BBgc3HiapKVQBYNcmo char GatewaysCChexstr[67] = { "03ea9c062b9652d8eff34879b504eda0717895d27597aaeb60347d65eed96ccb40" }; uint8_t GatewaysCCpriv[32] = { 0xf7, 0x4b, 0x5b, 0xa2, 0x7a, 0x5e, 0x9c, 0xda, 0x89, 0xb1, 0xcb, 0xb9, 0xe6, 0x9c, 0x2c, 0x70, 0x85, 0x37, 0xdd, 0x00, 0x7a, 0x67, 0xff, 0x7c, 0x62, 0x1b, 0xe2, 0xfb, 0x04, 0x8f, 0x85, 0xbf }; #include "CCcustom.inc" @@ -233,15 +227,6 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode) cp->evalcode = evalcode; switch ( evalcode ) { - case EVAL_STAKEGUARD: - strcpy(cp->unspendableCCaddr,StakeGuardAddr.c_str()); - strcpy(cp->normaladdr,StakeGuardAddr.c_str()); - strcpy(cp->CChexstr,StakeGuardPubKey.c_str()); - memcpy(cp->CCpriv,DecodeSecret(StakeGuardWIF).begin(),32); - cp->validate = StakeGuardValidate; - cp->ismyvin = IsStakeGuardInput; - break; - case EVAL_ASSETS: strcpy(cp->unspendableCCaddr,AssetsCCaddr); strcpy(cp->normaladdr,AssetsNormaladdr); @@ -298,13 +283,13 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode) cp->validate = AuctionValidate; cp->ismyvin = IsAuctionInput; break; - case EVAL_MOFN: - strcpy(cp->unspendableCCaddr,MofNCCaddr); - strcpy(cp->normaladdr,MofNNormaladdr); - strcpy(cp->CChexstr,MofNCChexstr); - memcpy(cp->CCpriv,MofNCCpriv,32); - cp->validate = MofNValidate; - cp->ismyvin = IsMofNInput; + case EVAL_HEIR: + strcpy(cp->unspendableCCaddr,HeirCCaddr); + strcpy(cp->normaladdr,HeirNormaladdr); + strcpy(cp->CChexstr,HeirCChexstr); + memcpy(cp->CCpriv,HeirCCpriv,32); + cp->validate = HeirValidate; + cp->ismyvin = IsHeirInput; break; case EVAL_CHANNELS: strcpy(cp->unspendableCCaddr,ChannelsCCaddr); diff --git a/src/cc/CCdice.h b/src/cc/CCdice.h index 92e2bc0cd..c868cc705 100644 --- a/src/cc/CCdice.h +++ b/src/cc/CCdice.h @@ -24,7 +24,7 @@ bool DiceValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); std::string DiceBet(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t bet,int32_t odds); -std::string DiceBetFinish(int32_t *resultp,uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid,int32_t winlosetimeout); +std::string DiceBetFinish(uint8_t &funcid,uint256 &entropyused,int32_t &entropyvout,int32_t *resultp,uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid,int32_t winlosetimeout,uint256 vin0txid,int32_t vin0vout); double DiceStatus(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid); std::string DiceCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks); std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount); diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index a1e433fdd..45025d274 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -48,12 +48,16 @@ one other technical note is that komodod has the insight-explorer extensions bui #include #include #include "../komodo_defs.h" +#include "../utlist.h" +#include "../uthash.h" extern int32_t KOMODO_CONNECTING,KOMODO_CCACTIVATE; extern uint32_t ASSETCHAINS_CC; +extern char ASSETCHAINS_SYMBOL[]; extern std::string CCerror; #define SMALLVAL 0.000000000000001 +#define MIN_NOTARIZATION_CONFIRMS 2 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; @@ -104,8 +108,8 @@ int32_t is_hexstr(char *str,int32_t n); bool myAddtomempool(CTransaction &tx, CValidationState *pstate = NULL); //uint64_t myGettxout(uint256 hash,int32_t n); bool myIsutxo_spentinmempool(uint256 txid,int32_t vout); +bool mytxid_inmempool(uint256 txid); int32_t myIsutxo_spent(uint256 &spenttxid,uint256 txid,int32_t vout); -bool mySendrawtransaction(std::string res); int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex); int32_t iguana_rwnum(int32_t rwflag,uint8_t *serialized,int32_t len,void *endianedp); int32_t iguana_rwbignum(int32_t rwflag,uint8_t *serialized,int32_t len,uint8_t *endianedp); @@ -133,24 +137,26 @@ CPubKey GetUnspendable(struct CCcontract_info *cp,uint8_t *unspendablepriv); // CCutils CPubKey buf2pk(uint8_t *buf33); void endiancpy(uint8_t *dest,uint8_t *src,int32_t len); -uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv); +uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv,int32_t entropyvout,int32_t usevout); CTxOut MakeCC1vout(uint8_t evalcode,CAmount nValue,CPubKey pk); CTxOut MakeCC1of2vout(uint8_t evalcode,CAmount nValue,CPubKey pk,CPubKey pk2); CC *MakeCCcond1(uint8_t evalcode,CPubKey pk); CC *MakeCCcond1of2(uint8_t evalcode,CPubKey pk1,CPubKey pk2); -CC *GetCryptoCondition(CScript const& scriptSig); +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); bool IsCCInput(CScript const& scriptSig); int32_t unstringbits(char *buf,uint64_t bits); uint64_t stringbits(char *str); uint256 revuint256(uint256 txid); +bool pubkey2addr(char *destaddr,uint8_t *pubkey33); char *uint256_str(char *dest,uint256 txid); char *pubkey33_str(char *dest,uint8_t *pubkey33); uint256 Parseuint256(char *hexstr); CPubKey pubkey2pk(std::vector pubkey); int64_t CCfullsupply(uint256 tokenid); int64_t CCtoken_balance(char *destaddr,uint256 tokenid); +int64_t CCtoken_balance2(char *destaddr,uint256 tokenid); bool _GetCCaddress(char *destaddr,uint8_t evalcode,CPubKey pk); bool GetCCaddress(struct CCcontract_info *cp,char *destaddr,CPubKey pk); bool GetCCaddress1of2(struct CCcontract_info *cp,char *destaddr,CPubKey pk,CPubKey pk2); @@ -160,7 +166,7 @@ bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey); std::vector Mypubkey(); bool Myprivkey(uint8_t myprivkey[]); int64_t CCduration(int32_t &numblocks,uint256 txid); - +bool isCCTxNotarizedConfirmed(uint256 txid); // 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); diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index fa3c8df01..2f2a86957 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -18,12 +18,12 @@ /* FinalizeCCTx is a very useful function that will properly sign both CC and normal inputs, adds normal change and the opreturn. - + This allows the contract transaction functions to create the appropriate vins and vouts and have FinalizeCCTx create a properly signed transaction. - + By using -addressindex=1, it allows tracking of all the CC addresses */ - + bool SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScript scriptPubKey) { #ifdef ENABLE_WALLET @@ -34,9 +34,8 @@ bool SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScrip UpdateTransaction(mtx,vini,sigdata); return(true); } else fprintf(stderr,"signing error for SignTx vini.%d %.8f\n",vini,(double)utxovalue/COIN); -#else - return(false); #endif + return(false); } std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey mypk,uint64_t txfee,CScript opret) @@ -112,7 +111,7 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran { privkey = myprivkey; cond = mycond; - //fprintf(stderr,"my CC addr.(%s)\n",myaddr); + } else if ( strcmp(destaddr,unspendable) == 0 ) { @@ -120,12 +119,14 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran cond = othercond; //fprintf(stderr,"unspendable CC addr.(%s)\n",unspendable); } - else if ( strcmp(destaddr,cp->unspendableaddr2) == 0 ) + else if ( strcmp(destaddr,cp->unspendableaddr2) == 0) { //fprintf(stderr,"matched %s unspendable2!\n",cp->unspendableaddr2); privkey = cp->unspendablepriv2; - if ( othercond2 == 0 ) + if ( othercond2 == 0 && cp->evalcode != EVAL_CHANNELS) othercond2 = MakeCCcond1(cp->evalcode2,cp->unspendablepk2); + else if ( othercond2 == 0 && cp->evalcode == EVAL_CHANNELS) + othercond2 = MakeCCcond1of2(cp->evalcode2,cp->unspendablepk2,cp->unspendablepk3); cond = othercond2; } else if ( strcmp(destaddr,cp->unspendableaddr3) == 0 ) @@ -138,7 +139,7 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran } else { - fprintf(stderr,"vini.%d has unknown CC address.(%s)\n",i,destaddr); + 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); @@ -251,13 +252,15 @@ 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 origpubkey; std::vector > unspentOutputs; + int64_t price,sum = 0; int32_t numvouts; CTransaction tx; uint256 assetid,assetid2,txid,hashBlock; std::vector origpubkey; + std::vector > unspentOutputs; SetCCunspents(unspentOutputs,coinaddr); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; 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 ) { sum += it->second.satoshis; @@ -273,6 +276,11 @@ int32_t CC_vinselect(int32_t *aboveip,int64_t *abovep,int32_t *belowip,int64_t * abovei = belowi = -1; for (above=below=i=0; i 500 ) { + // if ( (rand() % 100) < 80 ) + // continue; + //} if ( (atx_value= utxos[i].nValue) <= 0 ) continue; if ( atx_value == value ) @@ -319,21 +327,26 @@ int32_t CC_vinselect(int32_t *aboveip,int64_t *abovep,int32_t *belowip,int64_t * int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int32_t maxinputs) { - int32_t abovei,belowi,ind,vout,i,n = 0,maxutxos=1024; int64_t above,below; int64_t remains,nValue,totalinputs = 0; uint256 txid,hashBlock; std::vector vecOutputs; CTransaction tx; struct CC_utxo *utxos,*up; + 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 vecOutputs; CTransaction tx; struct CC_utxo *utxos,*up; #ifdef ENABLE_WALLET const CKeyStore& keystore = *pwalletMain; assert(pwalletMain != NULL); LOCK2(cs_main, pwalletMain->cs_wallet); pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); utxos = (struct CC_utxo *)calloc(maxutxos,sizeof(*utxos)); + threshold = total/maxinputs; + if ( maxinputs > maxutxos ) + maxutxos = maxinputs; + sum = 0; BOOST_FOREACH(const COutput& out, vecOutputs) { - if ( out.fSpendable != 0 ) + if ( out.fSpendable != 0 && out.tx->vout[out.i].nValue >= threshold ) { txid = out.tx->GetHash(); vout = out.i; - if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && vout < tx.vout.size() && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() == 0 ) + if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 && vout < tx.vout.size() && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() == 0 ) { + //fprintf(stderr,"check %.8f to vins array.%d of %d %s/v%d\n",(double)out.tx->vout[out.i].nValue/COIN,n,maxutxos,txid.GetHex().c_str(),(int32_t)vout); if ( mtx.vin.size() > 0 ) { for (i=0; itxid = txid; up->nValue = out.tx->vout[out.i].nValue; up->vout = vout; + sum += up->nValue; //fprintf(stderr,"add %.8f to vins array.%d of %d\n",(double)up->nValue/COIN,n,maxutxos); - if ( n >= maxutxos ) + if ( n >= maxutxos || sum >= total ) break; } } diff --git a/src/cc/CCutils.cpp b/src/cc/CCutils.cpp index b28f679da..3ce1d5eeb 100644 --- a/src/cc/CCutils.cpp +++ b/src/cc/CCutils.cpp @@ -74,6 +74,7 @@ CC* GetCryptoCondition(CScript const& scriptSig) std::vector ffbin; if (scriptSig.GetOp(pc, opcode, ffbin)) return cc_readFulfillmentBinary((uint8_t*)ffbin.data(), ffbin.size()-1); + else return(0); } bool IsCCInput(CScript const& scriptSig) @@ -238,6 +239,16 @@ bool GetCCParams(Eval* eval, const CTransaction &tx, uint32_t nIn, } } return false; + //fprintf(stderr,"ExtractDestination failed\n"); + return(false); +} + +bool pubkey2addr(char *destaddr,uint8_t *pubkey33) +{ + std::vectorpk; int32_t i; + for (i=0; i<33; i++) + pk.push_back(pubkey33[i]); + return(Getscriptaddress(destaddr,CScript() << pk << OP_CHECKSIG)); } CPubKey CCtxidaddr(char *txidaddr,uint256 txid) @@ -420,12 +431,12 @@ int64_t CCduration(int32_t &numblocks,uint256 txid) numblocks = 0; if ( myGetTransaction(txid,tx,hashBlock) == 0 ) { - fprintf(stderr,"CCduration cant find duration txid %s\n",uint256_str(str,txid)); + //fprintf(stderr,"CCduration cant find duration txid %s\n",uint256_str(str,txid)); return(0); } else if ( hashBlock == zeroid ) { - fprintf(stderr,"CCduration no hashBlock for txid %s\n",uint256_str(str,txid)); + //fprintf(stderr,"CCduration no hashBlock for txid %s\n",uint256_str(str,txid)); return(0); } else if ( (pindex= mapBlockIndex[hashBlock]) == 0 || (txtime= pindex->nTime) == 0 || (txheight= pindex->GetHeight()) <= 0 ) @@ -435,12 +446,22 @@ int64_t CCduration(int32_t &numblocks,uint256 txid) } else if ( (pindex= chainActive.LastTip()) == 0 || pindex->nTime < txtime || pindex->GetHeight() <= txheight ) { - fprintf(stderr,"CCduration backwards timestamps %u %u for txid %s hts.(%d %d)\n",(uint32_t)pindex->nTime,txtime,uint256_str(str,txid),txheight,(int32_t)pindex->GetHeight()); + if ( pindex->nTime < txtime ) + fprintf(stderr,"CCduration backwards timestamps %u %u for txid %s hts.(%d %d)\n",(uint32_t)pindex->nTime,txtime,uint256_str(str,txid),txheight,(int32_t)pindex->GetHeight()); return(0); } numblocks = (pindex->GetHeight() - txheight); duration = (pindex->nTime - txtime); - fprintf(stderr,"duration %d (%u - %u) numblocks %d (%d - %d)\n",(int32_t)duration,(uint32_t)pindex->nTime,txtime,numblocks,pindex->GetHeight(),txheight); + //fprintf(stderr,"duration %d (%u - %u) numblocks %d (%d - %d)\n",(int32_t)duration,(uint32_t)pindex->nTime,txtime,numblocks,pindex->GetHeight(),txheight); return(duration); } +bool isCCTxNotarizedConfirmed(uint256 txid) +{ + int32_t confirms; + + CCduration(confirms,txid); + if (confirms >= MIN_NOTARIZATION_CONFIRMS) + return (true); + return (false); +} diff --git a/src/cc/StakeGuard.cpp b/src/cc/StakeGuard.cpp deleted file mode 100644 index dbc14ebde..000000000 --- a/src/cc/StakeGuard.cpp +++ /dev/null @@ -1,460 +0,0 @@ -/******************************************************************** - * (C) 2018 Michael Toutonghi - * - * Distributed under the MIT software license, see the accompanying - * file COPYING or http://www.opensource.org/licenses/mit-license.php. - * - * This crypto-condition eval solves the problem of nothing-at-stake - * in a proof of stake consensus system. - * - */ - -#include "StakeGuard.h" -#include "script/script.h" -#include "main.h" -#include "hash.h" - -#include "streams.h" - -extern int32_t VERUS_MIN_STAKEAGE; - -bool IsData(opcodetype opcode) -{ - return (opcode >= 0 && opcode <= OP_PUSHDATA4) || (opcode >= OP_1 && opcode <= OP_16); -} - -bool UnpackStakeOpRet(const CTransaction &stakeTx, std::vector> &vData) -{ - bool isValid = stakeTx.vout[stakeTx.vout.size() - 1].scriptPubKey.GetOpretData(vData); - - if (isValid && vData.size() == 1) - { - CScript data = CScript(vData[0].begin(), vData[0].end()); - vData.clear(); - - uint32_t bytesTotal; - CScript::const_iterator pc = data.begin(); - std::vector vch = std::vector(); - opcodetype op; - bool moreData = true; - - for (bytesTotal = vch.size(); - bytesTotal <= nMaxDatacarrierBytes && !(isValid = (pc == data.end())) && (moreData = data.GetOp(pc, op, vch)) && IsData(op); - bytesTotal += vch.size()) - { - if (op >= OP_1 && op <= OP_16) - { - vch.resize(1); - vch[0] = (op - OP_1) + 1; - } - vData.push_back(vch); - } - - // if we ran out of data, we're ok - if (isValid && (vData.size() >= CStakeParams::STAKE_MINPARAMS) && (vData.size() <= CStakeParams::STAKE_MAXPARAMS)) - { - return true; - } - } - return false; -} - -CStakeParams::CStakeParams(const std::vector> &vData) -{ - // A stake OP_RETURN contains: - // 1. source block height in little endian 32 bit - // 2. target block height in little endian 32 bit - // 3. 32 byte prev block hash - // 4. 33 byte pubkey, or not present to use same as stake destination - - srcHeight = 0; - blkHeight = 0; - if (vData[0].size() == 1 && - vData[0][0] == OPRETTYPE_STAKEPARAMS && vData[1].size() <= 4 && - vData[2].size() <= 4 && - vData[3].size() == sizeof(prevHash) && - (vData.size() == STAKE_MINPARAMS || (vData.size() == STAKE_MAXPARAMS && vData[4].size() == 33))) - { - for (int i = 0, size = vData[1].size(); i < size; i++) - { - srcHeight = srcHeight | vData[1][i] << (8 * i); - } - for (int i = 0, size = vData[2].size(); i < size; i++) - { - blkHeight = blkHeight | vData[2][i] << (8 * i); - } - - prevHash = uint256(vData[3]); - - if (vData.size() == 4) - { - pk = CPubKey(); - } - else if (vData[4].size() == 33) - { - pk = CPubKey(vData[4]); - if (!pk.IsValid()) - { - // invalidate - srcHeight = 0; - } - } - else - { - // invalidate - srcHeight = 0; - } - } -} - -bool GetStakeParams(const CTransaction &stakeTx, CStakeParams &stakeParams) -{ - std::vector> vData = std::vector>(); - - //printf("opret stake script: %s\nvalue at scriptPubKey[0]: %x\n", stakeTx.vout[1].scriptPubKey.ToString().c_str(), stakeTx.vout[1].scriptPubKey[0]); - - if (stakeTx.vin.size() == 1 && - stakeTx.vout.size() == 2 && - stakeTx.vout[0].nValue > 0 && - stakeTx.vout[1].scriptPubKey.IsOpReturn() && - UnpackStakeOpRet(stakeTx, vData)) - { - stakeParams = CStakeParams(vData); - return stakeParams.IsValid(); - } - return false; -} - -// this validates the format of the stake transaction and, optionally, whether or not it is -// properly signed to spend the source stake. -// it does not validate the relationship to a coinbase guard, PoS eligibility or the actual stake spend. -// the only time it matters -// is to validate a properly formed stake transaction for either pre-check before PoS validity check, or to -// validate the stake transaction on a fork that will be used to spend a winning stake that cheated by being posted -// on two fork chains -bool ValidateStakeTransaction(const CTransaction &stakeTx, CStakeParams &stakeParams, bool validateSig) -{ - std::vector> vData = std::vector>(); - - // a valid stake transaction has one input and two outputs, one output is the monetary value and one is an op_ret with CStakeParams - // stake output #1 must be P2PK or P2PKH, unless a delegate for the coinbase is specified - if (GetStakeParams(stakeTx, stakeParams)) - { - // if we have gotten this far and are still valid, we need to validate everything else - // even if the utxo is spent, this can succeed, as it only checks that is was ever valid - CTransaction srcTx = CTransaction(); - uint256 blkHash = uint256(); - txnouttype txType; - CBlockIndex *pindex; - if (myGetTransaction(stakeTx.vin[0].prevout.hash, srcTx, blkHash)) - { - BlockMap::const_iterator it = mapBlockIndex.find(blkHash); - if (it != mapBlockIndex.end() && (pindex = it->second) != NULL) - { - std::vector> vAddr = std::vector>(); - - if (stakeParams.srcHeight == pindex->GetHeight() && - (stakeParams.blkHeight - stakeParams.srcHeight >= VERUS_MIN_STAKEAGE) && - Solver(srcTx.vout[stakeTx.vin[0].prevout.n].scriptPubKey, txType, vAddr)) - { - if (txType == TX_PUBKEY && !stakeParams.pk.IsValid()) - { - stakeParams.pk = CPubKey(vAddr[0]); - } - if ((txType == TX_PUBKEY) || (txType == TX_PUBKEYHASH && stakeParams.pk.IsFullyValid())) - { - auto consensusBranchId = CurrentEpochBranchId(stakeParams.blkHeight, Params().GetConsensus()); - - if (!validateSig || VerifyScript(stakeTx.vin[0].scriptSig, - srcTx.vout[stakeTx.vin[0].prevout.n].scriptPubKey, - MANDATORY_SCRIPT_VERIFY_FLAGS, - TransactionSignatureChecker(&stakeTx, (uint32_t)0, srcTx.vout[stakeTx.vin[0].prevout.n].nValue), - consensusBranchId)) - { - return true; - } - } - } - } - } - } - return false; -} - -bool MakeGuardedOutput(CAmount value, CPubKey &dest, CTransaction &stakeTx, CTxOut &vout) -{ - CCcontract_info *cp, C; - cp = CCinit(&C,EVAL_STAKEGUARD); - - CPubKey ccAddress = CPubKey(ParseHex(cp->CChexstr)); - - // return an output that is bound to the stake transaction and can be spent by presenting either a signed condition by the original - // destination address or a properly signed stake transaction of the same utxo on a fork - vout = MakeCC1of2vout(EVAL_STAKEGUARD, value, dest, ccAddress); - - std::vector vPubKeys = std::vector(); - vPubKeys.push_back(dest); - vPubKeys.push_back(ccAddress); - - std::vector> vData = std::vector>(); - - CVerusHashWriter hw = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION); - - hw << stakeTx.vin[0].prevout.hash; - hw << stakeTx.vin[0].prevout.n; - - uint256 utxo = hw.GetHash(); - vData.push_back(std::vector(utxo.begin(), utxo.end())); - - CStakeParams p; - if (GetStakeParams(stakeTx, p)) - { - // prev block hash and height is here to make validation easy - vData.push_back(std::vector(p.prevHash.begin(), p.prevHash.end())); - std::vector height = std::vector(4); - for (int i = 0; i < 4; i++) - { - height[i] = (p.blkHeight >> (8 * i)) & 0xff; - } - vData.push_back(height); - - COptCCParams ccp = COptCCParams(COptCCParams::VERSION, EVAL_STAKEGUARD, 1, 2, vPubKeys, vData); - - vout.scriptPubKey << ccp.AsVector() << OP_DROP; - return true; - } - return false; -} - -// validates if a stake transaction is both valid and cheating, defined by: -// the same exact utxo source, a target block height of later than that of this tx that is also targeting a fork -// of the chain. we know the transaction is a coinbase -bool ValidateMatchingStake(const CTransaction &ccTx, uint32_t voutNum, const CTransaction &stakeTx, bool &cheating) -{ - // an invalid or non-matching stake transaction cannot cheat - cheating = false; - - //printf("ValidateMatchingStake: ccTx.vin[0].prevout.hash: %s, ccTx.vin[0].prevout.n: %d\n", ccTx.vin[0].prevout.hash.GetHex().c_str(), ccTx.vin[0].prevout.n); - - if (ccTx.IsCoinBase()) - { - CStakeParams p; - if (ValidateStakeTransaction(stakeTx, p)) - { - std::vector> vParams = std::vector>(); - CScript dummy; - - if (ccTx.vout[voutNum].scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) && vParams.size() > 0) - { - COptCCParams ccp = COptCCParams(vParams[0]); - if (ccp.IsValid() & ccp.vData.size() >= 3 && ccp.vData[2].size() <= 4) - { - CVerusHashWriter hw = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION); - - hw << stakeTx.vin[0].prevout.hash; - hw << stakeTx.vin[0].prevout.n; - uint256 utxo = hw.GetHash(); - - uint32_t height = 0; - int i, dataLen = ccp.vData[2].size(); - for (i = dataLen - 1; i >= 0; i--) - { - height = (height << 8) + ccp.vData[2][i]; - } - // for debugging strange issue - // printf("iterator: %d, height: %d, datalen: %d\n", i, height, dataLen); - - if (utxo == uint256(ccp.vData[0])) - { - if (p.prevHash != uint256(ccp.vData[1]) && p.blkHeight >= height) - { - cheating = true; - return true; - } - // if block height is equal and we are at the else, prevHash must have been equal - else if (p.blkHeight == height) - { - return true; - } - } - } - } - } - } - return false; -} - -// this attaches an opret to a mutable transaction that provides the necessary evidence of a signed, cheating stake transaction -bool MakeCheatEvidence(CMutableTransaction &mtx, const CTransaction &ccTx, uint32_t voutNum, const CTransaction &cheatTx) -{ - std::vector vch; - CDataStream s = CDataStream(SER_DISK, CLIENT_VERSION); - bool isCheater = false; - - if (ValidateMatchingStake(ccTx, voutNum, cheatTx, isCheater) && isCheater) - { - CTxOut vOut = CTxOut(); - int64_t opretype_stakecheat = OPRETTYPE_STAKECHEAT; - - CScript vData = CScript(); - cheatTx.Serialize(s); - vch = std::vector(s.begin(), s.end()); - vData << opretype_stakecheat << vch; - vch = std::vector(vData.begin(), vData.end()); - vOut.scriptPubKey << OP_RETURN << vch; - - // printf("Script encoding inner:\n%s\nouter:\n%s\n", vData.ToString().c_str(), vOut.scriptPubKey.ToString().c_str()); - - vOut.nValue = 0; - mtx.vout.push_back(vOut); - } - return isCheater; -} - -typedef struct ccFulfillmentCheck { - std::vector &vPK; - std::vector &vCount; -} ccFulfillmentCheck; - -// to figure out which node is signed -int CCFulfillmentVisitor(CC *cc, struct CCVisitor visitor) -{ - //printf("cc_typeName: %s, cc_isFulfilled: %x, cc_isAnon: %x, cc_typeMask: %x, cc_condToJSONString:\n%s\n", - // cc_typeName(cc), cc_isFulfilled(cc), cc_isAnon(cc), cc_typeMask(cc), cc_conditionToJSONString(cc)); - - if (strcmp(cc_typeName(cc), "secp256k1-sha-256") == 0) - { - cJSON *json = cc_conditionToJSON(cc); - if (json) - { - cJSON *pubKeyNode = json->child->next; - if (strcmp(pubKeyNode->string, "publicKey") == 0) - { - ccFulfillmentCheck *pfc = (ccFulfillmentCheck *)(visitor.context); - - //printf("public key: %s\n", pubKeyNode->valuestring); - CPubKey pubKey = CPubKey(ParseHex(pubKeyNode->valuestring)); - - for (int i = 0; i < pfc->vPK.size(); i++) - { - if (i < pfc->vCount.size() && (pfc->vPK[i] == pubKey)) - { - pfc->vCount[i]++; - } - } - } - cJSON_free(json); - } - } - - return 1; -} - -int IsCCFulfilled(CC *cc, ccFulfillmentCheck *ctx) -{ - struct CCVisitor visitor = {&CCFulfillmentVisitor, NULL, 0, (void *)ctx}; - cc_visit(cc, visitor); - - //printf("count key 1: %d, count key 2: %d\n", ctx->vCount[0], ctx->vCount[1]); - return ctx->vCount[0]; -} - -bool StakeGuardValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) -{ - // WARNING: this has not been tested combined with time locks - // validate this spend of a transaction with it being past any applicable time lock and one of the following statements being true: - // 1. the spend is signed by the original output destination's private key and normal payment requirements, spends as normal - // 2. the spend is signed by the private key of the StakeGuard contract and pushes a signed stake transaction - // with the same exact utxo source, a target block height of later than or equal to this tx, and a different prevBlock hash - - // first, check to see if the spending contract is signed by the default destination address - // if so, success and we are done - - // get preConditions and parameters - std::vector> preConditions = std::vector>(); - std::vector> params = std::vector>(); - CTransaction txOut; - - bool signedByFirstKey = false; - bool validCheat = false; - - CC *cc = GetCryptoCondition(tx.vin[nIn].scriptSig); - - if (cc) - { - COptCCParams ccp; - signedByFirstKey = false; - validCheat = false; - - // tx is the spending tx, the cc transaction comes back in txOut - if (GetCCParams(eval, tx, nIn, txOut, preConditions, params)) - { - if (preConditions.size() > 0) - { - ccp = COptCCParams(preConditions[0]); - } - - if (ccp.IsValid() && ccp.m == 1 && ccp.n == 2 && ccp.vKeys.size() == 2) - { - std::vector vc = {0, 0}; - ccFulfillmentCheck fc = {ccp.vKeys, vc}; - - signedByFirstKey = (IsCCFulfilled(cc, &fc) != 0); - - if ((!signedByFirstKey && ccp.evalCode == EVAL_STAKEGUARD && ccp.vKeys.size() == 2 && ccp.version == COptCCParams::VERSION) && - params.size() == 2 && params[0].size() > 0 && params[0][0] == OPRETTYPE_STAKECHEAT) - { - CDataStream s = CDataStream(std::vector(params[1].begin(), params[1].end()), SER_DISK, CLIENT_VERSION); - bool checkOK = false; - CTransaction cheatTx; - try - { - cheatTx.Unserialize(s); - checkOK = true; - } - catch (...) - { - } - if (checkOK && !ValidateMatchingStake(txOut, tx.vin[0].prevout.n, cheatTx, validCheat)) - { - validCheat = false; - } - } - } - } - cc_free(cc); - } - if (!(signedByFirstKey || validCheat)) - { - eval->Error("error reading coinbase or spending proof invalid\n"); - return false; - } - else return true; -} - -bool IsStakeGuardInput(const CScript &scriptSig) -{ - printf("IsStakeGuardInput: not implemented"); - return false; -} - -UniValue StakeGuardInfo() -{ - UniValue result(UniValue::VOBJ); char numstr[64]; - CMutableTransaction mtx; - CPubKey pk; - - CCcontract_info *cp,C; - - cp = CCinit(&C,EVAL_STAKEGUARD); - - result.push_back(Pair("result","success")); - result.push_back(Pair("name","StakeGuard")); - - // all UTXOs to the contract address that are to any of the wallet addresses are to us - // each is spendable as a normal transaction, but the spend may fail if it gets spent out - // from under us - pk = GetUnspendable(cp,0); - return(result); -} - diff --git a/src/cc/StakeGuard.h b/src/cc/StakeGuard.h deleted file mode 100644 index 824d09efb..000000000 --- a/src/cc/StakeGuard.h +++ /dev/null @@ -1,41 +0,0 @@ -/******************************************************************** - * (C) 2018 Michael Toutonghi - * - * Distributed under the MIT software license, see the accompanying - * file COPYING or http://www.opensource.org/licenses/mit-license.php. - * - * This crypto-condition eval solves the problem of nothing-at-stake - * in a proof of stake consensus system. - * - */ - -#ifndef STAKEGUARD_H -#define STAKEGUARD_H - -#include - -#include "CCinclude.h" -#include "streams.h" -#include "script/script.h" - -#define DEFAULT_STAKE_TXFEE 0 - -bool UnpackStakeOpRet(const CTransaction &stakeTx, std::vector> &vData); - -bool GetStakeParams(const CTransaction &stakeTx, CStakeParams &stakeParams); - -bool ValidateStakeTransaction(const CTransaction &stakeTx, CStakeParams &stakeParams, bool validateSig = true); - -bool ValidateMatchingStake(const CTransaction &ccTx, uint32_t voutNum, const CTransaction &stakeTx, bool &cheating); - -bool MakeGuardedOutput(CAmount value, CPubKey &dest, CTransaction &stakeTx, CTxOut &vout); - -bool MakeCheatEvidence(CMutableTransaction &mtx, const CTransaction &ccTx, uint32_t voutNum, const CTransaction &cheatTx); - -bool StakeGuardValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); - -bool IsStakeGuardInput(const CScript &scriptSig); - -UniValue StakeGuardInfo(); - -#endif diff --git a/src/cc/assets.cpp b/src/cc/assets.cpp index 5f856563d..41a0ed7b1 100644 --- a/src/cc/assets.cpp +++ b/src/cc/assets.cpp @@ -167,7 +167,6 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx //vout.n-1: opreturn [EVAL_ASSETS] ['c'] [{"":""}] return eval->Invalid("unexpected AssetValidate for createasset"); break; - case 't': // transfer //vin.0: normal input //vin.1 .. vin.n-1: valid CC outputs @@ -250,8 +249,8 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx } fprintf(stderr,"fillbuy validated\n"); break; - case 'e': // selloffer - break; // disable swaps + //case 'e': // selloffer + // break; // disable swaps case 's': // selloffer //vin.0: normal input //vin.1+: valid CC output for sale @@ -323,6 +322,7 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx fprintf(stderr,"fill validated\n"); break; case 'E': // fillexchange + return eval->Invalid("unexpected assets fillexchange funcid"); break; // disable asset swaps //vin.0: normal input //vin.1: unspendable.(vout.0 assetoshis from selloffer) sellTx.vout[0] @@ -372,6 +372,10 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx } fprintf(stderr,"fill validated\n"); break; + default: + fprintf(stderr,"illegal assets funcid.(%c)\n",funcid); + return eval->Invalid("unexpected assets funcid"); + break; } return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts)); } diff --git a/src/cc/auction.cpp b/src/cc/auction.cpp index a47785cb5..77650405e 100644 --- a/src/cc/auction.cpp +++ b/src/cc/auction.cpp @@ -120,6 +120,7 @@ bool AuctionValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t int64_t AddAuctionInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) { + // add threshold check char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t n = 0; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); diff --git a/src/cc/channels.cpp b/src/cc/channels.cpp index b6c221c85..914ca4ebb 100644 --- a/src/cc/channels.cpp +++ b/src/cc/channels.cpp @@ -62,46 +62,86 @@ Possible third iteration: // start of consensus code -int64_t IsChannelsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) +int64_t IsChannelsvout(struct CCcontract_info *cp,const CTransaction& tx,CPubKey srcpub, CPubKey destpub,int32_t v) { - char destaddr[64]; + char destaddr[65],channeladdr[65]; + + GetCCaddress1of2(cp,channeladdr,srcpub,destpub); if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) { - if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 ) + if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,channeladdr) == 0 ) return(tx.vout[v].nValue); } return(0); } +int64_t IsChannelsMarkervout(struct CCcontract_info *cp,const CTransaction& tx,CPubKey srcpub,int32_t v) +{ + char destaddr[65],ccaddr[65]; + + GetCCaddress(cp,ccaddr,srcpub); + if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) + { + if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,ccaddr) == 0 ) + return(tx.vout[v].nValue); + } + return(0); +} + +CScript EncodeChannelsOpRet(uint8_t funcid,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); + return(opret); +} + +uint8_t DecodeChannelsOpRet(const CScript &scriptPubKey,uint256 &opentxid, CPubKey &srcpub,CPubKey &destpub,int32_t &numpayments,int64_t &payment,uint256 &hashchain) +{ + std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); + if ( vopret.size() > 2 ) + { + script = (uint8_t *)vopret.data(); + if ( script[0] == EVAL_CHANNELS ) + { + if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> opentxid; ss >> srcpub; ss >> destpub; ss >> numpayments; ss >> payment; ss >> hashchain) != 0 ) + { + return(f); + } + } else fprintf(stderr,"script[0] %02x != EVAL_CHANNELS\n",script[0]); + } else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size()); + return(0); +} + bool ChannelsExactAmounts(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; + uint256 txid,param3; + 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(); - for (i=0; i 0 && DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey, txid, srcpub, destpub, param1, param2, param3)!=0) { - //fprintf(stderr,"vini.%d\n",i); - if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 ) + for (i=0; iGetTxUnconfirmed(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 Channels from mempool"); - if ( (assetoshis= IsChannelsvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) - inputs += assetoshis; + inputs += vinTx.vout[tx.vin[i].prevout.n].nValue; } } } + else + { + return eval->Invalid("invalid op_return data"); + } for (i=0; i > txids; + int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numpayments,p1,param1; bool retval; + uint256 txid,hashblock,p3,param3,opentxid,tmp_txid,genhashchain,hashchain; + uint8_t funcid,hash[32],hashdest[32]; + int64_t p2,param2,payment; + CPubKey srcpub, destpub; + CTransaction channelOpenTx,channelCloseTx,prevTx; + numvins = tx.vin.size(); numvouts = tx.vout.size(); preventCCvins = preventCCvouts = -1; @@ -123,15 +167,7 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & return eval->Invalid("no vouts"); else { - for (i=0; iInvalid("illegal normal vini"); - } - } - //fprintf(stderr,"check amounts\n"); - if ( ChannelsExactAmounts(cp,eval,tx,1,10000) == false ) + if (ChannelsExactAmounts(cp,eval,tx,1,10000) == false ) { fprintf(stderr,"Channelsget invalid amount\n"); return false; @@ -140,10 +176,188 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & { txid = tx.GetHash(); memcpy(hash,&txid,sizeof(hash)); + + if ( (funcid = DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey, opentxid, srcpub, destpub, param1, param2, param3)) != 0) + { + switch ( funcid ) + { + case 'O': + //vin.0: normal input + //vout.0: CC vout for channel funding on CC1of2 pubkey + //vout.1: CC vout marker to senders pubKey + //vout.2: CC vout marker to receiver pubkey + //vout.n-2: normal change + //vout.n-1: opreturn - 'O' zerotxid senderspubkey receiverspubkey totalnumberofpayments paymentamount hashchain + return eval->Invalid("unexpected ChannelsValidate for channelsopen!"); + case 'P': + //vin.0: normal input + //vin.1: CC input from channel funding + //vin.2: CC input from src marker + //vout.0: CC vout change to CC1of2 pubkey + //vout.1: CC vout marker to senders pubKey + //vout.2: CC vout marker to receiver pubkey + //vout.3: normal output of payment amount to receiver pubkey + //vout.n-2: normal change + //vout.n-1: opreturn - 'P' opentxid senderspubkey receiverspubkey depth numpayments secret + if (isCCTxNotarizedConfirmed(opentxid) == 0) + return eval->Invalid("channelOpen is not yet confirmed(notarised)!"); + else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for channelPayment!"); + else if ( IsCCInput(tx.vin[1].scriptSig) == 0 ) + return eval->Invalid("vin.1 is CC for channelPayment!"); + else if ( IsCCInput(tx.vin[2].scriptSig) == 0 ) + return eval->Invalid("vin.2 is CC for channelPayment!"); + else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) + return eval->Invalid("vout.0 is CC for channelPayment!"); + else if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) + return eval->Invalid("vout.1 is CC for channelPayment (marker to srcPub)!"); + else if ( tx.vout[2].scriptPubKey.IsPayToCryptoCondition() == 0 ) + return eval->Invalid("vout.2 is CC for channelPayment (marker to dstPub)!"); + else if ( 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) + return eval->Invalid("payment funds do not go to receiver!"); + else if ( param1 > CHANNELS_MAXPAYMENTS) + return eval->Invalid("too many payment increments!"); + else + { + 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') + return eval->Invalid("invalid channelopen OP_RETURN data!"); + endiancpy(hash, (uint8_t * ) & param3, 32); + for (i = 0; i < numpayments-param1; i++) + { + vcalc_sha256(0, hashdest, hash, 32); + memcpy(hash, hashdest, 32); + } + endiancpy((uint8_t*)&genhashchain,hashdest,32); + if (hashchain!=genhashchain) + return eval->Invalid("invalid secret for payment, does not reach final hashchain!"); + else if (tx.vout[3].nValue != param2*payment) + return eval->Invalid("vout amount does not match number_of_payments*payment!"); + } + 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) + 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!"); + else if (tx.vout[2].scriptPubKey != prevTx.vout[2].scriptPubKey) + return eval->Invalid("invalid destination for receiver marker!"); + else if (param1+param2!=p1) + return eval->Invalid("invalid payment depth!"); + else if (tx.vout[3].nValue > prevTx.vout[0].nValue) + return eval->Invalid("not enough funds in channel for that amount!"); + } + } + break; + case 'C': + //vin.0: normal input + //vin.1: CC input from channel funding + //vin.2: CC input from src marker + //vout.0: CC vout for channel funding + //vout.1: CC vout marker to senders pubKey + //vout.2: CC vout marker to receiver pubkey + //vout.n-2: normal change + //vout.n-1: opreturn - 'C' opentxid senderspubkey receiverspubkey 0 0 0 + if (isCCTxNotarizedConfirmed(opentxid) == 0) + return eval->Invalid("channelOpen is not yet confirmed(notarised)!"); + else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for channelClose!"); + else if ( IsCCInput(tx.vin[1].scriptSig) == 0 ) + return eval->Invalid("vin.1 is CC for channelClose!"); + else if ( IsCCInput(tx.vin[2].scriptSig) == 0 ) + return eval->Invalid("vin.2 is CC for channelClose!"); + else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) + return eval->Invalid("vout.0 is CC for channelClose!"); + else if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) + return eval->Invalid("vout.1 is CC for channelClose (marker to srcPub)!"); + else if ( tx.vout[2].scriptPubKey.IsPayToCryptoCondition() == 0 ) + return eval->Invalid("vout.2 is CC for channelClose (marker to dstPub)!"); + 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') + 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) + 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!"); + else if (tx.vout[2].scriptPubKey != prevTx.vout[2].scriptPubKey) + return eval->Invalid("invalid destination for receiver marker!"); + else if (tx.vout[0].nValue != prevTx.vout[0].nValue) + return eval->Invalid("invalid CC amount, amount must match funds in channel"); + } + break; + case 'R': + //vin.0: normal input + //vin.1: CC input from channel funding + //vin.2: CC input from src marker + //vout.0: CC vout marker to senders pubKey + //vout.1: CC vout marker to receiver pubKey + //vout.2: normal output of CC input to senders pubkey + //vout.n-2: normal change + //vout.n-1: opreturn - 'R' opentxid senderspubkey receiverspubkey numpayments payment closetxid + if (isCCTxNotarizedConfirmed(opentxid) == 0) + return eval->Invalid("channelOpen is not yet confirmed(notarised)!"); + else if (isCCTxNotarizedConfirmed(param3) == 0) + return eval->Invalid("channelClose is not yet confirmed(notarised)!"); + else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for channelRefund!"); + else if ( IsCCInput(tx.vin[1].scriptSig) == 0 ) + return eval->Invalid("vin.1 is CC for channelRefund!"); + else if ( IsCCInput(tx.vin[2].scriptSig) == 0 ) + return eval->Invalid("vin.2 is CC for channelRefund!"); + else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) + return eval->Invalid("vout.0 is CC for channelRefund (marker to srcPub)!"); + else if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 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) + 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') + 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') + 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!"); + else if (tx.vout[2].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) + 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!"); + else if (tx.vout[1].scriptPubKey != prevTx.vout[2].scriptPubKey) + return eval->Invalid("invalid destination for receiver marker!"); + else if (tx.vout[2].nValue != prevTx.vout[0].nValue) + return eval->Invalid("invalid amount, refund amount and funds in channel must match!"); + } + break; + default: + fprintf(stderr,"illegal channels funcid.(%c)\n",funcid); + return eval->Invalid("unexpected channels funcid"); + break; + } + } else return eval->Invalid("unexpected channels missing funcid"); retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); if ( retval != 0 ) - fprintf(stderr,"Channelsget validated\n"); - else fprintf(stderr,"Channelsget invalid\n"); + fprintf(stderr,"Channel tx validated\n"); + else fprintf(stderr,"Channel tx invalid\n"); return(retval); } } @@ -152,65 +366,76 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & // helper functions for rpc calls in rpcwallet.cpp -CScript EncodeChannelsOpRet(uint8_t funcid,CPubKey srcpub,CPubKey destpub,int32_t numpayments,int64_t payment,uint256 hashchain) +int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx, CTransaction openTx, uint256 &prevtxid) { - CScript opret; uint8_t evalcode = EVAL_CHANNELS; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << srcpub << destpub << numpayments << payment << hashchain); - return(opret); -} - -uint8_t DecodeChannelsOpRet(uint256 txid,const CScript &scriptPubKey,CPubKey &srcpub,CPubKey &destpub,int32_t &numpayments,int64_t &payment,uint256 &hashchain) -{ - std::vector vopret; uint8_t *script,e,f,funcid; - GetOpReturnData(scriptPubKey, vopret); - if ( vopret.size() > 2 ) - { - script = (uint8_t *)vopret.data(); - if ( script[0] == EVAL_CHANNELS ) - { - if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> srcpub; ss >> destpub; ss >> numpayments; ss >> payment; ss >> hashchain) != 0 ) - { - return(f); - } - } else fprintf(stderr,"script[0] %02x != EVAL_CHANNELS\n",script[0]); - } else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size()); - return(0); -} - -int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) -{ - char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; + char coinaddr[65]; int64_t param2,totalinputs = 0,numvouts; uint256 txid=zeroid,tmp_txid,hashBlock,param3; CTransaction tx; int32_t param1; std::vector > unspentOutputs; - GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr); + 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') + { + GetCCaddress1of2(cp,coinaddr,srcpub,destpub); + SetCCunspents(unspentOutputs,coinaddr); + } + else + { + fprintf(stderr,"invalid channel open txid\n"); + return 0; + } + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - // no need to prevent dup - if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) + if ( (int32_t)it->first.index==0 && GetTransaction(it->first.txhash,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size()) > 0) { - if ( (nValue= IsChannelsvout(cp,vintx,vout)) > 0 && myIsutxo_spentinmempool(txid,vout) == 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,1))>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; + txid = it->first.txhash; + break; } } } - return(totalinputs); + if (txid!=zeroid && myIsutxo_spentinmempool(txid,0) != 0) + { + txid=zeroid; + int32_t mindepth=CHANNELS_MAXPAYMENTS; + BOOST_FOREACH(const CTxMemPoolEntry &e, mempool.mapTx) + { + 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 && + tmp_txid==openTx.GetHash() && param1 < mindepth) + { + txid=hash; + totalinputs=txmempool.vout[0].nValue+txmempool.vout[1].nValue; + mindepth=param1; + } + } + } + if (txid != zeroid) + { + prevtxid=txid; + mtx.vin.push_back(CTxIn(txid,0,CScript())); + mtx.vin.push_back(CTxIn(txid,1,CScript())); + Myprivkey(myprivkey); + CCaddr2set(cp,EVAL_CHANNELS,srcpub,myprivkey,coinaddr); + CCaddr3set(cp,EVAL_CHANNELS,destpub,myprivkey,coinaddr); + return totalinputs; + } + else return 0; } std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64_t payment) { - CMutableTransaction mtx; uint8_t hash[32],hashdest[32]; uint64_t funds; int32_t i; uint256 hashchain,entropy,hentropy; CPubKey mypk; struct CCcontract_info *cp,C; + CMutableTransaction mtx; uint8_t hash[32],hashdest[32]; uint64_t funds; int32_t i; uint256 hashchain,entropy,hentropy; + CPubKey mypk; struct CCcontract_info *cp,C; + if ( numpayments <= 0 || payment <= 0 || numpayments > CHANNELS_MAXPAYMENTS ) { - CCerror = strprintf("invalid ChannelsFund param numpayments.%d max.%d payment.%lld\n",numpayments,CHANNELS_MAXPAYMENTS,(long long)payment); + CCerror = strprintf("invalid ChannelOpen param numpayments.%d max.%d payment.%lld\n",numpayments,CHANNELS_MAXPAYMENTS,(long long)payment); fprintf(stderr,"%s\n",CCerror.c_str()); return(""); } @@ -221,7 +446,7 @@ std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64 funds = numpayments * payment; if ( AddNormalinputs(mtx,mypk,funds+3*txfee,64) > 0 ) { - hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash); + hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash,mtx.vin[0].prevout.n,1); endiancpy(hash,(uint8_t *)&hentropy,32); for (i=0; i 0) + { + if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid)) !=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 (mypk != srcpub && mypk != destpub) + { + fprintf(stderr,"this is not our channel\n"); + return(""); + } + else if (amount % payment != 0 || amount 0 && + ((funcid = DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, txid, srcpub, destpub, prevdepth, param2, param3)) != 0) && + (funcid == 'P' || funcid=='O')) + { + if (numpayments > prevdepth) + { + fprintf(stderr,"not enough funds in channel for that amount\n"); + return (""); + } else if (numpayments == 0) + { + fprintf(stderr,"invalid amount\n"); + return (""); + } + if (secret!=zeroid) + { + endiancpy(hash, (uint8_t * ) & secret, 32); + for (i = 0; i < totalnumpayments-(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(""); + } + } + 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 + { + fprintf(stderr, "invalid channel open tx\n"); + return (""); + } + mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS, change, mypk, destpub)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk)); + 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, mypk, destpub, prevdepth-numpayments, numpayments, secret))); + } + else + { + fprintf(stderr,"error adding CC inputs\n"); + return(""); + } + } + fprintf(stderr,"error adding normal inputs\n"); + return(""); +} + +std::string ChannelClose(uint64_t txfee,uint256 opentxid) +{ + CMutableTransaction mtx; CPubKey mypk,srcpub,destpub; struct CCcontract_info *cp,C; + CTransaction channelOpenTx; + uint256 hashblock,tmp_txid,prevtxid,hashchain; + int32_t numvouts,numpayments; + int64_t payment,funds; + // verify this is one of our outbound channels cp = CCinit(&C,EVAL_CHANNELS); if ( txfee == 0 ) txfee = 10000; mypk = pubkey2pk(Mypubkey()); + if (GetTransaction(opentxid,channelOpenTx,hashblock,false) == 0) + { + fprintf(stderr, "invalid channel open txid\n"); + return (""); + } + if ((numvouts=channelOpenTx.vout.size()) < 1 || DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey,tmp_txid,srcpub,destpub,numpayments,payment,hashchain)!='O') + { + fprintf(stderr, "invalid channel open tx\n"); + return (""); + } + if (mypk != srcpub) + { + fprintf(stderr,"cannot close, you are not channel owner\n"); + return(""); + } if ( AddNormalinputs(mtx,mypk,2*txfee,1) > 0 ) { - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('S',mypk,destpub,0,0,zeroid))); + if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid)) !=0 && funds-txfee>0) + { + mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS, funds-txfee, 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/payment,payment,zeroid))); + } + else + { + fprintf(stderr,"error adding CC inputs\n"); + return(""); + } } + fprintf(stderr,"error adding normal inputs\n"); return(""); } -std::string ChannelPayment(uint64_t txfee,uint256 prevtxid,uint256 origtxid,int32_t n,int64_t amount) +std::string ChannelRefund(uint64_t txfee,uint256 opentxid,uint256 closetxid) { - CMutableTransaction mtx; CPubKey mypk,destpub; uint256 secret; struct CCcontract_info *cp,C; int32_t prevdepth; - // verify lasttxid and origtxid match and src is me - // also verify hashchain depth and amount, set prevdepth - cp = CCinit(&C,EVAL_CHANNELS); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - if ( AddNormalinputs(mtx,mypk,2*txfee,1) > 0 ) - { - // add locked funds inputs - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('P',mypk,destpub,prevdepth-n,amount,secret))); - } - return(""); -} + CMutableTransaction mtx; 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; + CTransaction channelOpenTx,channelCloseTx,prevTx; + CPubKey srcpub,destpub; + uint8_t funcid,hash[32],hashdest[32];; -std::string ChannelCollect(uint64_t txfee,uint256 paytxid,uint256 origtxid,int32_t n,int64_t amount) -{ - CMutableTransaction mtx; CPubKey mypk,senderpub; struct CCcontract_info *cp,C; int32_t prevdepth; - // verify paytxid and origtxid match and dest is me - // also verify hashchain depth and amount - cp = CCinit(&C,EVAL_CHANNELS); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - if ( AddNormalinputs(mtx,mypk,2*txfee,1) > 0 ) - { - // add locked funds inputs - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk)); - mtx.vout.push_back(CTxOut(amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('C',senderpub,mypk,prevdepth-n,amount,paytxid))); - } - return(""); -} - -std::string ChannelRefund(uint64_t txfee,uint256 stoptxid,uint256 origtxid) -{ - CMutableTransaction mtx; CPubKey mypk; struct CCcontract_info *cp,C; int64_t amount; // verify stoptxid and origtxid match and are mine cp = CCinit(&C,EVAL_CHANNELS); if ( txfee == 0 ) txfee = 10000; mypk = pubkey2pk(Mypubkey()); + if (GetTransaction(closetxid,channelCloseTx,hashblock,false) == 0) + { + fprintf(stderr, "invalid channel close txid\n"); + return (""); + } + if ((numvouts=channelCloseTx.vout.size()) < 1 || DecodeChannelsOpRet(channelCloseTx.vout[numvouts-1].scriptPubKey,txid,srcpub,destpub,param1,param2,param3)!='C') + { + fprintf(stderr, "invalid channel close tx\n"); + return (""); + } + if (txid!=opentxid) + { + fprintf(stderr, "open and close txid are not from same channel\n"); + return (""); + } + if (GetTransaction(opentxid,channelOpenTx,hashblock,false) == 0) + { + fprintf(stderr, "invalid channel open txid\n"); + return (""); + } + if ((numvouts=channelOpenTx.vout.size()) < 1 || DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey,txid,srcpub,destpub,numpayments,payment,hashchain)!='O') + { + fprintf(stderr, "invalid channel open tx\n"); + return (""); + } + if (mypk != srcpub) + { + fprintf(stderr,"cannot refund, you are not the channel owenr\n"); + return(""); + } if ( AddNormalinputs(mtx,mypk,2*txfee,1) > 0 ) { - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk)); - mtx.vout.push_back(CTxOut(amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('R',mypk,mypk,0,0,stoptxid))); + if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid)) !=0 && funds-txfee>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) + { + 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))); + } + else + { + fprintf(stderr,"previous tx is invalid\n"); + return(""); + } + } + else + { + fprintf(stderr,"error adding CC inputs\n"); + return(""); + } } return(""); } -UniValue ChannelsInfo() +UniValue ChannelsInfo(uint256 channeltxid) { - UniValue result(UniValue::VOBJ); CTransaction tx; uint256 txid,hashBlock,hashchain; struct CCcontract_info *cp,C; uint8_t funcid; char myCCaddr[64]; int32_t vout,numvouts,numpayments; int64_t nValue,payment; CPubKey srcpub,destpub,mypk; + UniValue result(UniValue::VOBJ); CTransaction tx,opentx; uint256 txid,tmp_txid,hashBlock,param3,opentxid,hashchain,prevtxid; + struct CCcontract_info *cp,C; char myCCaddr[65],addr[65],str1[512],str2[256]; int32_t vout,numvouts,param1,numpayments; + int64_t nValue,param2,payment; CPubKey srcpub,destpub,mypk; std::vector > txids; + result.push_back(Pair("result","success")); - result.push_back(Pair("name","Channels")); cp = CCinit(&C,EVAL_CHANNELS); mypk = pubkey2pk(Mypubkey()); - GetCCaddress(cp,myCCaddr,mypk); - SetCCtxids(txids,myCCaddr); - for (std::vector >::const_iterator it=txids.begin(); it!=txids.end(); it++) + if (channeltxid==zeroid) { - //int height = it->first.blockHeight; - txid = it->first.txhash; - vout = (int32_t)it->first.index; - nValue = (int64_t)it->second; - if ( (vout == 1 || vout == 2) && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 ) + result.push_back(Pair("name","Channels Info")); + GetCCaddress(cp,myCCaddr,mypk); + SetCCtxids(txids,myCCaddr); + for (std::vector >::const_iterator it=txids.begin(); it!=txids.end(); it++) { - if ( DecodeChannelsOpRet(txid,tx.vout[numvouts-1].scriptPubKey,srcpub,destpub,numpayments,payment,hashchain) == 'O' || funcid == 'P' ) + //int height = it->first.blockHeight; + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second; + if ( (vout == 1 || vout == 2) && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 ) { - char str[67],str2[67]; - fprintf(stderr,"%s func.%c %s -> %s %.8f num.%d of %.8f\n",mypk == srcpub ? "send" : "recv",funcid,pubkey33_str(str,(uint8_t *)&srcpub),pubkey33_str(str2,(uint8_t *)&destpub),(double)tx.vout[0].nValue/COIN,numpayments,(double)payment/COIN); + if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tmp_txid,srcpub,destpub,param1,param2,param3) == 'O') + { + GetCCaddress1of2(cp,addr,srcpub,destpub); + sprintf(str1,"%s - %lld payments of %lld satoshi - %s",addr,(long long)param1,(long long)param2,tx.GetHash().ToString().c_str()); + result.push_back(Pair("Channel", str1)); + } + } + } + } + else + { + 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')) + { + GetCCaddress1of2(cp,addr,srcpub,destpub); + sprintf(str1,"Channel %s",addr); + result.push_back(Pair("name",str1)); + SetCCtxids(txids,addr); + prevtxid=zeroid; + for (std::vector >::const_iterator it=txids.begin(); it!=txids.end(); it++) + { + + txid = it->first.txhash; + if (txid!=prevtxid && 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' && tx.GetHash()==channeltxid) + { + sprintf(str1,"%lld payments of %lld satoshi",(long long)param1,(long long)param2); + result.push_back(Pair("Open", str1)); + } + else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,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') + { + Getscriptaddress(str2,tx.vout[3].scriptPubKey); + sprintf(str1,"%lld satoshi to %s, %lld payments left",(long long)(param2*payment),str2,(long long)param1); + result.push_back(Pair("Payment",str1)); + } + } + else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,opentxid,srcpub,destpub,param1,param2,param3) == 'C' && opentxid==channeltxid) + { + result.push_back(Pair("Close","channel")); + } + else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,opentxid,srcpub,destpub,param1,param2,param3) == 'R' && opentxid==channeltxid) + { + Getscriptaddress(str2,tx.vout[2].scriptPubKey); + sprintf(str1,"%lld satoshi back to %s",(long long)(param1*param2),str2); + result.push_back(Pair("Refund",str1)); + } + } + prevtxid=txid; } } } return(result); } - diff --git a/src/cc/dapps/diceloop b/src/cc/dapps/diceloop new file mode 100755 index 000000000..44550b46a --- /dev/null +++ b/src/cc/dapps/diceloop @@ -0,0 +1,6 @@ +while true +do +./c dicestatus KMDICE 5be49570c56d036abb08b6d084da93a8a86f58fc48db4a1086be95540d752d6f + +sleep 10 +done diff --git a/src/cc/dapps/oracle_dapp.sh b/src/cc/dapps/oracle_dapp.sh deleted file mode 100755 index f6efd6a2b..000000000 --- a/src/cc/dapps/oracle_dapp.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -# SET AC -read -p "Enter AC name you use : " acname -sed -i "/#define ACNAME */c\#define ACNAME \"$acname\"" oraclefeed.c -# Set ORACLETXID -read -p "Enter your oracle TXID (Oracle should have L data type) : " oracletxid -sed -i "/#define ORACLETXID */c\#define ORACLETXID \"$oracletxid\"" oraclefeed.c -# SET PUBKEY -read -p "Enter your pubkey : " pubkey -sed -i "/#define MYPUBKEY */c\#define MYPUBKEY \"$pubkey\"" oraclefeed.c -# COMPILATION -echo "Great, compiling !" -gcc oraclefeed.c -lm -o oracle_dapp -mv oracle_dapp ../../oracle_dapp -echo "Oracle is ready to use !" -while true; do - read -p "Would you like to run BTCUSD oracle app? [Y/N]" yn - case $yn in - [Yy]* ) cd ../..; ./oracle_dapp; break;; - [Nn]* ) exit;; - * ) echo "Please answer yes or no.";; - esac -done diff --git a/src/cc/dapps/oraclefeed.c b/src/cc/dapps/oraclefeed.c index 8af31c2e4..a0930bae4 100644 --- a/src/cc/dapps/oraclefeed.c +++ b/src/cc/dapps/oraclefeed.c @@ -19,6 +19,8 @@ #include #include "cJSON.c" +bits256 zeroid; + char hexbyte(int32_t c) { c &= 0xf; @@ -139,7 +141,7 @@ long _stripwhite(char *buf,int accept) char *clonestr(char *str) { char *clone; - if ( str == 0 || str[0] == 0 ) + if ( str == 0 || str[0]==0) { printf("warning cloning nullstr.%p\n",str); //#ifdef __APPLE__ @@ -308,17 +310,30 @@ uint64_t get_btcusd() return(btcusd); } -cJSON *get_komodocli(char **retstrp,char *acname,char *method,char *arg0,char *arg1,char *arg2) +char *REFCOIN_CLI; + +cJSON *get_komodocli(char *refcoin,char **retstrp,char *acname,char *method,char *arg0,char *arg1,char *arg2,char *arg3) { long fsize; cJSON *retjson = 0; char cmdstr[32768],*jsonstr,fname[256]; sprintf(fname,"/tmp/oraclefeed.%s",method); if ( acname[0] != 0 ) - sprintf(cmdstr,"./komodo-cli -ac_name=%s %s %s %s %s > %s\n",acname,method,arg0,arg1,arg2,fname); - else sprintf(cmdstr,"./komodo-cli %s %s %s %s > %s\n",method,arg0,arg1,arg2,fname); + { + if ( refcoin[0] != 0 && strcmp(refcoin,"KMD") != 0 ) + printf("unexpected: refcoin.(%s) acname.(%s)\n",refcoin,acname); + sprintf(cmdstr,"./komodo-cli -ac_name=%s %s %s %s %s %s > %s\n",acname,method,arg0,arg1,arg2,arg3,fname); + } + else if ( strcmp(refcoin,"KMD") == 0 ) + sprintf(cmdstr,"./komodo-cli %s %s %s %s %s > %s\n",method,arg0,arg1,arg2,arg3,fname); + else if ( REFCOIN_CLI != 0 && REFCOIN_CLI[0] != 0 ) + { + sprintf(cmdstr,"%s %s %s %s %s %s > %s\n",REFCOIN_CLI,method,arg0,arg1,arg2,arg3,fname); + printf("ref.(%s) REFCOIN_CLI (%s)\n",refcoin,cmdstr); + } system(cmdstr); *retstrp = 0; if ( (jsonstr= filestr(&fsize,fname)) != 0 ) - { + { + jsonstr[strlen(jsonstr)-1]='\0'; //fprintf(stderr,"%s -> jsonstr.(%s)\n",cmdstr,jsonstr); if ( (jsonstr[0] != '{' && jsonstr[0] != '[') || (retjson= cJSON_Parse(jsonstr)) == 0 ) *retstrp = jsonstr; @@ -327,13 +342,13 @@ cJSON *get_komodocli(char **retstrp,char *acname,char *method,char *arg0,char *a return(retjson); } -bits256 komodobroadcast(char *acname,cJSON *hexjson) +bits256 komodobroadcast(char *refcoin,char *acname,cJSON *hexjson) { char *hexstr,*retstr,str[65]; cJSON *retjson; bits256 txid; memset(txid.bytes,0,sizeof(txid)); if ( (hexstr= jstr(hexjson,"hex")) != 0 ) { - if ( (retjson= get_komodocli(&retstr,acname,"sendrawtransaction",hexstr,"","")) != 0 ) + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"sendrawtransaction",hexstr,"","","")) != 0 ) { //fprintf(stderr,"broadcast.(%s)\n",jprint(retjson,0)); free_json(retjson); @@ -345,19 +360,19 @@ bits256 komodobroadcast(char *acname,cJSON *hexjson) retstr[64] = 0; decode_hex(txid.bytes,32,retstr); } - fprintf(stderr,"broadcast %s txid.(%s)\n",acname,bits256_str(str,txid)); + fprintf(stderr,"broadcast %s txid.(%s)\n",strlen(acname)>0?acname:refcoin,bits256_str(str,txid)); free(retstr); } } return(txid); } -bits256 sendtoaddress(char *acname,char *destaddr,int64_t satoshis) +bits256 sendtoaddress(char *refcoin,char *acname,char *destaddr,int64_t satoshis) { char numstr[32],*retstr,str[65]; cJSON *retjson; bits256 txid; memset(txid.bytes,0,sizeof(txid)); sprintf(numstr,"%.8f",(double)satoshis/SATOSHIDEN); - if ( (retjson= get_komodocli(&retstr,acname,"sendtoaddress",destaddr,numstr,"")) != 0 ) + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"sendtoaddress",destaddr,numstr,"","")) != 0 ) { fprintf(stderr,"unexpected sendrawtransaction json.(%s)\n",jprint(retjson,0)); free_json(retjson); @@ -375,36 +390,34 @@ bits256 sendtoaddress(char *acname,char *destaddr,int64_t satoshis) return(txid); } -int32_t get_KMDheight(char *acname) +int32_t get_coinheight(char *refcoin,char *acname) { cJSON *retjson; char *retstr; int32_t height=0; - if ( (retjson= get_komodocli(&retstr,acname,"getinfo","","","")) != 0 ) + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getblockchaininfo","","","","")) != 0 ) { height = jint(retjson,"blocks"); - //fprintf(stderr,"%s height.%d\n",acname[0]!=0?acname:"KMD",height); free_json(retjson); } else if ( retstr != 0 ) { - fprintf(stderr,"get_KMDheight.(%s) error.(%s)\n",acname,retstr); + fprintf(stderr,"%s get_coinheight.(%s) error.(%s)\n",refcoin,acname,retstr); free(retstr); } return(height); } -bits256 get_KMDblockhash(int32_t height) +bits256 get_coinblockhash(char *refcoin,char *acname,int32_t height) { cJSON *retjson; char *retstr,heightstr[32]; bits256 hash; memset(hash.bytes,0,sizeof(hash)); sprintf(heightstr,"%d",height); - if ( (retjson= get_komodocli(&retstr,"","getblockhash",heightstr,"","")) != 0 ) + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getblockhash",heightstr,"","","")) != 0 ) { fprintf(stderr,"unexpected blockhash json.(%s)\n",jprint(retjson,0)); free_json(retjson); } else if ( retstr != 0 ) { - //fprintf(stderr,"get_KMDblockhash.(%s) %d\n",retstr,(int32_t)strlen(retstr)); if ( strlen(retstr) >= 64 ) { retstr[64] = 0; @@ -415,11 +428,11 @@ bits256 get_KMDblockhash(int32_t height) return(hash); } -bits256 get_KMDmerkleroot(bits256 blockhash) +bits256 get_coinmerkleroot(char *refcoin,char *acname,bits256 blockhash) { cJSON *retjson; char *retstr,str[65]; bits256 merkleroot; memset(merkleroot.bytes,0,sizeof(merkleroot)); - if ( (retjson= get_komodocli(&retstr,"","getblockheader",bits256_str(str,blockhash),"","")) != 0 ) + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getblockheader",bits256_str(str,blockhash),"","","")) != 0 ) { merkleroot = jbits256(retjson,"merkleroot"); //fprintf(stderr,"got merkleroot.(%s)\n",bits256_str(str,merkleroot)); @@ -427,24 +440,24 @@ bits256 get_KMDmerkleroot(bits256 blockhash) } else if ( retstr != 0 ) { - fprintf(stderr,"get_KMDmerkleroot error.(%s)\n",retstr); + fprintf(stderr,"%s %s get_coinmerkleroot error.(%s)\n",refcoin,acname,retstr); free(retstr); } return(merkleroot); } -int32_t get_KMDheader(bits256 *blockhashp,bits256 *merklerootp,int32_t prevheight) +int32_t get_coinheader(char *refcoin,char *acname,bits256 *blockhashp,bits256 *merklerootp,int32_t prevheight) { int32_t height = 0; char str[65]; if ( prevheight == 0 ) - height = get_KMDheight("") - 20; + height = get_coinheight(refcoin,acname) - 20; else height = prevheight + 1; if ( height > 0 ) { - *blockhashp = get_KMDblockhash(height); + *blockhashp = get_coinblockhash(refcoin,acname,height); if ( bits256_nonz(*blockhashp) != 0 ) { - *merklerootp = get_KMDmerkleroot(*blockhashp); + *merklerootp = get_coinmerkleroot(refcoin,acname,*blockhashp); if ( bits256_nonz(*merklerootp) != 0 ) return(height); } @@ -454,26 +467,26 @@ int32_t get_KMDheader(bits256 *blockhashp,bits256 *merklerootp,int32_t prevheigh return(0); } -cJSON *get_gatewayspending(char *acname,char *oraclestxidstr,char *coin) +cJSON *get_gatewayspending(char *refcoin,char *acname,char *bindtxidstr) { cJSON *retjson; char *retstr; - if ( (retjson= get_komodocli(&retstr,acname,"gatewayspending",oraclestxidstr,coin,"")) != 0 ) + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"gatewayspending",bindtxidstr,refcoin,"","")) != 0 ) { //printf("pending.(%s)\n",jprint(retjson,0)); return(retjson); } else if ( retstr != 0 ) { - fprintf(stderr,"get_gatewayspending.(%s) error.(%s)\n",acname,retstr); + fprintf(stderr,"%s get_gatewayspending.(%s) error.(%s)\n",refcoin,acname,retstr); free(retstr); } return(0); } -cJSON *get_rawmempool(char *acname) +cJSON *get_rawmempool(char *refcoin,char *acname) { cJSON *retjson; char *retstr; - if ( (retjson= get_komodocli(&retstr,acname,"getrawmempool","","","")) != 0 ) + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getrawmempool","","","","")) != 0 ) { //printf("mempool.(%s)\n",jprint(retjson,0)); return(retjson); @@ -486,11 +499,13 @@ cJSON *get_rawmempool(char *acname) return(0); } -cJSON *get_addressutxos(char *acname,char *coinaddr) +cJSON *get_addressutxos(char *refcoin,char *acname,char *coinaddr) { cJSON *retjson; char *retstr,jsonbuf[256]; + if ( refcoin[0] != 0 && strcmp(refcoin,"KMD") != 0 ) + printf("warning: assumes %s has addressindex enabled\n",refcoin); sprintf(jsonbuf,"{\\\"addresses\\\":[\\\"%s\\\"]}",coinaddr); - if ( (retjson= get_komodocli(&retstr,acname,"getaddressutxos",jsonbuf,"","")) != 0 ) + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getaddressutxos",jsonbuf,"","","")) != 0 ) { //printf("addressutxos.(%s)\n",jprint(retjson,0)); return(retjson); @@ -503,28 +518,209 @@ cJSON *get_addressutxos(char *acname,char *coinaddr) return(0); } -cJSON *get_rawtransaction(char *acname,bits256 txid) +cJSON *get_rawtransaction(char *refcoin,char *acname,bits256 txid) { cJSON *retjson; char *retstr,str[65]; - if ( (retjson= get_komodocli(&retstr,acname,"getrawtransaction",bits256_str(str,txid),"1","")) != 0 ) + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getrawtransaction",bits256_str(str,txid),"1","","")) != 0 ) { return(retjson); } else if ( retstr != 0 ) { - fprintf(stderr,"get_rawtransaction.(%s) error.(%s)\n",acname,retstr); + fprintf(stderr,"get_rawtransaction.(%s) %s error.(%s)\n",refcoin,acname,retstr); free(retstr); } return(0); } -void gatewaysmarkdone(char *acname,bits256 txid) +int32_t validateaddress(char *refcoin,char *acname,char *depositaddr, char* compare) +{ + cJSON *retjson; char *retstr; int32_t res=0; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"validateaddress",depositaddr,"","","")) != 0 ) + { + if (is_cJSON_True(jobj(retjson,compare)) != 0 ) res=1; + free_json(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"validateaddress.(%s) %s error.(%s)\n",refcoin,acname,retstr); + free(retstr); + } + + return (res); +} + +void importaddress(char *refcoin,char *acname,char *depositaddr) +{ + cJSON *retjson; char *retstr; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"importaddress",depositaddr,"","true","")) != 0 ) + { + printf("importaddress.(%s)\n",jprint(retjson,0)); + free_json(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"importaddress.(%s) %s error.(%s)\n",refcoin,acname,retstr); + free(retstr); + } +} + +void addmultisigaddress(char *refcoin,char *acname,int32_t M, char *pubkeys,char *bindtxidstr) +{ + cJSON *retjson; char *retstr,Mstr[10],tmp[128]; + + sprintf(Mstr,"%d",M); + sprintf(tmp,"\"%s\"",bindtxidstr); + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"addmultisigaddress",Mstr,pubkeys,tmp,"")) != 0 ) + { + fprintf(stderr,"unexpected addmultisigaddress json.(%s)\n",jprint(retjson,0)); + free(retstr); + } + else if ( retstr != 0 ) + { + printf("addmultisigaddress.(%s)\n",retstr); + free_json(retjson); + } +} + +cJSON *getinputarray(int64_t *totalp,cJSON *unspents,int64_t required) +{ + cJSON *vin,*item,*vins = cJSON_CreateArray(); int32_t i,n,v; int64_t satoshis; bits256 txid; + *totalp = 0; + if ( (n= cJSON_GetArraySize(unspents)) > 0 ) + { + for (i=0; i= required ) + break; + } + } + } + return(vins); +} + +char *createmultisig(char *refcoin,char *acname,char *depositaddr,char *signeraddr,char *withdrawaddr,int64_t satoshis) +{ + char *retstr,*retstr2,array[128],*txstr = 0; cJSON *retjson2,*retjson,*vins,*vouts; int64_t txfee,total,change = 0; + if ( strcmp(refcoin,"BTC") == 0 ) + txfee = 20000; + else txfee = 10000; + if ( satoshis < txfee ) + { + printf("createmultisig satoshis %.8f < txfee %.8f\n",(double)satoshis/SATOSHIDEN,(double)txfee/SATOSHIDEN); + return(0); + } + satoshis -= txfee; + sprintf(array,"\'[\"%s\"]\'",depositaddr); + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"listunspent","1","99999999",array,"")) != 0 ) + { + //createrawtransaction [{"txid":"id","vout":n},...] {"address":amount,...} + if ( (vins= getinputarray(&total,retjson,satoshis)) != 0 ) + { + if ( total >= satoshis ) + { + vouts = cJSON_CreateObject(); + jaddnum(vouts,withdrawaddr,(double)satoshis/SATOSHIDEN); + if ( total > satoshis+txfee ) + { + change = (total - satoshis); + jaddnum(vouts,depositaddr,(double)change/SATOSHIDEN); + } + char *tmpA=jprint(vins,1); + char *tmpB=jprint(vouts,1); + char *argA=malloc(sizeof(char) * (strlen(tmpA)+3)); + char *argB=malloc(sizeof(char) * (strlen(tmpB)+3)); + sprintf(argA,"\'%s\'",tmpA); + sprintf(argB,"\'%s\'",tmpB); + if ( (retjson2= get_komodocli(refcoin,&txstr,acname,"createrawtransaction",argA,argB,"","")) != 0 ) + { + printf("createmultisig: unexpected JSON2.(%s)\n",jprint(retjson2,0)); + free_json(retjson2); + } + else if ( txstr == 0 ) + printf("createmultisig: null txstr and JSON2\n"); + free(tmpA); + free(tmpB); + free(argA); + free(argB); + } + } + free_json(retjson); + } + else if ( retstr != 0 ) + { + printf("createmultisig: unexpected null JSON, retstr.(%s)\n",retstr); + free(retstr); + } + else printf("createmultisig: null retstr and JSON\n"); + return(txstr); +} + +cJSON *addmultisignature(char *refcoin,char *acname,char *signeraddr,char *rawtx) +{ + char *retstr,*hexstr; cJSON *retjson; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"signrawtransaction",rawtx,"","","")) != 0 ) + { + if ( is_cJSON_True(jobj(retjson,"complete")) != 0 ) + return(retjson); + else if ( (hexstr= jstr(retjson,"hex")) != 0 && strlen(hexstr) > strlen(rawtx) ) + { + jaddnum(retjson,"partialtx",1); + return(retjson); + } + free_json(retjson); + } + return(0); +} + +char *get_gatewaysmultisig(char *refcoin,char *acname,char *txidaddr,int32_t *K) +{ + char *retstr,*hexstr,*hex=0; cJSON *retjson; + if ( (retjson= get_komodocli("KMD",&retstr,acname,"gatewaysmultisig",txidaddr,"","","")) != 0 ) + { + if ((hexstr=jstr(retjson,"hex")) != 0 ) + { + if (strlen(hexstr)>0) hex = clonestr(hexstr); + } + *K=jint(retjson,"number_of_signs"); + free_json(retjson); + } + return(hex); +} + +bits256 gatewayspartialsign(char *refcoin,char *acname,bits256 txid,char *hex) { char str[65],*retstr; cJSON *retjson; - printf("spend %s %s/v2 as marker\n",acname,bits256_str(str,txid)); - if ( (retjson= get_komodocli(&retstr,acname,"gatewaysmarkdone",bits256_str(str,txid),"","")) != 0 ) + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"gatewayspartialsign",bits256_str(str,txid),refcoin,hex,"")) != 0 ) { - komodobroadcast(acname,retjson); + return(komodobroadcast(refcoin,acname,retjson)); + } + else if ( retstr != 0 ) + { + printf("error parsing gatewayspartialsing.(%s)\n",retstr); + free(retstr); + } + return (zeroid); +} + +void gatewaysmarkdone(char *refcoin,char *acname,bits256 withtxid,char *coin,bits256 cointxid) +{ + char str[65],str2[65],*retstr; cJSON *retjson; + printf("spend %s %s/v2 as marker\n",acname,bits256_str(str,withtxid)); + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"gatewaysmarkdone",bits256_str(str,withtxid),coin,bits256_str(str2,cointxid),"")) != 0 ) + { + komodobroadcast(refcoin,acname,retjson); free_json(retjson); } else if ( retstr != 0 ) @@ -534,17 +730,57 @@ void gatewaysmarkdone(char *acname,bits256 txid) } } -int32_t tx_has_voutaddress(char *acname,bits256 txid,char *coinaddr) +int32_t get_gatewaysinfo(char *refcoin,char *acname,char *depositaddr,int32_t *Mp,int32_t *Np,char *bindtxidstr,char *coin,char *oraclestr, char **pubkeys) { - cJSON *txobj,*vouts,*vout,*sobj,*addresses; char *addr,str[65]; int32_t i,j,n,numvouts,retval = 0; - if ( (txobj= get_rawtransaction(acname,txid)) != 0 ) + char *oracle,*retstr,*name,*deposit,temp[128]; cJSON *retjson,*pubarray; int32_t n; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"gatewaysinfo",bindtxidstr,"","","")) != 0 ) { - if ( (vouts= jarray(&numvouts,txobj,"vout")) != 0 ) + if ( (oracle= jstr(retjson,"oracletxid")) != 0 && strcmp(oracle,oraclestr) == 0 && (deposit= jstr(retjson,"deposit")) != 0 ) { - for (i=0; i= 1 ) { - vout = jitem(vouts,i); - if ( (sobj= jobj(vout,"scriptPubKey")) != 0 ) + *Mp = jint(retjson,"M"); + *Np = jint(retjson,"N"); + } + else printf("coin.%s vs %s\n",jstr(retjson,"coin"),coin); + if ((pubarray=jarray(&n,retjson,"pubkeys"))!=0) + { + *pubkeys=malloc((sizeof(char)*70*n)+64); + sprintf(*pubkeys,"\"["); + for (int i=0;i 0 ) + if ( tx_has_voutaddress(refcoin,acname,txid,coinaddr) > 0 ) { num = 1; break; @@ -592,11 +857,11 @@ int32_t coinaddrexists(char *acname,char *coinaddr) } free_json(array); } else return(-1); - } + } return(num); } -void update_gatewayspending(char *acname,char *oraclestxidstr,char *coin) +void update_gatewayspending(char *refcoin,char *acname,char *bindtxidstr,int32_t M,int32_t N) { // check queue to prevent duplicate // check KMD chain and mempool for txidaddr @@ -605,10 +870,11 @@ void update_gatewayspending(char *acname,char *oraclestxidstr,char *coin) /// if enough sigs, sendrawtransaction and when it confirms spend marker (txid.2) /// if not enough sigs, post partially signed to acname with marker2 // monitor marker2, for the partially signed withdraws - cJSON *retjson,*pending,*item; char str[65],*coinstr,*txidaddr,*signeraddr,*withdrawaddr; int32_t i,n,retval,processed = 0; bits256 txid,withtxid,origtxid; int64_t satoshis; - if ( (retjson= get_gatewayspending(acname,oraclestxidstr,coin)) != 0 ) + cJSON *retjson,*pending,*item,*clijson; char str[65],*rawtx,*coinstr,*txidaddr,*signeraddr,*depositaddr,*withdrawaddr; int32_t i,j,n,K,retval,processed = 0; bits256 txid,cointxid,origtxid; int64_t satoshis; + memset(&zeroid,0,sizeof(zeroid)); + if ( (retjson= get_gatewayspending("KMD",acname,bindtxidstr)) != 0 ) { - if ( jint(retjson,"queueflag") != 0 && (coinstr= jstr(retjson,"coin")) != 0 && strcmp(coinstr,coin) == 0 ) + if ( jint(retjson,"queueflag") != 0 && (coinstr= jstr(retjson,"coin")) != 0 && strcmp(coinstr,refcoin) == 0 ) { if ( (pending= jarray(&n,retjson,"pending")) != 0 ) { @@ -619,33 +885,64 @@ void update_gatewayspending(char *acname,char *oraclestxidstr,char *coin) item = jitem(pending,i); origtxid = jbits256(item,"txid"); //process item.0 {"txid":"10ec8f4dad6903df6b249b361b879ac77b0617caad7629b97e10f29fa7e99a9b","txidaddr":"RMbite4TGugVmkGmu76ytPHDEQZQGSUjxz","withdrawaddr":"RNJmgYaFF5DbnrNUX6pMYz9rcnDKC2tuAc","amount":"1.00000000","depositaddr":"RHV2As4rox97BuE3LK96vMeNY8VsGRTmBj","signeraddr":"RHV2As4rox97BuE3LK96vMeNY8VsGRTmBj"} - if ( (txidaddr= jstr(item,"txidaddr")) != 0 && (withdrawaddr= jstr(item,"withdrawaddr")) != 0 && (signeraddr= jstr(item,"signeraddr")) != 0 ) + if ( (txidaddr= jstr(item,"txidaddr")) != 0 && (withdrawaddr= jstr(item,"withdrawaddr")) != 0 && (depositaddr= jstr(item,"depositaddr")) != 0 && (signeraddr= jstr(item,"signeraddr")) != 0 ) { - if ( (satoshis= jdouble(item,"amount")*SATOSHIDEN) != 0 && (retval= coinaddrexists(acname,txidaddr)) == 0 ) - { - // this is less errors but more expensive: ./komodo-cli z_sendmany "signeraddr" '[{"address":"","amount":0.0001},{"address":"","amount":}]' - txid = sendtoaddress(acname,txidaddr,10000); - if ( bits256_nonz(txid) != 0 && coinaddrexists(acname,txidaddr) > 0 ) + if ( (satoshis= jdouble(item,"amount")*SATOSHIDEN) != 0 && markerfromthisnodeorunconfirmed("KMD",acname,txidaddr) == 0) + { + // the actual withdraw + if ( strcmp(depositaddr,signeraddr) == 0 ) { - // the actual withdraw - withtxid = sendtoaddress(strcmp("KMD",coin)==0?"":coin,withdrawaddr,satoshis); - if ( bits256_nonz(withtxid) != 0 ) + txid= sendtoaddress("KMD",acname,txidaddr,10000); + if (bits256_nonz(txid) != 0) { - fprintf(stderr,"withdraw %s %s %s %.8f processed\n",coin,bits256_str(str,withtxid),withdrawaddr,(double)satoshis/SATOSHIDEN); - gatewaysmarkdone(acname,origtxid); - processed++; + cointxid = sendtoaddress(refcoin,"",withdrawaddr,satoshis); + if ( bits256_nonz(cointxid) != 0) + { + fprintf(stderr,"withdraw %s %s %s %.8f processed\n",refcoin,bits256_str(str,cointxid),withdrawaddr,(double)satoshis/SATOSHIDEN); + gatewaysmarkdone("KMD",acname,origtxid,refcoin,cointxid); + processed++; + } + else + { + fprintf(stderr,"ERROR withdraw %s %s %s %.8f processed\n",refcoin,bits256_str(str,cointxid),withdrawaddr,(double)satoshis/SATOSHIDEN); + } } else { - fprintf(stderr,"ERROR withdraw %s %s %s %.8f processed\n",coin,bits256_str(str,withtxid),withdrawaddr,(double)satoshis/SATOSHIDEN); + fprintf(stderr,"ERROR sending withdraw marker %s %s to %s %.8f processed\n",refcoin,bits256_str(str,cointxid),txidaddr,(double)10000/SATOSHIDEN); } - } else fprintf(stderr,"error sending %s txidaddr.%s -> %s exists.%d\n",acname,txidaddr,bits256_str(str,txid),coinaddrexists(acname,txidaddr)); - } - else if ( retval > 0 ) - { - fprintf(stderr,"already did withdraw %s %s %.8f processed\n",coin,withdrawaddr,(double)satoshis/SATOSHIDEN); - gatewaysmarkdone(acname,origtxid); - } + } + else + { + if ( (rawtx= get_gatewaysmultisig(refcoin,acname,txidaddr,&K)) == 0) + { + rawtx = createmultisig(refcoin,"",depositaddr,signeraddr,withdrawaddr,satoshis); + } + if ( rawtx != 0 ) + { + if ( (clijson= addmultisignature(refcoin,"",signeraddr,rawtx)) != 0 ) + { + if ( is_cJSON_True(jobj(clijson,"complete")) != 0 ) + { + cointxid = komodobroadcast(refcoin,"",clijson); + if ( bits256_nonz(cointxid) != 0 ) + { + fprintf(stderr,"withdraw %s M.%d N.%d %s %s %.8f processed\n",refcoin,M,N,bits256_str(str,cointxid),withdrawaddr,(double)satoshis/SATOSHIDEN); + gatewaysmarkdone("KMD",acname,origtxid,refcoin,cointxid); + } + } + else if ( jint(clijson,"partialtx") != 0 ) + { + txid=gatewayspartialsign(refcoin,acname,origtxid,jstr(clijson,"hex")); + fprintf(stderr,"%d of %d partialtx %s sent\n",K+1,N,bits256_str(str,txid)); + } + free_json(clijson); + } + processed++; + free(rawtx); + } else fprintf(stderr,"couldnt create msig rawtx\n"); + } + } } } } @@ -654,7 +951,7 @@ void update_gatewayspending(char *acname,char *oraclestxidstr,char *coin) } } -int32_t get_oracledata(int32_t prevheight,char *hexstr,int32_t maxsize,char *format) +int32_t get_oracledata(char *refcoin,char *acname,int32_t prevheight,char *hexstr,int32_t maxsize,char *format) { int32_t i; uint32_t height; uint64_t price; bits256 blockhash,merkleroot; hexstr[0] = 0; @@ -670,7 +967,7 @@ int32_t get_oracledata(int32_t prevheight,char *hexstr,int32_t maxsize,char *for } else if ( strcmp(format,"Ihh") == 0 ) { - if ( (height= get_KMDheader(&blockhash,&merkleroot,prevheight)) > prevheight ) + if ( (height= get_coinheader(refcoin,acname,&blockhash,&merkleroot,prevheight)) > prevheight ) { for (i=0; i<4; i++) sprintf(&hexstr[i*2],"%02x",(uint8_t)((height >> (i*8)) & 0xff)); @@ -711,28 +1008,56 @@ oraclesdata 17a841a919c284cea8a676f34e793da002e606f19a9258a3190bed12d5aaa3ff 034 int32_t main(int32_t argc,char **argv) { - cJSON *clijson,*clijson2,*regjson,*item; int32_t acheight,i,retval,n,height,prevheight = 0; char *format,*acname,*oraclestr,*bindtxidstr,*pkstr,*pubstr,*retstr,*retstr2,hexstr[4096]; uint64_t price; bits256 txid; - if ( argc != 6 ) + cJSON *clijson,*clijson2,*regjson,*item; int32_t acheight,i,retval,M,N,n,height,prevheight = 0; char *pubkeys,*format,*acname,*oraclestr,*bindtxidstr,*pkstr,*pubstr,*retstr,*retstr2,depositaddr[64],hexstr[4096],refcoin[64]; uint64_t price; bits256 txid; + if ( argc < 6 ) { - printf("usage: oraclefeed $ACNAME $ORACLETXID $MYPUBKEY $FORMAT $BINDTXID\nPowered by CoinDesk (%s) %.8f\n","https://www.coindesk.com/price/",dstr(get_btcusd())); + printf("usage: oraclefeed $ACNAME $ORACLETXID $MYPUBKEY $FORMAT $BINDTXID [refcoin_cli]\n"); return(-1); } + printf("Powered by CoinDesk (%s) %.8f\n","https://www.coindesk.com/price/",dstr(get_btcusd())); acname = argv[1]; oraclestr = argv[2]; pkstr = argv[3]; format = argv[4]; bindtxidstr = argv[5]; + if ( argc > 6 ) + REFCOIN_CLI = argv[6]; + else REFCOIN_CLI = "./komodo-cli"; if ( strncmp(format,"Ihh",3) != 0 && format[0] != 'L' ) { printf("only formats of L and Ihh are supported now\n"); return(-1); } + M = N = 1; acheight = 0; + refcoin[0] = 0; while ( 1 ) { retstr = 0; - if ( prevheight < (get_KMDheight("") - 10) && (clijson= get_komodocli(&retstr,acname,"oraclesinfo",oraclestr,"","")) != 0 ) + if ( (refcoin[0] == 0 || prevheight < (get_coinheight(refcoin,"") - 10)) && (clijson= get_komodocli("KMD",&retstr,acname,"oraclesinfo",oraclestr,"","","")) != 0 ) { + if ( refcoin[0] == 0 && jstr(clijson,"name") != 0 ) + { + strcpy(refcoin,jstr(clijson,"name")); + if ( strcmp("KMD",refcoin) != 0 && argc != 7 ) + { + printf("need to specify path to refcoin's cli as last argv\n"); + exit(0); + } + pubkeys=0; + if ( get_gatewaysinfo("KMD",acname,depositaddr,&M,&N,bindtxidstr,refcoin,oraclestr,&pubkeys) < 0 ) + { + printf("cant find bindtxid.(%s)\n",bindtxidstr); + exit(0); + } + if (validateaddress(refcoin,"",depositaddr,"iswatchonly")==0) + { + if (M==N==1) importaddress(refcoin,"",depositaddr); + else addmultisigaddress(refcoin,"",M,pubkeys,bindtxidstr); + } + if (pubkeys!=0) free(pubkeys); + printf("set refcoin %s <- %s [%s] M.%d of N.%d\n",depositaddr,refcoin,REFCOIN_CLI,M,N); + } if ( (regjson= jarray(&n,clijson,"registered")) != 0 ) { for (i=0; ibettxid = bettxid; + HASH_ADD(hh,DICEHASH_TABLE,bettxid,sizeof(bettxid),ptr); + return(ptr); +} + +int32_t _dicerevealed_find(uint256 &oldbettxid,CTransaction &oldbetTx,int32_t &oldentropyvout,uint256 entropyused,uint256 bettxid,int32_t entropyvout) +{ + struct dice_entropy *ptr; + HASH_FIND(hh,DICE_ENTROPY,&entropyused,sizeof(entropyused),ptr); + if ( ptr != 0 ) + { + if ( entropyvout == ptr->entropyvout ) + { + if ( bettxid == ptr->bettxid ) + { + //fprintf(stderr,"identical %s E.%s v.%d\n",bettxid.GetHex().c_str(),entropyused.GetHex().c_str(),entropyvout); + return(entropyvout+1); + } + else + { + fprintf(stderr,"found identical entropy used.%s %s vs %s v.%d vs %d\n",entropyused.GetHex().c_str(),bettxid.GetHex().c_str(),ptr->bettxid.GetHex().c_str(),entropyvout,ptr->entropyvout); + oldbettxid = ptr->bettxid; + oldbetTx = ptr->betTx; + oldentropyvout = ptr->entropyvout; + return(-1); + } + } else fprintf(stderr,"shared entropy.%s vouts %d vs %d\n",entropyused.GetHex().c_str(),entropyvout,ptr->entropyvout); + } + return(0); +} + +struct dice_entropy *_dicerevealed_add(uint256 entropyused,uint256 bettxid,CTransaction betTx,int32_t entropyvout) +{ + struct dice_entropy *ptr; + ptr = (struct dice_entropy *)calloc(1,sizeof(*ptr)); + ptr->entropyused = entropyused; + ptr->bettxid = bettxid; + ptr->betTx = betTx; + ptr->entropyvout = entropyvout; + HASH_ADD(hh,DICE_ENTROPY,entropyused,sizeof(entropyused),ptr); + return(ptr); +} + +int32_t DiceEntropyUsed(CTransaction &oldbetTx,uint256 &oldbettxid,int32_t &oldentropyvout,uint256 entropyused,uint256 bettxid,CTransaction betTx,int32_t entropyvout) +{ + int32_t retval; + oldbettxid = zeroid; + if ( entropyused == zeroid || bettxid == zeroid ) + { + fprintf(stderr,"null entropyused or bettxid\n"); + return(1); + } + pthread_mutex_lock(&DICEREVEALED_MUTEX); + retval = _dicerevealed_find(oldbettxid,oldbetTx,oldentropyvout,entropyused,bettxid,entropyvout); + pthread_mutex_unlock(&DICEREVEALED_MUTEX); + return(retval); +} + +bool mySenddicetransaction(std::string res,uint256 entropyused,int32_t entropyvout,uint256 bettxid,CTransaction betTx,uint8_t funcid,struct dicefinish_info *ptr) +{ + CTransaction tx; int32_t i=0,retval=-1,oldentropyvout; uint256 oldbettxid; CTransaction oldbetTx; if ( res.empty() == 0 && res.size() > 64 && is_hexstr((char *)res.c_str(),0) > 64 ) { if ( DecodeHexTx(tx,res) != 0 ) { - fprintf(stderr,"%s\n%s\n",res.c_str(),uint256_str(str,tx.GetHash())); - LOCK(cs_main); - if ( myAddtomempool(tx) != 0 ) + if ( ptr != 0 ) + ptr->txid = tx.GetHash(); + //fprintf(stderr,"%s\n%s\n",res.c_str(),uint256_str(str,tx.GetHash())); + if ( funcid == 'R' || (retval= DiceEntropyUsed(oldbetTx,oldbettxid,oldentropyvout,entropyused,bettxid,betTx,entropyvout)) >= 0 ) { - RelayTransaction(tx); - fprintf(stderr,"added to mempool and broadcast\n"); - return(true); - } else fprintf(stderr,"error adding to mempool\n"); + //LOCK(cs_main); + if ( myAddtomempool(tx) != 0 ) + { + RelayTransaction(tx); + if ( retval == 0 ) + { + if ( ptr != 0 ) + ptr->revealed = (uint32_t)time(NULL); + pthread_mutex_lock(&DICEREVEALED_MUTEX); + _dicerevealed_add(entropyused,bettxid,betTx,entropyvout); + pthread_mutex_unlock(&DICEREVEALED_MUTEX); + fprintf(stderr,"added.%c to mempool.[%d] and broadcast entropyused.%s bettxid.%s -> %s\n",funcid,i,entropyused.GetHex().c_str(),bettxid.GetHex().c_str(),tx.GetHash().GetHex().c_str()); + } else fprintf(stderr,"rebroadcast.%c to mempool.[%d] and broadcast entropyused.%s bettxid.%s -> %s\n",funcid,i,entropyused.GetHex().c_str(),bettxid.GetHex().c_str(),tx.GetHash().GetHex().c_str()); + return(true); + } + else + { + RelayTransaction(tx); + fprintf(stderr,"rebroadcast.%c and clear [%d] and broadcast entropyused.%s bettxid.%s -> %s\n",funcid,i,entropyused.GetHex().c_str(),bettxid.GetHex().c_str(),tx.GetHash().GetHex().c_str()); + if ( ptr->rawtx.empty() == 0 ) + ptr->rawtx.clear(); + ptr->txid = zeroid; + //fprintf(stderr,"error adding funcid.%c E.%s bet.%s -> %s to mempool, probably Disable replacement feature size.%d\n",funcid,entropyused.GetHex().c_str(),bettxid.GetHex().c_str(),tx.GetHash().GetHex().c_str(),(int32_t)ptr->rawtx.size()); + } + } else fprintf(stderr,"error duplicate entropyused different bettxid\n"); } else fprintf(stderr,"error decoding hex\n"); } return(false); } -void *dicefinish(void *_ptr) +int32_t dicefinish_utxosget(int32_t &total,struct dicefinish_utxo *utxos,int32_t max,char *coinaddr) { - char str[65],str2[65],name[32]; std::string res; int32_t i,result,duplicate=0; struct dicefinish_info *ptr; - ptr = (struct dicefinish_info *)_ptr; - sleep(3); // wait for bettxid to be in mempool - for (i=0; ibettxid ) + int32_t n = 0; int64_t threshold = 2 * 10000; + total = 0; + std::vector > unspentOutputs; + SetCCunspents(unspentOutputs,coinaddr); + { + LOCK(mempool.cs); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { - duplicate = 1; - break; - } - if ( duplicate == 0 ) - { - for (i=0; ifirst.txhash,(int32_t)it->first.index) == 0 ) { - bettxids[i] = ptr->bettxid; - break; + if ( it->second.satoshis < threshold || it->second.satoshis > 10*threshold ) + continue; + total++; + if ( n < max ) + { + if ( utxos != 0 ) + { + utxos[n].txid = it->first.txhash; + utxos[n].vout = (int32_t)it->first.index; + } + n++; + } } - if ( i == sizeof(bettxids)/sizeof(*bettxids) ) - bettxids[rand() % i] = ptr->bettxid; + } } - unstringbits(name,ptr->sbits); - //fprintf(stderr,"duplicate.%d dicefinish.%d %s funding.%s bet.%s\n",duplicate,ptr->iswin,name,uint256_str(str,ptr->fundingtxid),uint256_str(str2,ptr->bettxid)); - if ( duplicate == 0 ) + total -= n; + return(n); +} + +int32_t dice_betspent(char *debugstr,uint256 bettxid) +{ + CSpentIndexValue value,value2; + CSpentIndexKey key(bettxid,0); + CSpentIndexKey key2(bettxid,1); + if ( GetSpentIndex(key,value) != 0 || GetSpentIndex(key2,value2) != 0 ) { - res = DiceBetFinish(&result,0,name,ptr->fundingtxid,ptr->bettxid,ptr->iswin); - if ( result > 0 ) - mySendrawtransaction(res); + //fprintf(stderr,"%s txid.%s already spent\n",debugstr,bettxid.GetHex().c_str()); + return(1); + } + { + //LOCK(mempool.cs); + if ( myIsutxo_spentinmempool(bettxid,0) != 0 || myIsutxo_spentinmempool(bettxid,1) != 0 ) + { + fprintf(stderr,"%s bettxid.%s already spent in mempool\n",debugstr,bettxid.GetHex().c_str()); + return(-1); + } } - free(ptr); return(0); } -void DiceQueue(int32_t iswin,uint64_t sbits,uint256 fundingtxid,uint256 bettxid) +void dicefinish_delete(struct dicefinish_info *ptr) { - struct dicefinish_info *ptr = (struct dicefinish_info *)calloc(1,sizeof(*ptr)); - ptr->fundingtxid = fundingtxid; - ptr->bettxid = bettxid; - ptr->sbits = sbits; - ptr->iswin = iswin; - if ( ptr != 0 && pthread_create((pthread_t *)malloc(sizeof(pthread_t)),NULL,dicefinish,(void *)ptr) != 0 ) + pthread_mutex_lock(&DICE_MUTEX); + _dicehash_clear(ptr->bettxid); + pthread_mutex_unlock(&DICE_MUTEX); + DL_DELETE(DICEFINISH_LIST,ptr); + free(ptr); +} + +void *dicefinish(void *_ptr) +{ + std::vector mypk; struct CCcontract_info *cp,C; char name[32],coinaddr[64],CCaddr[64]; std::string res; int32_t newht,newblock,entropyvout,numblocks,lastheight=0,vin0_needed,i,n,m,num,iter,result; struct dicefinish_info *ptr,*tmp; uint32_t now; struct dicefinish_utxo *utxos; uint256 hashBlock,entropyused; CTransaction betTx,finishTx; + mypk = Mypubkey(); + pubkey2addr(coinaddr,mypk.data()); + cp = CCinit(&C,EVAL_DICE); + GetCCaddress(cp,CCaddr,GetUnspendable(cp,0)); + fprintf(stderr,"start dicefinish thread %s CCaddr.%s\n",coinaddr,CCaddr); + if ( (newht= KOMODO_INSYNC) == 0 ) + sleep(7); + sleep(3); + while ( 1 ) { - //fprintf(stderr,"DiceQueue.%d\n",iswin); - } // small memory leak per DiceQueue + if ( newht != 0 && lastheight != newht ) + { + lastheight = newht; + newblock = 1; + fprintf(stderr,"dicefinish process lastheight.%d <- newht.%d\n",lastheight,newht); + } else newblock = 0; + now = (uint32_t)time(NULL); + for (iter=-1; iter<=1; iter+=2) + { + vin0_needed = 0; + DL_FOREACH_SAFE(DICEFINISH_LIST,ptr,tmp) + { + if ( ptr->iswin != iter ) + continue; + if ( ptr->revealed != 0 && now > ptr->revealed+3600 ) + { + fprintf(stderr,"purge %s\n",ptr->bettxid.GetHex().c_str()); + dicefinish_delete(ptr); + continue; + } + if ( ptr->bettxid_ready == 0 ) + { + if ( myGetTransaction(ptr->bettxid,betTx,hashBlock) != 0 && hashBlock != zeroid ) + ptr->bettxid_ready = (uint32_t)time(NULL); + else if ( mytxid_inmempool(ptr->bettxid) != 0 ) + ptr->bettxid_ready = (uint32_t)time(NULL); + } + else if ( newblock != 0 && (myGetTransaction(ptr->bettxid,betTx,hashBlock) == 0 || now > ptr->bettxid_ready+600) ) + { + fprintf(stderr,"ORPHANED bettxid.%s\n",ptr->bettxid.GetHex().c_str()); + dicefinish_delete(ptr); + continue; + } + else if ( newblock != 0 && ptr->txid != zeroid ) + { + if ( myGetTransaction(ptr->txid,finishTx,hashBlock) == 0 ) + { + ptr->orphaned++; + fprintf(stderr,"ORPHANED.%d finish txid.%s\n",ptr->orphaned,ptr->txid.GetHex().c_str()); + if ( ptr->orphaned < 4 ) + continue; + if ( ptr->rawtx.empty() == 0 ) + ptr->rawtx.clear(); + ptr->txid = zeroid; + unstringbits(name,ptr->sbits); + result = 0; + ptr->orphaned = 0; + res = DiceBetFinish(ptr->funcid,entropyused,entropyvout,&result,0,name,ptr->fundingtxid,ptr->bettxid,ptr->iswin,zeroid,-2); + if ( ptr->entropyused == zeroid ) + { + ptr->entropyused = entropyused; + ptr->entropyvout = entropyvout; + } + if ( entropyused != ptr->entropyused || entropyvout != ptr->entropyvout ) + { + fprintf(stderr,"WARNING entropy %s != %s || %d != %d\n",entropyused.GetHex().c_str(),ptr->entropyused.GetHex().c_str(),entropyvout,ptr->entropyvout); + } + if ( result > 0 ) + { + ptr->rawtx = res; + fprintf(stderr,"send refund!\n"); + mySenddicetransaction(ptr->rawtx,ptr->entropyused,ptr->entropyvout,ptr->bettxid,ptr->betTx,ptr->funcid,ptr); + } + continue; + } + } + if ( ptr->bettxid_ready != 0 ) + { + if ( newblock != 0 && ptr->txid != zeroid ) + { + CCduration(numblocks,ptr->txid); + //fprintf(stderr,"duration finish txid.%s\n",ptr->txid.GetHex().c_str()); + if ( numblocks == 0 ) + mySenddicetransaction(ptr->rawtx,ptr->entropyused,ptr->entropyvout,ptr->bettxid,ptr->betTx,ptr->funcid,ptr); + else continue; + } + if ( ptr->txid == zeroid ) + vin0_needed++; + } + } + if ( vin0_needed > 0 ) + { + num = 0; + //fprintf(stderr,"iter.%d vin0_needed.%d\n",iter,vin0_needed); + utxos = (struct dicefinish_utxo *)calloc(vin0_needed,sizeof(*utxos)); + if ( (n= dicefinish_utxosget(num,utxos,vin0_needed,coinaddr)) > 0 ) + { + //fprintf(stderr,"iter.%d vin0_needed.%d got %d\n",iter,vin0_needed,n); + m = 0; + DL_FOREACH_SAFE(DICEFINISH_LIST,ptr,tmp) + { + if ( ptr->iswin != iter ) + continue; + if ( ptr->revealed != 0 && time(NULL) > ptr->revealed+3600 ) + { + fprintf(stderr,"purge2 %s\n",ptr->bettxid.GetHex().c_str()); + dicefinish_delete(ptr); + continue; + } + if ( ptr->txid != zeroid ) + { + CCduration(numblocks,ptr->txid); + //fprintf(stderr,"numblocks %s %d\n",ptr->txid.GetHex().c_str(),numblocks); + if ( numblocks > 0 ) + continue; + } + if ( ptr->bettxid_ready != 0 && ptr->rawtx.size() == 0 && dice_betspent((char *)"dicefinish",ptr->bettxid) <= 0 && ptr->txid == zeroid ) + { + unstringbits(name,ptr->sbits); + result = 0; + res = DiceBetFinish(ptr->funcid,entropyused,entropyvout,&result,0,name,ptr->fundingtxid,ptr->bettxid,ptr->iswin,utxos[m].txid,utxos[m].vout); + if ( ptr->entropyused == zeroid ) + { + ptr->entropyused = entropyused; + ptr->entropyvout = entropyvout; + } + if ( entropyused != ptr->entropyused || entropyvout != ptr->entropyvout ) + { + fprintf(stderr,"WARNING entropy %s != %s || %d != %d\n",entropyused.GetHex().c_str(),ptr->entropyused.GetHex().c_str(),entropyvout,ptr->entropyvout); + } + if ( result > 0 ) + { + ptr->rawtx = res; + mySenddicetransaction(ptr->rawtx,ptr->entropyused,ptr->entropyvout,ptr->bettxid,ptr->betTx,ptr->funcid,ptr); + } + else if ( result != -2 ) + { + fprintf(stderr,"error doing the dicefinish %d of %d process %s %s using %s/v%d need %.8f\n",m,n,iter<0?"loss":"win",ptr->bettxid.GetHex().c_str(),utxos[m].txid.GetHex().c_str(),utxos[m].vout,(double)(iter<0 ? 0 : ptr->winamount)/COIN); + if ( ptr->rawtx.empty() == 0 ) + ptr->rawtx.clear(); + ptr->txid = zeroid; + } + if ( ++m >= n ) + break; + } + else + { + //fprintf(stderr,"error ready.%d dicefinish %d of %d process %s %s using need %.8f finish.%s size.%d betspent.%d\n",ptr->bettxid_ready,m,n,iter<0?"loss":"win",ptr->bettxid.GetHex().c_str(),(double)(iter<0 ? 0 : ptr->winamount)/COIN,ptr->txid.GetHex().c_str(),(int32_t)ptr->rawtx.size(),dice_betspent((char *)"dicefinish",ptr->bettxid)); + } + } + } + else if ( newblock != 0 ) + dicefinish_utxosget(num,0,0,coinaddr); + free(utxos); + if ( newblock != 0 && num < DICE_MINUTXOS ) + { + char *cmd = (char *)malloc(100 * 128); + sprintf(cmd,"./komodo-cli -ac_name=%s sendmany \"\" \"{\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002,\\\"%s\\\":0.0002}\"",ASSETCHAINS_SYMBOL,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr,coinaddr); + n = sqrt((DICE_MINUTXOS - num) / 100)*2 + 1; + fprintf(stderr,"num normal 0.0002 utxos.%d < %d -> n.%d\n",num,DICE_MINUTXOS,n); + for (i=0; ifundingtxid = fundingtxid; + ptr->bettxid = bettxid; + ptr->betTx = betTx; + ptr->sbits = sbits; + ptr->iswin = iswin; + ptr->winamount = betTx.vout[1].nValue * ((betTx.vout[2].nValue - txfee)+1); + ptr->entropyvout = entropyvout; + DL_APPEND(DICEFINISH_LIST,ptr); + fprintf(stderr,"queued %dx iswin.%d %.8f -> %.8f %s\n",(int32_t)(betTx.vout[2].nValue - txfee),iswin,(double)betTx.vout[1].nValue/COIN,(double)ptr->winamount/COIN,bettxid.GetHex().c_str()); + } + else + { + //fprintf(stderr,"DiceQueue status bettxid.%s already in list\n",bettxid.GetHex().c_str()); + //_dicehash_clear(bettxid); + } + pthread_mutex_unlock(&DICE_MUTEX); } CPubKey DiceFundingPk(CScript scriptPubKey) @@ -177,11 +557,16 @@ CPubKey DiceFundingPk(CScript scriptPubKey) return(pk); } -uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv) // max 1 vout per txid used +uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv,int32_t vout,int32_t usevout) { int32_t i; uint8_t _entropy[32],_hentropy[32]; bits256 tmp256,txidpub,txidpriv,mypriv,mypub,ssecret,ssecret2; uint256 hentropy; memset(&hentropy,0,32); endiancpy(txidpriv.bytes,(uint8_t *)&_txidpriv,32); + if ( usevout != 0 ) + { + txidpriv.bytes[1] ^= (vout & 0xff); + txidpriv.bytes[2] ^= ((vout>>8) & 0xff); + } txidpriv.bytes[0] &= 0xf8, txidpriv.bytes[31] &= 0x7f, txidpriv.bytes[31] |= 0x40; txidpub = curve25519(txidpriv,curve25519_basepoint9()); @@ -218,12 +603,15 @@ int32_t dice_5nibbles(uint8_t *fivevals) return(((int32_t)fivevals[0]<<16) + ((int32_t)fivevals[1]<<12) + ((int32_t)fivevals[2]<<8) + ((int32_t)fivevals[3]<<4) + ((int32_t)fivevals[4])); } -uint64_t DiceCalc(int64_t bet,int64_t odds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks,uint256 houseentropy,uint256 bettorentropy) +uint64_t DiceCalc(int64_t bet,int64_t vout2,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks,uint256 houseentropy,uint256 bettorentropy) { - uint8_t buf[64],_house[32],_bettor[32],_hash[32],hash[32],hash16[64]; uint64_t winnings; arith_uint256 house,bettor; char str[65],str2[65]; int32_t i,modval; - if ( odds < 10000 ) + uint8_t buf[64],_house[32],_bettor[32],_hash[32],hash[32],hash16[64]; uint64_t odds,winnings; arith_uint256 house,bettor; char str[65],str2[65]; int32_t i,modval; + if ( vout2 <= 10000 ) + { + fprintf(stderr,"unexpected vout2.%llu\n",(long long)vout2); return(0); - else odds -= 10000; + } + else odds = (vout2 - 10000); if ( bet < minbet || bet > maxbet ) { CCerror = strprintf("bet size violation %.8f",(double)bet/COIN); @@ -236,7 +624,6 @@ uint64_t DiceCalc(int64_t bet,int64_t odds,int64_t minbet,int64_t maxbet,int64_t fprintf(stderr,"%s\n", CCerror.c_str() ); return(0); } - //fprintf(stderr,"calc house entropy %s vs bettor %s\n",uint256_str(str,houseentropy),uint256_str(str2,bettorentropy)); endiancpy(buf,(uint8_t *)&houseentropy,32); endiancpy(&buf[32],(uint8_t *)&bettorentropy,32); @@ -248,6 +635,7 @@ uint64_t DiceCalc(int64_t bet,int64_t odds,int64_t minbet,int64_t maxbet,int64_t vcalc_sha256(0,(uint8_t *)&_bettor,buf,64); endiancpy((uint8_t *)&bettor,_bettor,32); winnings = 0; + //fprintf(stderr,"calc house entropy %s vs bettor %s\n",uint256_str(str,*(uint256 *)&house),uint256_str(str2,*(uint256 *)&bettor)); if ( odds > 1 ) { if ( 0 ) @@ -278,7 +666,7 @@ uint64_t DiceCalc(int64_t bet,int64_t odds,int64_t minbet,int64_t maxbet,int64_t break; } } - fprintf(stderr,"modval %d vs %d\n",modval,(int32_t)(10000/(odds+1))); + //fprintf(stderr,"modval %d vs %d\n",modval,(int32_t)(10000/(odds+1))); if ( modval < 10000/(odds+1) ) winnings = bet * (odds+1); } @@ -317,8 +705,10 @@ CScript EncodeDiceOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid,uint25 uint8_t DecodeDiceOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid,uint256 &hash,uint256 &proof) { std::vector vopret; uint8_t *script,e,f,funcid; int64_t minbet,maxbet,maxodds,timeoutblocks; + //script = (uint8_t *)scriptPubKey.data(); + //fprintf(stderr,"decode %02x %02x %02x\n",script[0],script[1],script[2]); GetOpReturnData(scriptPubKey,vopret); - if ( vopret.size() > 2 ) + if ( vopret.size() > 2 )//&& script[0] == 0x6a ) { script = (uint8_t *)vopret.data(); if ( script[0] == EVAL_DICE ) @@ -334,7 +724,7 @@ uint8_t DecodeDiceOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits } else if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid; ss >> hash; ss >> proof) != 0 ) { - if ( e == EVAL_DICE && (f == 'B' || f == 'W' || f == 'L' || f == 'T' || f == 'E') ) + if ( e == EVAL_DICE && (f == 'R' || f == 'B' || f == 'W' || f == 'L' || f == 'T' || f == 'E') ) return(f); else fprintf(stderr,"mismatched e.%02x f.(%c)\n",e,f); } @@ -379,7 +769,7 @@ int64_t DiceAmounts(uint64_t &inputs,uint64_t &outputs,struct CCcontract_info *c return eval->Invalid("always should find vin, but didnt"); else { - if ( (assetoshis= IsDicevout(cp,vinTx,tx.vin[i].prevout.n,refsbits,reffundingtxid)) != 0 ) + if ( (assetoshis= IsDicevout(cp,vinTx,(int32_t)tx.vin[i].prevout.n,refsbits,reffundingtxid)) != 0 ) inputs += assetoshis; } } @@ -401,7 +791,7 @@ bool DiceIsmine(const CScript scriptPubKey) return(strcmp(destaddr,myaddr) == 0); } -int32_t DiceIsWinner(uint256 &entropy,uint256 txid,CTransaction tx,CTransaction vinTx,uint256 bettorentropy,uint64_t sbits,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks,uint256 fundingtxid) +int32_t DiceIsWinner(uint256 &entropy,int32_t &entropyvout,uint256 txid,CTransaction tx,CTransaction vinTx,uint256 bettorentropy,uint64_t sbits,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks,uint256 fundingtxid) { uint64_t vinsbits,winnings; uint256 vinproof,vinfundingtxid,hentropy,hentropy2; uint8_t funcid; //char str[65],str2[65]; @@ -409,11 +799,18 @@ int32_t DiceIsWinner(uint256 &entropy,uint256 txid,CTransaction tx,CTransaction { if ( ((funcid= DecodeDiceOpRet(txid,vinTx.vout[vinTx.vout.size()-1].scriptPubKey,vinsbits,vinfundingtxid,hentropy,vinproof)) == 'E' || funcid == 'W' || funcid == 'L') && sbits == vinsbits && fundingtxid == vinfundingtxid ) { - hentropy2 = DiceHashEntropy(entropy,vinTx.vin[0].prevout.hash); + hentropy2 = DiceHashEntropy(entropy,vinTx.vin[0].prevout.hash,vinTx.vin[0].prevout.n,0); + entropyvout = vinTx.vin[0].prevout.n; + //fprintf(stderr,"bettxid %s -> vin0 %s/v%d -> %s\n",txid.GetHex().c_str(),vinTx.vin[0].prevout.hash.GetHex().c_str(),entropyvout,entropy.GetHex().c_str()); + if ( hentropy != hentropy2 ) + { + hentropy2 = DiceHashEntropy(entropy,vinTx.vin[0].prevout.hash,vinTx.vin[0].prevout.n,1); + //fprintf(stderr,"alt bettxid %s -> vin0 %s/v%d -> %s\n",txid.GetHex().c_str(),vinTx.vin[0].prevout.hash.GetHex().c_str(),entropyvout,entropy.GetHex().c_str()); + } if ( hentropy == hentropy2 ) { winnings = DiceCalc(tx.vout[1].nValue,tx.vout[2].nValue,minbet,maxbet,maxodds,timeoutblocks,entropy,bettorentropy); - char str[65]; fprintf(stderr,"%s winnings %.8f bet %.8f at odds %d:1\n",uint256_str(str,tx.GetHash()),(double)winnings/COIN,(double)tx.vout[1].nValue/COIN,(int32_t)(tx.vout[2].nValue-10000)); + //char str[65]; fprintf(stderr,"%s winnings %.8f bet %.8f at odds %d:1\n",uint256_str(str,tx.GetHash()),(double)winnings/COIN,(double)tx.vout[1].nValue/COIN,(int32_t)(tx.vout[2].nValue-10000)); //fprintf(stderr,"I am house entropy %.8f entropy.(%s) vs %s -> winnings %.8f\n",(double)vinTx.vout[0].nValue/COIN,uint256_str(str,entropy),uint256_str(str2,hash),(double)winnings/COIN); if ( winnings == 0 ) { @@ -425,9 +822,13 @@ int32_t DiceIsWinner(uint256 &entropy,uint256 txid,CTransaction tx,CTransaction // queue 'W' winning tx return(1); } - } else fprintf(stderr,"hentropy != hentropy2\n"); + } + else + { + fprintf(stderr,"both hentropy != hentropy2\n"); + } } else fprintf(stderr,"funcid.%c sbits %llx vs %llx cmp.%d\n",funcid,(long long)sbits,(long long)vinsbits,fundingtxid == vinfundingtxid); - } //else fprintf(stderr,"notmine or not CC\n"); + } //else fprintf(stderr,"notmine.%d or not CC.%d\n",DiceIsmine(vinTx.vout[1].scriptPubKey) != 0,vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0); return(0); } @@ -441,7 +842,7 @@ bool DiceVerifyTimeout(CTransaction &betTx,int32_t timeoutblocks) bool DiceValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, uint32_t nIn) { - uint256 txid,fundingtxid,vinfundingtxid,vinhentropy,vinproof,hashBlock,hash,proof,entropy; int64_t minbet,maxbet,maxodds,timeoutblocks,odds,winnings; uint64_t vinsbits,sbits,amount,inputs,outputs,txfee=10000; int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,iswin; uint8_t funcid; CScript fundingPubKey; CTransaction fundingTx,vinTx,vinofvinTx; char CCaddr[64]; + uint256 txid,fundingtxid,vinfundingtxid,vinhentropy,vinproof,hashBlock,hash,proof,entropy; int64_t minbet,maxbet,maxodds,timeoutblocks,odds,winnings; uint64_t vinsbits,refsbits=0,sbits,amount,inputs,outputs,txfee=10000; int32_t numvins,entropyvout,numvouts,preventCCvins,preventCCvouts,i,iswin; uint8_t funcid; CScript fundingPubKey; CTransaction fundingTx,vinTx,vinofvinTx; char CCaddr[64]; numvins = tx.vin.size(); numvouts = tx.vout.size(); preventCCvins = preventCCvouts = -1; @@ -450,7 +851,7 @@ bool DiceValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, else { txid = tx.GetHash(); - if ( (funcid= DecodeDiceOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 ) + if ( (funcid= DecodeDiceOpRet(txid,tx.vout[numvouts-1].scriptPubKey,refsbits,fundingtxid,hash,proof)) != 0 ) { if ( eval->GetTxUnconfirmed(fundingtxid,fundingTx,hashBlock) == 0 ) return eval->Invalid("cant find fundingtxid"); @@ -459,6 +860,11 @@ bool DiceValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, if ( maxodds > 9999 ) return eval->Invalid("maxodds too big"); fundingPubKey = fundingTx.vout[1].scriptPubKey; + if ( sbits != refsbits ) + { + fprintf(stderr,"VALIDATION ERROR: sbits %llx != refsbits %llx\n",(long long)sbits,(long long)refsbits); + //return eval->Invalid("mismatched diceplan"); + } switch ( funcid ) { case 'F': @@ -497,36 +903,51 @@ bool DiceValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, return eval->Invalid("always should find vin.0, but didnt for bet"); else if ( vinTx.vout[1].scriptPubKey != fundingPubKey ) return eval->Invalid("entropy tx not fundingPubKey for bet"); - else if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,vinTx.vout[tx.vin[0].prevout.n].nValue) == 0 ) + else if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,(int64_t)vinTx.vout[tx.vin[0].prevout.n].nValue) == 0 ) + { + fprintf(stderr,"%s prevout.%d %.8f\n",tx.vin[0].prevout.hash.GetHex().c_str(),(int32_t)tx.vin[0].prevout.n,(double)vinTx.vout[tx.vin[0].prevout.n].nValue/COIN); return eval->Invalid("vout[0] != entropy nValue for bet"); + } else if ( ConstrainVout(tx.vout[1],1,cp->unspendableCCaddr,0) == 0 ) return eval->Invalid("vout[1] constrain violation for bet"); - else if ( tx.vout[2].nValue > txfee+maxodds || tx.vout[2].nValue < txfee ) + else if ( tx.vout[2].nValue > txfee+maxodds || tx.vout[2].nValue <= txfee ) return eval->Invalid("vout[2] nValue violation for bet"); else if ( eval->GetTxUnconfirmed(vinTx.vin[0].prevout.hash,vinofvinTx,hashBlock) == 0 || vinofvinTx.vout.size() < 1 ) return eval->Invalid("always should find vinofvin.0, but didnt for bet"); else if ( vinTx.vin[0].prevout.hash != fundingtxid ) { - if ( vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey != fundingPubKey ) + if ( (int32_t)vinTx.vin[0].prevout.n < 0 || vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey != fundingPubKey ) { - uint8_t *ptr0,*ptr1; int32_t i; char str[65]; - fprintf(stderr,"bidTx.%s\n",uint256_str(str,txid)); - fprintf(stderr,"entropyTx.%s v%d\n",uint256_str(str,tx.vin[0].prevout.hash),(int32_t)tx.vin[0].prevout.n); - fprintf(stderr,"entropyTx vin0 %s v%d\n",uint256_str(str,vinTx.vin[0].prevout.hash),(int32_t)vinTx.vin[0].prevout.n); - const CScript &s0 = vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey; - for (i=0; iInvalid("vin1 of entropy tx not fundingPubKey for bet"); + uint8_t *ptr0,*ptr1; int32_t i; char str[65],addr0[64],addr1[64]; + Getscriptaddress(addr0,vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey); + Getscriptaddress(addr1,fundingPubKey); + if ( strcmp(addr0,addr1) != 0 ) + { + fprintf(stderr,"%s != %s betTx.%s\n",addr0,addr1,uint256_str(str,txid)); + fprintf(stderr,"entropyTx.%s v%d\n",uint256_str(str,tx.vin[0].prevout.hash),(int32_t)tx.vin[0].prevout.n); + fprintf(stderr,"entropyTx vin0 %s v%d\n",uint256_str(str,vinTx.vin[0].prevout.hash),(int32_t)vinTx.vin[0].prevout.n); + ptr0 = (uint8_t *)&vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey[0]; + ptr1 = (uint8_t *)&fundingPubKey[0]; + for (i=0; iInvalid("vin1 of entropy tx not fundingPubKey for bet"); + } } } - if ( (iswin= DiceIsWinner(entropy,txid,tx,vinTx,hash,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 ) + if ( (iswin= DiceIsWinner(entropy,entropyvout,txid,tx,vinTx,hash,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 ) { // will only happen for fundingPubKey - DiceQueue(iswin,sbits,fundingtxid,txid); + if ( KOMODO_INSYNC != 0 ) + DiceQueue(iswin,sbits,fundingtxid,txid,tx,entropyvout); + } + else + { + //fprintf(stderr,"why does node1 get VALIDATION ERROR: invalid dicebet bettxid %s\n",txid.GetHex().c_str()); + //return eval->Invalid("invalid dicebet bettxid"); } break; // make sure all funding txid are from matching sbits and fundingtxid!! @@ -579,7 +1000,7 @@ bool DiceValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, return eval->Invalid("vout[2] payut mismatch for win/timeout"); else if ( inputs != (outputs + tx.vout[2].nValue) && inputs != (outputs + tx.vout[2].nValue+txfee) ) { - fprintf(stderr,"inputs %.8f != outputs %.8f + %.8f\n",(double)inputs/COIN,(double)outputs/COIN,(double)tx.vout[2].nValue/COIN); + fprintf(stderr,"inputs %.8f != outputs %.8f (%.8f %.8f %.8f %.8f)\n",(double)inputs/COIN,(double)outputs/COIN,(double)tx.vout[0].nValue/COIN,(double)tx.vout[1].nValue/COIN,(double)tx.vout[2].nValue/COIN,(double)tx.vout[3].nValue/COIN); return eval->Invalid("CC funds mismatch for win/timeout"); } else if ( tx.vout[3].scriptPubKey != fundingPubKey ) @@ -603,8 +1024,29 @@ bool DiceValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, else if ( DiceVerifyTimeout(vinTx,timeoutblocks) == 0 ) return eval->Invalid("invalid timeout claim for timeout"); break; + case 'R': + if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 ) + return eval->Invalid("always should find vin.0, but didnt for refund"); + else if ( vinTx.vout[tx.vin[0].prevout.n].scriptPubKey != fundingPubKey ) + { + char fundingaddr[64],cmpaddr[64]; + Getscriptaddress(fundingaddr,fundingPubKey); + Getscriptaddress(cmpaddr,vinTx.vout[tx.vin[0].prevout.n].scriptPubKey); + if ( strcmp(cmpaddr,fundingaddr) != 0 ) + { + fprintf(stderr,"cmpaddr.%s != fundingaddr.%s\n",cmpaddr,fundingaddr); + return eval->Invalid("vin.0 not from fundingPubKey for refund"); + } + } + if ( (rand() % 1000) == 0 ) + fprintf(stderr,"add more validation for refunds\n"); + break; + default: + fprintf(stderr,"illegal dice funcid.(%c)\n",funcid); + return eval->Invalid("unexpected dice funcid"); + break; } - } + } else return eval->Invalid("unexpected dice missing funcid"); return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts)); } return(true); @@ -612,15 +1054,18 @@ bool DiceValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, uint64_t AddDiceInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint64_t total,int32_t maxinputs,uint64_t refsbits,uint256 reffundingtxid) { - char coinaddr[64],str[65]; uint64_t sbits,nValue,totalinputs = 0; uint256 txid,hash,proof,hashBlock,fundingtxid; CTransaction tx; int32_t j,vout,n = 0; uint8_t funcid; + char coinaddr[64],str[65]; uint64_t threshold,sbits,nValue,totalinputs = 0; uint256 txid,hash,proof,hashBlock,fundingtxid; CTransaction tx; int32_t j,vout,n = 0; uint8_t funcid; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); SetCCunspents(unspentOutputs,coinaddr); + if ( maxinputs > 0 ) + threshold = total / maxinputs; + else threshold = total / 64; for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; - if ( it->second.satoshis < 1000000 ) + if ( vout != 0 || it->second.satoshis < threshold ) continue; //fprintf(stderr,"(%s) %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); for (j=0; j 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 ) + if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 ) { if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 ) { char str[65],sstr[16]; unstringbits(sstr,sbits); - fprintf(stderr,"(%c) %.8f %s %s\n",funcid,(double)tx.vout[0].nValue/COIN,sstr,uint256_str(str,txid)); if ( sbits == refsbits && (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid ) { if ( funcid == 'F' || funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T' ) { if ( total != 0 && maxinputs != 0 ) + { + fprintf(stderr,"use (%c) %.8f %s %s/v%d\n",funcid,(double)tx.vout[0].nValue/COIN,sstr,uint256_str(str,txid),vout); mtx.vin.push_back(CTxIn(txid,vout,CScript())); + } totalinputs += it->second.satoshis; n++; if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) @@ -653,64 +1100,97 @@ uint64_t AddDiceInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubK return(totalinputs); } -int64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbits,struct CCcontract_info *cp,CPubKey dicepk,uint256 reffundingtxid) +int64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbits,struct CCcontract_info *cp,CPubKey dicepk,uint256 reffundingtxid, int32_t &entropytxs,bool random) { - char coinaddr[64],str[65]; uint64_t sbits; int64_t nValue,totalinputs = 0; uint256 hash,txid,proof,hashBlock,fundingtxid; CScript fundingPubKey; CTransaction tx,vinTx; int32_t vout,first=0,n=0; uint8_t funcid; + char coinaddr[64],str[65]; uint64_t sbits; int64_t nValue,sum,totalinputs = 0; uint256 hash,txid,proof,hashBlock,fundingtxid; CScript fundingPubKey; CTransaction tx,vinTx; int32_t vout,first=0,n=0,i=0,pendingbets=0; uint8_t funcid; std::vector > unspentOutputs; entropyval = 0; entropytxid = zeroid; - if ( GetTransaction(reffundingtxid,tx,hashBlock,false) != 0 && tx.vout.size() > 1 && ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,0) != 0 ) + if ( myGetTransaction(reffundingtxid,tx,hashBlock) != 0 && tx.vout.size() > 1 && ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,0) != 0 ) { fundingPubKey = tx.vout[1].scriptPubKey; } else return(0); GetCCaddress(cp,coinaddr,dicepk); SetCCunspents(unspentOutputs,coinaddr); entropyval = 0; + int loops = 0; + int numtxs = unspentOutputs.size()/2; + int startfrom = rand() % (numtxs+1); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; - if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 ) + if ( vout != 0 ) + continue; + sum += it->second.satoshis; + loops++; + if (random) { + if ( loops < startfrom ) + continue; + if ( (rand() % 100) < 90 ) + continue; + } + if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 ) { - //char str[65],str2[65]; - if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 ) + if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 && sbits == refsbits ) { + if ( funcid == 'B' ) + { + pendingbets++; + fprintf(stderr,"%d: %s/v%d (%c %.8f) %.8f %.8f\n",n,uint256_str(str,txid),vout,funcid,(double)it->second.satoshis/COIN,(double)totalinputs/COIN,(double)sum/COIN); + } if ( (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid ) { - if ( refsbits == sbits && (nValue= IsDicevout(cp,tx,vout,refsbits,reffundingtxid)) > 10000 && (funcid == 'F' || funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T') ) + //fprintf(stderr,"%d: %s/v%d (%c %.8f) %.8f %.8f\n",n,uint256_str(str,txid),vout,funcid,(double)it->second.satoshis/COIN,(double)totalinputs/COIN,(double)sum/COIN); + if ( (nValue= IsDicevout(cp,tx,vout,refsbits,reffundingtxid)) >= 10000 && (funcid == 'F' || funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T') ) { - fprintf(stderr,"%s.(%c %.8f) ",uint256_str(str,txid),funcid,(double)nValue/COIN); - if ( funcid != 'F' && funcid != 'T' ) + if ( funcid == 'L' || funcid == 'W' || funcid == 'E' ) n++; totalinputs += nValue; if ( first == 0 && (funcid == 'E' || funcid == 'W' || funcid == 'L') ) { - fprintf(stderr,"check first\n"); - if ( fundingPubKey == tx.vout[1].scriptPubKey ) + //fprintf(stderr,"check first\n"); + if ( tx.vout.size() > 1 && fundingPubKey == tx.vout[1].scriptPubKey ) { - if ( funcid == 'E' && fundingtxid != tx.vin[0].prevout.hash ) + if ( myGetTransaction(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 || (int32_t)tx.vin[0].prevout.n < 0 ) { - if ( GetTransaction(tx.vin[0].prevout.hash,vinTx,hashBlock,false) == 0 ) + fprintf(stderr,"cant find entropy vin0 %s or vin0prev %d vouts[%d], iscoinbase.%d\n",uint256_str(str,tx.vin[0].prevout.hash),(int32_t)tx.vin[0].prevout.n,(int32_t)vinTx.vout.size(),(int32_t)vinTx.vin.size()); + continue; + } + if ( (int32_t)vinTx.vin[0].prevout.n < 0 || vinTx.vout.size() < 2 ) + { + fprintf(stderr,"skip coinbase or strange entropy tx\n"); + continue; + } + //if ( fundingtxid != tx.vin[0].prevout.hash && vinTx.vout[tx.vin[0].prevout.n].scriptPubKey != fundingPubKey ) + if ( fundingtxid != tx.vin[0].prevout.hash && vinTx.vout[1].scriptPubKey != fundingPubKey ) + { + uint8_t *ptr0,*ptr1; int32_t i; char str[65],addr0[64],addr1[64]; + Getscriptaddress(addr0,vinTx.vout[1].scriptPubKey); + Getscriptaddress(addr1,fundingPubKey); + if ( strcmp(addr0,addr1) != 0 ) { - fprintf(stderr,"cant find entropy vin0 %s or vin0prev %d vouts[%d]\n",uint256_str(str,tx.vin[0].prevout.hash),tx.vin[0].prevout.n,(int32_t)vinTx.vout.size()); - continue; - } - if ( vinTx.vout[tx.vin[0].prevout.n].scriptPubKey != fundingPubKey ) - { - uint8_t *ptr0,*ptr1; int32_t i; char str[65]; - const CScript &s0 = vinTx.vout[tx.vin[0].prevout.n].scriptPubKey; - for (i=0; i 1 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 ) + if ( myGetTransaction(fundingtxid,tx,hashBlock) != 0 && tx.vout.size() > 1 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 ) { if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' && sbits == refsbits ) { @@ -758,7 +1245,7 @@ bool DicePlanExists(CScript &fundingPubKey,uint256 &fundingtxid,struct CCcontrac txid = it->first.txhash; if ( fundingtxid != zeroid && txid != fundingtxid ) continue; - if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 ) + if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 ) { if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' ) { @@ -797,7 +1284,7 @@ struct CCcontract_info *Diceinit(CScript &fundingPubKey,uint256 reffundingtxid,s UniValue DiceInfo(uint256 diceid) { UniValue result(UniValue::VOBJ); CPubKey dicepk; uint256 hashBlock,entropytxid; CTransaction vintx; int64_t minbet,maxbet,maxodds,timeoutblocks; uint64_t sbits,funding,entropyval; char str[67],numstr[65]; struct CCcontract_info *cp,C; - if ( GetTransaction(diceid,vintx,hashBlock,false) == 0 ) + if ( myGetTransaction(diceid,vintx,hashBlock) == 0 ) { fprintf(stderr,"cant find fundingtxid\n"); ERR_RESULT("cant find fundingtxid"); @@ -822,9 +1309,11 @@ UniValue DiceInfo(uint256 diceid) result.push_back(Pair("timeoutblocks",timeoutblocks)); cp = CCinit(&C,EVAL_DICE); dicepk = GetUnspendable(cp,0); - funding = DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,diceid); + int32_t entropytxs; + funding = DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,diceid,entropytxs,false); sprintf(numstr,"%.8f",(double)funding/COIN); result.push_back(Pair("funding",numstr)); + result.push_back(Pair("entropytxs",entropytxs)); return(result); } @@ -836,7 +1325,7 @@ UniValue DiceList() for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) { txid = it->first.txhash; - if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) + if ( myGetTransaction(txid,vintx,hashBlock) != 0 ) { if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 0 ) { @@ -865,7 +1354,7 @@ std::string DiceCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t memset(&zero,0,sizeof(zero)); if ( (cp= Diceinit(fundingPubKey,zero,&C,planstr,txfee,mypk,dicepk,sbits,a,b,c,d)) == 0 ) { - CCerror = "Diceinit error in create funding"; + CCerror = "Diceinit error in create funding, is your transaction confirmed?"; fprintf(stderr,"%s\n", CCerror.c_str() ); return(""); } @@ -890,8 +1379,10 @@ std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int6 fprintf(stderr,"%s\n", CCerror.c_str() ); return(""); } - if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 ) + if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 ) { + CCerror = "Diceinit error in add funding, is your transaction confirmed?"; return(""); + } scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG; if ( 0 ) { @@ -905,9 +1396,9 @@ std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int6 } if ( scriptPubKey == fundingPubKey ) { - if ( AddNormalinputs(mtx,mypk,amount+2*txfee,60) > 0 ) + if ( AddNormalinputs(mtx,mypk,amount+2*txfee,1) > 0 ) { - hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash); + hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash,mtx.vin[0].prevout.n,1); mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,dicepk)); mtx.vout.push_back(CTxOut(txfee,fundingPubKey)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('E',sbits,fundingtxid,hentropy,zeroid))); @@ -924,66 +1415,77 @@ std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int6 std::string DiceBet(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t bet,int32_t odds) { - CMutableTransaction mtx; CScript fundingPubKey; CPubKey mypk,dicepk; uint64_t sbits,entropyval; int64_t funding,minbet,maxbet,maxodds,timeoutblocks; uint256 entropytxid,entropy,hentropy; struct CCcontract_info *cp,C; + CMutableTransaction mtx; CScript fundingPubKey; CPubKey mypk,dicepk; uint64_t sbits,entropyval,entropyval2; int64_t funding,minbet,maxbet,maxodds,timeoutblocks; uint256 entropytxid,entropytxid2,entropy,hentropy; struct CCcontract_info *cp,C; if ( bet < 0 ) { CCerror = "bet must be positive"; - fprintf(stderr,"%s\n", CCerror.c_str() ); return(""); } - if ( odds < 1 || odds > 9999 ) + if ( odds < 2 || odds > 9999 ) { - CCerror = "odds must be between 1 and 9999"; - fprintf(stderr,"%s\n", CCerror.c_str() ); + CCerror = "odds must be between 2 and 9999"; return(""); } - if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 ) + if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 ) { + CCerror = "Diceinit error in bet, is your transaction confirmed?"; return(""); + } if ( bet < minbet || bet > maxbet || odds > maxodds ) { CCerror = strprintf("Dice plan %s illegal bet %.8f: minbet %.8f maxbet %.8f or odds %d vs max.%d\n",planstr,(double)bet/COIN,(double)minbet/COIN,(double)maxbet/COIN,(int32_t)odds,(int32_t)maxodds); - fprintf(stderr,"%s\n", CCerror.c_str() ); return(""); } - if ( (funding= DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,fundingtxid)) >= 2*bet*odds+txfee && entropyval != 0 ) + int32_t entropytxs=0,emptyvar=0; + funding = DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,fundingtxid,entropytxs,false); + DicePlanFunds(entropyval2,entropytxid2,sbits,cp,dicepk,fundingtxid,emptyvar,true); + if ( entropyval2 != 0 && entropytxid2 != zeroid ) { + entropyval = entropyval2; + entropytxid = entropytxid2; + } + if ( ( funding >= 2*bet*odds+txfee && entropyval != 0 ) ) + { + if ( entropytxs < 100 ) { + CCerror = "Your dealer is broke, find a new casino."; + return(""); + } if ( myIsutxo_spentinmempool(entropytxid,0) != 0 ) { CCerror = "entropy txid is spent"; - fprintf(stderr,"%s\n", CCerror.c_str() ); return(""); } mtx.vin.push_back(CTxIn(entropytxid,0,CScript())); if ( AddNormalinputs(mtx,mypk,bet+2*txfee+odds,60) > 0 ) { - hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash); + hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash,mtx.vin[0].prevout.n,1); mtx.vout.push_back(MakeCC1vout(cp->evalcode,entropyval,dicepk)); mtx.vout.push_back(MakeCC1vout(cp->evalcode,bet,dicepk)); mtx.vout.push_back(CTxOut(txfee+odds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('B',sbits,fundingtxid,entropy,zeroid))); - } else fprintf(stderr,"cant find enough normal inputs for %.8f, plan funding %.8f\n",(double)bet/COIN,(double)funding/COIN); + } else CCerror = "cant find enough normal inputs for %.8f, plan funding %.8f\n"; } if ( entropyval == 0 && funding != 0 ) CCerror = "cant find dice entropy inputs"; else CCerror = "cant find dice input"; - fprintf(stderr,"%s\n", CCerror.c_str() ); return(""); } -std::string DiceBetFinish(int32_t *resultp,uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid,int32_t winlosetimeout) +std::string DiceBetFinish(uint8_t &funcid,uint256 &entropyused,int32_t &entropyvout,int32_t *resultp,uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid,int32_t winlosetimeout,uint256 vin0txid,int32_t vin0vout) { - CMutableTransaction mtx; CScript scriptPubKey,fundingPubKey; CTransaction betTx,entropyTx; uint256 hentropyproof,entropytxid,hashBlock,bettorentropy,entropy,hentropy; CPubKey mypk,dicepk,fundingpk; struct CCcontract_info *cp,C; int64_t inputs=0,CCchange=0,odds,fundsneeded,minbet,maxbet,maxodds,timeoutblocks; uint8_t funcid=0; int32_t iswin=0; uint64_t entropyval,sbits; + CMutableTransaction mtx; CScript scriptPubKey,fundingPubKey; CTransaction oldbetTx,betTx,entropyTx; uint256 hentropyproof,entropytxid,hashBlock,bettorentropy,entropy,hentropy,oldbettxid; CPubKey mypk,dicepk,fundingpk; struct CCcontract_info *cp,C; int64_t inputs=0,CCchange=0,odds,fundsneeded,minbet,maxbet,maxodds,timeoutblocks; int32_t oldentropyvout,retval=0,iswin=0; uint64_t entropyval,sbits; + entropyused = zeroid; *resultp = 0; + funcid = 0; //char str[65]; fprintf(stderr,"DiceBetFinish.%s %s\n",planstr,uint256_str(str,bettxid)); if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 ) { - CCerror = "Diceinit error in finish"; + CCerror = "Diceinit error in finish, is your transaction confirmed?"; fprintf(stderr,"%s\n", CCerror.c_str() ); return(""); } fundingpk = DiceFundingPk(fundingPubKey); - if ( winlosetimeout != 0 ) + if ( winlosetimeout != 0 ) // must be dealernode { scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG; if ( scriptPubKey != fundingPubKey ) @@ -992,47 +1494,91 @@ std::string DiceBetFinish(int32_t *resultp,uint64_t txfee,char *planstr,uint256 winlosetimeout = 0; } } - if ( AddNormalinputs(mtx,mypk,txfee,1) == 0 ) + if ( myGetTransaction(bettxid,betTx,hashBlock) != 0 && myGetTransaction(betTx.vin[0].prevout.hash,entropyTx,hashBlock) != 0 ) { - CCerror = "no txfee inputs for win/lose"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - if ( GetTransaction(bettxid,betTx,hashBlock,false) != 0 && GetTransaction(betTx.vin[0].prevout.hash,entropyTx,hashBlock,false) != 0 ) - { - bettorentropy = DiceGetEntropy(betTx,'B'); - if ( winlosetimeout == 0 || (iswin= DiceIsWinner(hentropyproof,bettxid,betTx,entropyTx,bettorentropy,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 ) + entropytxid = betTx.vin[0].prevout.hash; + /*if ( dice_betspent((char *)"DiceBetFinish",bettxid) != 0 ) { - if ( winlosetimeout != 0 ) - winlosetimeout = iswin; - if ( iswin == winlosetimeout ) + CCerror = "bettxid already spent"; + fprintf(stderr,"%s\n", CCerror.c_str() ); + return(""); + }*/ + bettorentropy = DiceGetEntropy(betTx,'B'); + if ( winlosetimeout == 0 || (iswin= DiceIsWinner(hentropyproof,entropyvout,bettxid,betTx,entropyTx,bettorentropy,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 ) + { + if ( vin0txid == zeroid || vin0vout < 0 ) { - if ( myIsutxo_spentinmempool(bettxid,0) != 0 || myIsutxo_spentinmempool(bettxid,1) != 0 ) + if ( AddNormalinputs(mtx,mypk,2*txfee,1) == 0 ) // must be a single vin!! { - CCerror = "bettxid already spent"; + CCerror = "no txfee inputs for win/lose"; fprintf(stderr,"%s\n", CCerror.c_str() ); return(""); } + } + else + { + //fprintf(stderr,"use vin0 %s/%d\n",vin0txid.GetHex().c_str(),vin0vout); + mtx.vin.push_back(CTxIn(vin0txid,vin0vout,CScript())); + } + if ( winlosetimeout != 0 ) // dealernode + { + entropyused = hentropyproof; + if ( vin0vout == -2 ) + retval = -1; + /*if ( iswin == 0 ) + { + retval = -1; + fprintf(stderr,"invalid dicebet %s\n",bettxid.GetHex().c_str()); + } else retval = 0;*/ + if ( retval < 0 || (retval= DiceEntropyUsed(oldbetTx,oldbettxid,oldentropyvout,entropyused,bettxid,betTx,entropyvout)) != 0 ) + { + if ( retval < 0 ) + { + fprintf(stderr,"orphan that reveals entropy, generate refund tx with proofs\n"); + // make sure we dont refund wrong amounts + mtx.vin.push_back(CTxIn(bettxid,0,CScript())); + mtx.vin.push_back(CTxIn(bettxid,1,CScript())); + funcid = 'R'; + mtx.vout.push_back(CTxOut(betTx.vout[0].nValue,fundingPubKey)); + mtx.vout.push_back(CTxOut(txfee,fundingPubKey)); + mtx.vout.push_back(CTxOut(betTx.vout[1].nValue,betTx.vout[2].scriptPubKey)); + *resultp = 1; + return(FinalizeCCTx(0,cp,mtx,fundingpk,txfee,EncodeDiceOpRet(funcid,sbits,fundingtxid,entropyused,oldbettxid))); // need to change opreturn to include oldbetTx to allow validation + } + else + { + CCerror = "DiceBetFinish: duplicate betTx"; + *resultp = -2; // demote error to warning + } + //fprintf(stderr,"%s\n", CCerror.c_str() ); + return(""); + } + //fprintf(stderr,"set winlosetimeout %d <- %d\n",winlosetimeout,iswin); + if ( (winlosetimeout= iswin) > 0 ) + funcid = 'W'; + else funcid = 'L'; + } + if ( iswin == winlosetimeout ) // dealernode and normal node paths should always get here + { //fprintf(stderr,"iswin.%d matches\n",iswin); mtx.vin.push_back(CTxIn(bettxid,0,CScript())); mtx.vin.push_back(CTxIn(bettxid,1,CScript())); - if ( iswin == 0 ) + if ( iswin == 0 && funcid != 'L' && funcid != 'W' ) // normal node path { - funcid = 'T'; if ( DiceVerifyTimeout(betTx,timeoutblocks) == 0 ) // hasnt timed out yet { return(""); } else { + funcid = 'T'; hentropy = hentropyproof = zeroid; iswin = 1; + fprintf(stderr,"set timeout win T\n"); } } - if ( iswin > 0 ) + if ( iswin > 0 && funcid != 0 ) // dealernode 'W' or normal node 'T' path { - if ( funcid != 'T' ) - funcid = 'W'; odds = (betTx.vout[2].nValue - txfee); if ( odds < 1 || odds > maxodds ) { @@ -1044,29 +1590,38 @@ std::string DiceBetFinish(int32_t *resultp,uint64_t txfee,char *planstr,uint256 fundsneeded = txfee + (odds+1)*betTx.vout[1].nValue; if ( CCchange >= fundsneeded ) CCchange -= fundsneeded; - else if ( (inputs= AddDiceInputs(cp,mtx,dicepk,fundsneeded,60,sbits,fundingtxid)) > 0 ) + else if ( (inputs= AddDiceInputs(cp,mtx,dicepk,fundsneeded,1,sbits,fundingtxid)) >= fundsneeded ) { if ( inputs > fundsneeded ) CCchange += (inputs - fundsneeded); } else { - CCerror = strprintf("not enough inputs for %.8f\n",(double)fundsneeded/COIN); - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); + if ( (inputs= AddDiceInputs(cp,mtx,dicepk,fundsneeded,60,sbits,fundingtxid)) > 0 ) + { + if ( inputs > fundsneeded ) + CCchange += (inputs - fundsneeded); + } + else + { + CCerror = strprintf("not enough inputs for %.8f\n",(double)fundsneeded/COIN); + fprintf(stderr,"%s\n", CCerror.c_str() ); + return(""); + } } mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,dicepk)); mtx.vout.push_back(CTxOut(txfee,fundingPubKey)); mtx.vout.push_back(CTxOut((odds+1) * betTx.vout[1].nValue,betTx.vout[2].scriptPubKey)); } - else + else // dealernode 'L' path { funcid = 'L'; mtx.vout.push_back(MakeCC1vout(cp->evalcode,betTx.vout[0].nValue + betTx.vout[1].nValue,dicepk)); mtx.vout.push_back(CTxOut(txfee,fundingPubKey)); } - if ( winlosetimeout != 0 ) - hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash); + //fprintf(stderr,"make tx.%c\n",funcid); + if ( funcid == 'L' || funcid == 'W' ) // dealernode only + hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash,mtx.vin[0].prevout.n,1); *resultp = 1; //char str[65],str2[65]; //fprintf(stderr,"iswin.%d house entropy %s vs bettor %s\n",iswin,uint256_str(str,hentropyproof),uint256_str(str2,bettorentropy)); @@ -1081,20 +1636,19 @@ std::string DiceBetFinish(int32_t *resultp,uint64_t txfee,char *planstr,uint256 } } *resultp = -1; - CCerror = "couldnt find bettx or entropytx"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); + return("couldnt find bettx or entropytx"); } double DiceStatus(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid) { - CScript fundingPubKey,scriptPubKey; CTransaction spenttx,betTx; uint256 hash,proof,txid,hashBlock,spenttxid; CPubKey mypk,dicepk,fundingpk; struct CCcontract_info *cp,C; int32_t i,result,vout,n=0; int64_t minbet,maxbet,maxodds,timeoutblocks; uint64_t sbits; char coinaddr[64]; std::string res; - if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 ) + CScript fundingPubKey,scriptPubKey; CTransaction spenttx,betTx,entropyTx; uint256 hentropyproof,entropyused,hash,proof,txid,hashBlock,spenttxid,bettorentropy; CPubKey mypk,dicepk,fundingpk; struct CCcontract_info *cp,C; int32_t i,entropyvout,flag,win,loss,duplicate=0,result,iswin,vout,n=0; int64_t minbet,maxbet,maxodds,timeoutblocks,sum=0; uint64_t sbits,refsbits; char coinaddr[64]; std::string res; uint8_t funcid; + if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,refsbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 ) { - CCerror = "Diceinit error in status"; + CCerror = "Diceinit error in status, is your transaction confirmed?"; fprintf(stderr,"%s\n", CCerror.c_str() ); return(0.); } + win = loss = 0; fundingpk = DiceFundingPk(fundingPubKey); scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG; GetCCaddress(cp,coinaddr,dicepk); @@ -1106,26 +1660,67 @@ double DiceStatus(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettx { txid = it->first.txhash; vout = (int32_t)it->first.index; - if ( GetTransaction(txid,betTx,hashBlock,false) != 0 && betTx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 ) + if ( vout != 0 ) + continue; + sum += it->second.satoshis; + if ( myGetTransaction(txid,betTx,hashBlock) != 0 && betTx.vout.size() >= 4 && betTx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 ) { - if ( DecodeDiceOpRet(txid,betTx.vout[betTx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof) == 'B' ) + if ( DecodeDiceOpRet(txid,betTx.vout[betTx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof) == 'B' && sbits == refsbits ) { - res = DiceBetFinish(&result,txfee,planstr,fundingtxid,txid,scriptPubKey == fundingPubKey); - if ( result > 0 ) + if ( myGetTransaction(betTx.vin[0].prevout.hash,entropyTx,hashBlock) != 0 ) { - mySendrawtransaction(res); - n++; - } + flag = 0; + if ( scriptPubKey == fundingPubKey ) + { + bettorentropy = DiceGetEntropy(betTx,'B'); + if ( (iswin= DiceIsWinner(hentropyproof,entropyvout,txid,betTx,entropyTx,bettorentropy,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 ) + { + if ( iswin > 0 ) + win++; + else if ( iswin < 0 ) + loss++; + n++; + DiceQueue(iswin,sbits,fundingtxid,txid,betTx,entropyvout); + } //else flag = 1; + } + if ( flag != 0 || scriptPubKey != fundingPubKey ) + { + if ( flag != 0 ) + fprintf(stderr,"illegal bettxid %d: iswin.%d W.%d L.%d %s/v%d (%c %.8f) %.8f\n",n,iswin,win,loss,txid.GetHex().c_str(),vout,funcid,(double)it->second.satoshis/COIN,(double)sum/COIN); + res = DiceBetFinish(funcid,entropyused,entropyvout,&result,txfee,planstr,fundingtxid,txid,scriptPubKey == fundingPubKey,zeroid,-1); + if ( result > 0 ) + { + mySenddicetransaction(res,entropyused,entropyvout,txid,betTx,funcid,0); + n++; + } + } + } else fprintf(stderr,"bettxid.%s cant find entropyTx.%s\n",txid.GetHex().c_str(),betTx.vin[0].prevout.hash.GetHex().c_str()); } } } - if ( 0 && scriptPubKey == fundingPubKey ) + if ( scriptPubKey == fundingPubKey ) { - for (i=0; i<=n; i++) + CTransaction tx; uint64_t entropyval; uint256 entropytxid; int32_t entropytxs; + DicePlanFunds(entropyval,entropytxid,refsbits,cp,dicepk,fundingtxid,entropytxs,false); + if ( entropytxs < DICE_MINUTXOS ) { - res = DiceAddfunding(txfee,planstr,fundingtxid,COIN); - fprintf(stderr,"ENTROPY tx:\n"); - mySendrawtransaction(res); + n = 10;//sqrt(DICE_MINUTXOS - entropytxs) + 10; + for (i=0; i 64 && is_hexstr((char *)res.c_str(),0) > 64 ) + { + if ( DecodeHexTx(tx,res) != 0 ) + { + //LOCK(cs_main); + if ( myAddtomempool(tx) != 0 ) + { + fprintf(stderr,"ENTROPY %s: %d of %d, %d\n",tx.GetHash().GetHex().c_str(),i,n,DICE_MINUTXOS - entropytxs); + RelayTransaction(tx); + } else break; + } else break; + } else break; + } } } return(n); @@ -1136,7 +1731,7 @@ double DiceStatus(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettx if ( (vout= myIsutxo_spent(spenttxid,bettxid,1)) >= 0 ) { //fprintf(stderr,"bettx is spent\n"); - if ( GetTransaction(bettxid,betTx,hashBlock,false) != 0 && GetTransaction(spenttxid,spenttx,hashBlock,false) != 0 && spenttx.vout.size() > 2 ) + if ( myGetTransaction(bettxid,betTx,hashBlock) != 0 && myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 2 ) { //fprintf(stderr,"found spenttxid %s\n",uint256_str(str,spenttxid)); if ( betTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 || betTx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 || spenttx.vout[2].scriptPubKey != betTx.vout[2].scriptPubKey ) @@ -1144,29 +1739,27 @@ double DiceStatus(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettx else return((double)spenttx.vout[2].nValue/COIN); } CCerror = "couldnt find bettx or spenttx %s\n",uint256_str(str,spenttxid); - fprintf(stderr,"%s\n", CCerror.c_str()); return(-1.); } else if ( scriptPubKey == fundingPubKey ) - res = DiceBetFinish(&result,txfee,planstr,fundingtxid,bettxid,1); - else res = DiceBetFinish(&result,txfee,planstr,fundingtxid,bettxid,0); + res = DiceBetFinish(funcid,entropyused,entropyvout,&result,txfee,planstr,fundingtxid,bettxid,1,zeroid,-1); + else res = DiceBetFinish(funcid,entropyused,entropyvout,&result,txfee,planstr,fundingtxid,bettxid,0,zeroid,-1); if ( result > 0 ) { - mySendrawtransaction(res); + mySenddicetransaction(res,entropyused,entropyvout,bettxid,betTx,funcid,0); sleep(1); if ( (vout= myIsutxo_spent(spenttxid,bettxid,1)) >= 0 ) { - if ( GetTransaction(txid,betTx,hashBlock,false) != 0 && GetTransaction(spenttxid,spenttx,hashBlock,false) != 0 && spenttx.vout.size() >= 2 ) + if ( myGetTransaction(txid,betTx,hashBlock) != 0 && myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() >= 2 ) { - if ( betTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 || betTx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 || spenttx.vout[2].scriptPubKey != betTx.vout[2].scriptPubKey ) + if ( funcid == 'L' )//betTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 || betTx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 || spenttx.vout[2].scriptPubKey != betTx.vout[2].scriptPubKey ) //if ( spenttx.vout[2].scriptPubKey == fundingPubKey || ((uint8_t *)spenttx.vout[2].scriptPubKey.data())[0] == 0x6a ) return(0.); else return((double)spenttx.vout[2].nValue/COIN); } else return(0.); } CCerror = "didnt find dicefinish tx"; - fprintf(stderr,"%s\n", CCerror.c_str()); - } + } else CCerror = res; return(-1.); } return(0.); diff --git a/src/cc/eval.cpp b/src/cc/eval.cpp index 8e23506ac..7a348ee97 100644 --- a/src/cc/eval.cpp +++ b/src/cc/eval.cpp @@ -76,15 +76,13 @@ bool Eval::Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn) case EVAL_IMPORTPAYOUT: //return ImportPayout(vparams, txTo, nIn); break; - + case EVAL_IMPORTCOIN: //return ImportCoin(vparams, txTo, nIn); break; - + default: - // only support coinbase guard for now - if (ecode == EVAL_STAKEGUARD) - return(ProcessCC(cp,this, vparams, txTo, nIn)); + return(ProcessCC(cp,this, vparams, txTo, nIn)); break; } return Invalid("invalid-code, dont forget to add EVAL_NEWCC to Eval::Dispatch"); @@ -100,10 +98,9 @@ bool Eval::GetSpendsConfirmed(uint256 hash, std::vector &spends) c bool Eval::GetTxUnconfirmed(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock) const { - // there is a LOCK(cs_main) in the normal GetTransaction(), which leads to deadlocks - //bool fAllowSlow = false; // Don't allow slow - //return GetTransaction(hash, txOut, hashBlock, fAllowSlow); - return myGetTransaction(hash, txOut,hashBlock); + if (!myGetTransaction(hash, txOut,hashBlock)) { + return(GetTransaction(hash, txOut,hashBlock)); + } else return(true); } @@ -117,7 +114,6 @@ bool Eval::GetTxConfirmed(const uint256 &hash, CTransaction &txOut, CBlockIndex return true; } - unsigned int Eval::GetCurrentHeight() const { return chainActive.Height(); diff --git a/src/cc/eval.h b/src/cc/eval.h index 12ff1b453..2a5f41b71 100644 --- a/src/cc/eval.h +++ b/src/cc/eval.h @@ -36,9 +36,9 @@ * there should be a code identifying it. For example, * a possible code is EVAL_BITCOIN_SCRIPT, where the entire binary * after the code is interpreted as a bitcoin script. + * Verus EVAL_STAKEGUARD is 0x01 */ #define FOREACH_EVAL(EVAL) \ - EVAL(EVAL_STAKEGUARD, 0x1) \ EVAL(EVAL_IMPORTPAYOUT, 0xe1) \ EVAL(EVAL_IMPORTCOIN, 0xe2) \ EVAL(EVAL_ASSETS, 0xe3) \ @@ -48,7 +48,7 @@ EVAL(EVAL_FSM, 0xe7) \ EVAL(EVAL_AUCTION, 0xe8) \ EVAL(EVAL_LOTTO, 0xe9) \ - EVAL(EVAL_MOFN, 0xea) \ + EVAL(EVAL_HEIR, 0xea) \ EVAL(EVAL_CHANNELS, 0xeb) \ EVAL(EVAL_ORACLES, 0xec) \ EVAL(EVAL_PRICES, 0xed) \ diff --git a/src/cc/faucet.cpp b/src/cc/faucet.cpp index 76fb631e1..2a736ea1b 100644 --- a/src/cc/faucet.cpp +++ b/src/cc/faucet.cpp @@ -142,14 +142,17 @@ 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) { - char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; + char coinaddr[64]; int64_t threshold,nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); SetCCunspents(unspentOutputs,coinaddr); + threshold = total/maxinputs; for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; + if ( it->second.satoshis < threshold ) + continue; //char str[65]; fprintf(stderr,"check %s/v%d %.8f`\n",uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); // no need to prevent dup if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) diff --git a/src/cc/fsm.cpp b/src/cc/fsm.cpp index fc51ed4aa..676537929 100644 --- a/src/cc/fsm.cpp +++ b/src/cc/fsm.cpp @@ -122,6 +122,7 @@ bool FSMValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, u int64_t AddFSMInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) { + // add threshold check char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t n = 0; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); diff --git a/src/cc/gateways.cpp b/src/cc/gateways.cpp index 5ee05c4fa..2e03211b9 100644 --- a/src/cc/gateways.cpp +++ b/src/cc/gateways.cpp @@ -18,10 +18,10 @@ /* prevent duplicate bindtxid via mempool scan wait for notarization for oraclefeed and validation of gatewaysdeposit - + debug multisig and do partial signing validation -string oracles + string oracles */ /* @@ -58,7 +58,7 @@ string oracles 5. Now a gatewaysbind can bind an external coin to an asset, along with the oracle for the merkleroots. the txid from the bind is used in most of the other gateways CC calls usage: - ./c tokencreate KMD 1000000 KMD_equivalent_token_for_gatewaysCC + ./c tokencreate KMD 1000000 KMD_equivalent_token_for_gatewaysCC a7398a8748354dd0a3f8d07d70e65294928ecc3674674bb2d9483011ccaa9a7a transfer to gateways pubkey: 03ea9c062b9652d8eff34879b504eda0717895d27597aaeb60347d65eed96ccb40 RDMqGyREkP1Gwub1Nr5Ye8a325LGZsWBCb @@ -74,6 +74,7 @@ string oracles ./c oraclessubscribe 1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808 02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92 1000 f9499d8bb04ffb511fcec4838d72e642ec832558824a2ce5aed87f1f686f8102 + gatewaysbind tokenid oracletxid coin tokensupply M N pubkey(s) ./c gatewaysbind a7398a8748354dd0a3f8d07d70e65294928ecc3674674bb2d9483011ccaa9a7a 1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808 KMD 100000000000000 1 1 02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92 e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e @@ -93,7 +94,7 @@ string oracles "remaining": "1000000.00000000", "issued": "0.00000000" } - + To make a gateway deposit, send the funds to the "deposit" address, along with any amount to the same pubkey address you want to get the assetized KMD to appear in. 0223d114dededb04f253816d6ad0ce78dd08c617c94ce3c53bf50dc74a5157bef8 pubkey for RFpxgqff7FDHFuHa3jSX5NzqqWCcELz8ha @@ -105,16 +106,16 @@ string oracles ./komodo-cli getrawtransaction bc41a00e429db741c3199f17546a48012fd3b7eea45dfc6bc2f5228278133009 010000000149964cdcd17fe9b1cae4d0f3b5f5db301d9b4f54099fdf4d34498df281757094010000006a4730440220594f3a630dd73c123f44621aa8bb9968ab86734833453dd479af6d79ae6f584202207bb5e35f13b337ccc8a88d9a006c8c5ddb016c0a6f4f2dc44357a8128623d85d01210223154bf53cd3a75e64d86697070d6437c8f0010a09c1df35b659e31ce3d79b5dffffffff0310270000000000001976a91447d2e323a14b0c3be08698aa46a9b91489b189d688ac701de52d000000001976a91459fdba29ea85c65ad90f6d38f7a6646476b26b1688acb0a86a00000000001976a914f9a9daf5519dae38b8b61d945f075da895df441d88ace18d965b - + gatewaysdeposit bindtxid height coin cointxid claimvout deposithex proof destpub amount -./komodo-cli -ac_name=AT5 gatewaysdeposit e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e 1003776 KMD bc41a00e429db741c3199f17546a48012fd3b7eea45dfc6bc2f5228278133009 0 010000000149964cdcd17fe9b1cae4d0f3b5f5db301d9b4f54099fdf4d34498df281757094010000006a4730440220594f3a630dd73c123f44621aa8bb9968ab86734833453dd479af6d79ae6f584202207bb5e35f13b337ccc8a88d9a006c8c5ddb016c0a6f4f2dc44357a8128623d85d01210223154bf53cd3a75e64d86697070d6437c8f0010a09c1df35b659e31ce3d79b5dffffffff0310270000000000001976a91447d2e323a14b0c3be08698aa46a9b91489b189d688ac701de52d000000001976a91459fdba29ea85c65ad90f6d38f7a6646476b26b1688acb0a86a00000000001976a914f9a9daf5519dae38b8b61d945f075da895df441d88ace18d965b 04000000232524ea04b54489eb222f8b3f566ed35504e3050488a63f0ab7b1c2030000007f91615f04835822bf6984a56482aeeb11d03814352eca9afc0a20192fdcae900000000000000000000000000000000000000000000000000000000000000000268e965ba608071d0800038e16762900000000000000000000000000000000000000000042008dd6fd4005016b4292b05df6dfd316c458c53e28fb2befb96e4870a40c6c04e733d75d8a7a18cce34fe326005efdc403bfa4644e30eeafdaeff34419edc591299e6cc5933cb2eeecbab5c4dfe97cd413b75215999a3dd02b540373581e81d8512bff1640590a6b4da4aaa9b8adc0102c38ca0022daed997b53ed192ba326e212fba5e505ce29e3ad149cef7f48d0e00948a1acd81731d84008760759211eb4abbc7b037939a7964182edb59cf9065357e864188ee5fc7316e8796963036bb99eeb9f06c95d64f78749ecec7181c12eb5d83a3b9b1c1e8a0aae9a20ce04a250b28216620bfc99bb81a6de4db80b93a5aea916de97c1a272e26644abdd683f19c5e3174a2e4513ed767d8f11a4c3074295f697839c5d9139676a813451cc7da38f68cbae5d990a79075f98903233ca04fe1b4b099e433585e5adcc45d41d54a9c648179297359c75950a5e574f13f70b728bbbf552770256315cd0a00139d6ab6934cb5ed70a4fc01a92611b096dd0028f17f4cc687b75f37dca530aa47a18321c50528dbd9272eabb3e13a87021a05918a6d2627e2caba6d7cf1a9f0b831ea3337b9a6af92746d83140078d60c72b6beacf91c9e68a34cee209e08670be1d17ff8d80b7a2285b1325461a2e33f2ee675593f1900e066a5d212615cd8da18749b0e684eee73edcc9031709be715b889c6d015cf4bd4ad5ab7e21bd3492c208930a54d353ef36a437f507ead38855633c1b88d060d9e4221ca8ce2f698e8a6ae0d41e9ace3cbd401f1e0f07650e9c126d4ef20278c8be6e85c7637513643f8d02d7ad64c09da11c16429d60e5160c345844b8158ece62794e8ad280d4e4664150e74978609ece431e51a9f9e1ce8aa49c16f36c7fd12b71acc42d893e18476b8b1e144a8175519612efc93e0aecc61f3b21212c958b0e2331d76aaa62faf11a58fe2bd91ab9ab01b906406c9bbc02df2a106e67182aae0a20b538bf19f09c57f9de5e198ba254580fb1b11e22ad526550093420cb7c68628d4c3ad329c8acc6e219093d277810ed016b6099b7e3781de412a22dacedaa2acf29e8062debcd85c7b9529a20b2782a2470763ac27cf89611a527d43ac89b8063ffb93b6ed993425194f8ee821a8493a563072c896f9584f95db28e3f2fc5fb4a6f3c39d615cd563641717cd50afb73ed3989cbf504b2043882993ce9575f56402534173b1396fbc13df80920b46788ae340ad5a91f25177cc74aa69024d76f56166199d2e4d50a053555256c4e3137ea1cee1130e916a88b6ee5cf2c85652fb8824d5dacfa485e3ef6190591ac0c2fcacc4fc7deb65aca4b0b89b76e35a46b0627e2e967cc63a5d606a984c8e63eabb98fde3e69114340ae524c974cb936e57690e98a7a74533f6f7d1d0496976496b54d14a8163efb32b70dfbb79d80a3022c4f53571c08bf044270565716b435084376714b224ab23e9817c05af8223723afc0577af5c8fc28f71036ca82528aaa4ca9bcd18a50e25d2a528f183d3a2074d968d170876d8dce434c5937261b55173ab87e03d5632ca0834fdc5387c15ab3a17d75c0f274004f289ff1bf7d14e97fdf4172eb49adfb418cc2f4794806ae7c0111c97df4d65d38679ec93fea3ef738ed565e8906a8fe1861cafe3938c772fedcfab40159938e06ef414fd299f2355c6d3369bc1bd3c4db64ce205f0a1b70a40030f505b736e28230de82e97776b5ee7b10708bb3020d28cec7a8e124549ec80c547ac4e7b52bf397c72bcfce30820554ab8fb4d1f73b209bc32a0e7e878843cdbf5f01222728ccea7e6ab7cb5e3fee3234f5b85d1985f91492f6ceaa6454a658dab5074f163ce26ed753137fa61c940679de13bd7b212cd3cf2b334f5201cecbc7473342bd7a239e09169bccd56d03000000037a9068df0625e548e71263c8361b4e904c998378f6b9e32729c3f19b10ad752e093013788222f5c26bfc5da4eeb7d32f01486a54179f19c341b79d420ea041bc8878d22fad4692b2d609c3cf190903874d3682a714c7483518c9392e07c25035010b 0223d114dededb04f253816d6ad0ce78dd08c617c94ce3c53bf50dc74a5157bef8 7.6999 + ./komodo-cli -ac_name=AT5 gatewaysdeposit e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e 1003776 KMD bc41a00e429db741c3199f17546a48012fd3b7eea45dfc6bc2f5228278133009 0 010000000149964cdcd17fe9b1cae4d0f3b5f5db301d9b4f54099fdf4d34498df281757094010000006a4730440220594f3a630dd73c123f44621aa8bb9968ab86734833453dd479af6d79ae6f584202207bb5e35f13b337ccc8a88d9a006c8c5ddb016c0a6f4f2dc44357a8128623d85d01210223154bf53cd3a75e64d86697070d6437c8f0010a09c1df35b659e31ce3d79b5dffffffff0310270000000000001976a91447d2e323a14b0c3be08698aa46a9b91489b189d688ac701de52d000000001976a91459fdba29ea85c65ad90f6d38f7a6646476b26b1688acb0a86a00000000001976a914f9a9daf5519dae38b8b61d945f075da895df441d88ace18d965b 04000000232524ea04b54489eb222f8b3f566ed35504e3050488a63f0ab7b1c2030000007f91615f04835822bf6984a56482aeeb11d03814352eca9afc0a20192fdcae900000000000000000000000000000000000000000000000000000000000000000268e965ba608071d0800038e16762900000000000000000000000000000000000000000042008dd6fd4005016b4292b05df6dfd316c458c53e28fb2befb96e4870a40c6c04e733d75d8a7a18cce34fe326005efdc403bfa4644e30eeafdaeff34419edc591299e6cc5933cb2eeecbab5c4dfe97cd413b75215999a3dd02b540373581e81d8512bff1640590a6b4da4aaa9b8adc0102c38ca0022daed997b53ed192ba326e212fba5e505ce29e3ad149cef7f48d0e00948a1acd81731d84008760759211eb4abbc7b037939a7964182edb59cf9065357e864188ee5fc7316e8796963036bb99eeb9f06c95d64f78749ecec7181c12eb5d83a3b9b1c1e8a0aae9a20ce04a250b28216620bfc99bb81a6de4db80b93a5aea916de97c1a272e26644abdd683f19c5e3174a2e4513ed767d8f11a4c3074295f697839c5d9139676a813451cc7da38f68cbae5d990a79075f98903233ca04fe1b4b099e433585e5adcc45d41d54a9c648179297359c75950a5e574f13f70b728bbbf552770256315cd0a00139d6ab6934cb5ed70a4fc01a92611b096dd0028f17f4cc687b75f37dca530aa47a18321c50528dbd9272eabb3e13a87021a05918a6d2627e2caba6d7cf1a9f0b831ea3337b9a6af92746d83140078d60c72b6beacf91c9e68a34cee209e08670be1d17ff8d80b7a2285b1325461a2e33f2ee675593f1900e066a5d212615cd8da18749b0e684eee73edcc9031709be715b889c6d015cf4bd4ad5ab7e21bd3492c208930a54d353ef36a437f507ead38855633c1b88d060d9e4221ca8ce2f698e8a6ae0d41e9ace3cbd401f1e0f07650e9c126d4ef20278c8be6e85c7637513643f8d02d7ad64c09da11c16429d60e5160c345844b8158ece62794e8ad280d4e4664150e74978609ece431e51a9f9e1ce8aa49c16f36c7fd12b71acc42d893e18476b8b1e144a8175519612efc93e0aecc61f3b21212c958b0e2331d76aaa62faf11a58fe2bd91ab9ab01b906406c9bbc02df2a106e67182aae0a20b538bf19f09c57f9de5e198ba254580fb1b11e22ad526550093420cb7c68628d4c3ad329c8acc6e219093d277810ed016b6099b7e3781de412a22dacedaa2acf29e8062debcd85c7b9529a20b2782a2470763ac27cf89611a527d43ac89b8063ffb93b6ed993425194f8ee821a8493a563072c896f9584f95db28e3f2fc5fb4a6f3c39d615cd563641717cd50afb73ed3989cbf504b2043882993ce9575f56402534173b1396fbc13df80920b46788ae340ad5a91f25177cc74aa69024d76f56166199d2e4d50a053555256c4e3137ea1cee1130e916a88b6ee5cf2c85652fb8824d5dacfa485e3ef6190591ac0c2fcacc4fc7deb65aca4b0b89b76e35a46b0627e2e967cc63a5d606a984c8e63eabb98fde3e69114340ae524c974cb936e57690e98a7a74533f6f7d1d0496976496b54d14a8163efb32b70dfbb79d80a3022c4f53571c08bf044270565716b435084376714b224ab23e9817c05af8223723afc0577af5c8fc28f71036ca82528aaa4ca9bcd18a50e25d2a528f183d3a2074d968d170876d8dce434c5937261b55173ab87e03d5632ca0834fdc5387c15ab3a17d75c0f274004f289ff1bf7d14e97fdf4172eb49adfb418cc2f4794806ae7c0111c97df4d65d38679ec93fea3ef738ed565e8906a8fe1861cafe3938c772fedcfab40159938e06ef414fd299f2355c6d3369bc1bd3c4db64ce205f0a1b70a40030f505b736e28230de82e97776b5ee7b10708bb3020d28cec7a8e124549ec80c547ac4e7b52bf397c72bcfce30820554ab8fb4d1f73b209bc32a0e7e878843cdbf5f01222728ccea7e6ab7cb5e3fee3234f5b85d1985f91492f6ceaa6454a658dab5074f163ce26ed753137fa61c940679de13bd7b212cd3cf2b334f5201cecbc7473342bd7a239e09169bccd56d03000000037a9068df0625e548e71263c8361b4e904c998378f6b9e32729c3f19b10ad752e093013788222f5c26bfc5da4eeb7d32f01486a54179f19c341b79d420ea041bc8878d22fad4692b2d609c3cf190903874d3682a714c7483518c9392e07c25035010b 0223d114dededb04f253816d6ad0ce78dd08c617c94ce3c53bf50dc74a5157bef8 7.6999 -> 9d80ea79a65aaa0d464f8b762356fa01047e16e9793505a22ca04559f81a6eb6 to get the merkleroots onchain, from the multisig signers nodes run the oraclefeed program with acname oracletxid pubkey Ihh ./oraclefeed AT5 1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808 02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92 Ihh gatewaysclaim bindtxid coin deposittxid destpub amount -./c gatewaysclaim e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e KMD 9d80ea79a65aaa0d464f8b762356fa01047e16e9793505a22ca04559f81a6eb6 0223d114dededb04f253816d6ad0ce78dd08c617c94ce3c53bf50dc74a5157bef8 7.6999 + ./c gatewaysclaim e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e KMD 9d80ea79a65aaa0d464f8b762356fa01047e16e9793505a22ca04559f81a6eb6 0223d114dededb04f253816d6ad0ce78dd08c617c94ce3c53bf50dc74a5157bef8 7.6999 now the asset is in the pubkey's asset address! it can be used, traded freely and any node who has the asset can do a gatewayswithdraw @@ -125,14 +126,24 @@ string oracles Now there is a withdraw pending, so it needs to be processed by the signing nodes on the KMD side - gatewayspending bindtxid coin + gatewayspending bindtxid coin gatewayspending will display all pending withdraws and if it is done on one of the msigpubkeys, then it will queue it for processing ./c gatewayspending e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e KMD -*/ + + 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. + + 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. + + */ -int32_t GatewaysAddQueue(std::string coin,uint256 txid,CScript scriptPubKey,int64_t nValue) +void GatewaysAddQueue(std::string coin,uint256 txid,CScript scriptPubKey,int64_t nValue) { char destaddr[64],str[65]; Getscriptaddress(destaddr,scriptPubKey); @@ -167,6 +178,41 @@ uint8_t DecodeGatewaysOpRet(const CScript &scriptPubKey,std::string &coin,uint25 return(0); } +uint8_t DecodeGatewaysPartialOpRet(const CScript &scriptPubKey,int32_t &K, CPubKey &signerpk, std::string &coin,std::string &hex) +{ + std::vector 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 >> K; ss >> signerpk; ss >> coin; ss >> hex) != 0 ) + { + return(f); + } + return(0); +} + +uint8_t DecodeGatewaysWithdrawOpRet(const CScript &scriptPubKey, uint256 &assetid, std::string &refcoin, CPubKey &withdrawpub, int64_t &amount) +{ + std::vector 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 ) + { + return(f); + } + return(0); +} +uint8_t DecodeGatewaysMarkdoneOpRet(const CScript &scriptPubKey, std::string &refcoin, uint256 &cointxid) +{ + std::vector 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 >> refcoin; ss >> cointxid) != 0 ) + { + return(f); + } + return(0); +} + uint8_t DecodeGatewaysBindOpRet(char *depositaddr,const CScript &scriptPubKey,std::string &coin,uint256 &tokenid,int64_t &totalsupply,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector &pubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2) { std::vector vopret; uint8_t *script,e,f; @@ -178,8 +224,11 @@ uint8_t DecodeGatewaysBindOpRet(char *depositaddr,const CScript &scriptPubKey,st if ( prefix == 60 ) { if ( N > 1 ) - Getscriptaddress(depositaddr,GetScriptForMultisig(M,pubkeys)); - else Getscriptaddress(depositaddr,CScript() << ParseHex(HexStr(pubkeys[0])) << OP_CHECKSIG); + { + strcpy(depositaddr,CBitcoinAddress(CScriptID(GetScriptForMultisig(M,pubkeys))).ToString().c_str()); + //Getscriptaddress(depositaddr,GetScriptForMultisig(M,pubkeys)); + fprintf(stderr,"f.%c M.%d of N.%d size.%d -> %s\n",f,M,N,(int32_t)pubkeys.size(),depositaddr); + } else Getscriptaddress(depositaddr,CScript() << ParseHex(HexStr(pubkeys[0])) << OP_CHECKSIG); } else { @@ -281,28 +330,47 @@ 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,int64_t total,int32_t maxinputs) +int64_t AddGatewaysInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 refassetid,int64_t total,int32_t maxinputs) { - char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; + char coinaddr[64],destaddr[64]; int64_t threshold,nValue,price,totalinputs = 0; uint256 assetid,txid,hashBlock; std::vector origpubkey; std::vector vopret; CTransaction vintx; int32_t j,vout,n = 0; uint8_t evalcode,funcid; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); SetCCunspents(unspentOutputs,coinaddr); + threshold = total/maxinputs; + fprintf(stderr,"check %s for gateway inputs\n",coinaddr); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; - // no need to prevent dup + if ( it->second.satoshis < threshold ) + continue; + for (j=0; j 10000 && myIsutxo_spentinmempool(txid,vout) == 0 ) + Getscriptaddress(destaddr,vintx.vout[vout].scriptPubKey); + fprintf(stderr,"check %s vout.%d %.8f %.8f\n",destaddr,vout,(double)vintx.vout[vout].nValue/COIN,(double)it->second.satoshis/COIN); + 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 ( 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; + 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 ) + { + //fprintf(stderr,"total %llu maxinputs.%d %.8f\n",(long long)total,maxinputs,(double)it->second.satoshis/COIN); + 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; + } } } } @@ -376,7 +444,7 @@ UniValue GatewaysInfo(uint256 bindtxid) result.push_back(Pair("name","Gateways")); cp = CCinit(&C,EVAL_GATEWAYS); Gatewayspk = GetUnspendable(cp,0); - _GetCCaddress(gatewaysassets,EVAL_ASSETS,Gatewayspk); + _GetCCaddress(gatewaysassets,EVAL_GATEWAYS,Gatewayspk); if ( GetTransaction(bindtxid,tx,hashBlock,false) != 0 ) { depositaddr[0] = 0; @@ -399,7 +467,7 @@ UniValue GatewaysInfo(uint256 bindtxid) result.push_back(Pair("tokenid",uint256_str(str,tokenid))); sprintf(numstr,"%.8f",(double)totalsupply/COIN); result.push_back(Pair("totalsupply",numstr)); - remaining = CCaddress_balance(gatewaysassets); + remaining = CCtoken_balance(gatewaysassets,tokenid); sprintf(numstr,"%.8f",(double)remaining/COIN); result.push_back(Pair("remaining",numstr)); sprintf(numstr,"%.8f",(double)(totalsupply - remaining)/COIN); @@ -432,19 +500,24 @@ std::string GatewaysBind(uint64_t txfee,std::string coin,uint256 tokenid,int64_t { CMutableTransaction mtx; CTransaction oracletx; uint8_t taddr,prefix,prefix2; CPubKey mypk,gatewayspk; CScript opret; uint256 hashBlock; struct CCcontract_info *cp,C; std::string name,description,format; int32_t i,numvouts; int64_t fullsupply; char destaddr[64],coinaddr[64],str[65],*fstr; cp = CCinit(&C,EVAL_GATEWAYS); + if ( strcmp((char *)"KMD",coin.c_str()) == 0 ) + { + taddr = 0; + prefix = 60; + prefix2 = 85; + } + else + { + fprintf(stderr,"set taddr, prefix, prefix2 for %s\n",coin.c_str()); + taddr = 0; + prefix = 60; + prefix2 = 85; + } if ( N == 0 || N > 15 || M > N ) { fprintf(stderr,"illegal M.%d or N.%d\n",M,N); return(""); } - if ( strcmp((char *)"KMD",coin.c_str()) != 0 ) - { - fprintf(stderr,"only KMD supported for now\n"); - return(""); - } - taddr = 0; - prefix = 60; - prefix2 = 85; if ( pubkeys.size() != N ) { fprintf(stderr,"M.%d N.%d but pubkeys[%d]\n",M,N,(int32_t)pubkeys.size()); @@ -463,7 +536,7 @@ std::string GatewaysBind(uint64_t txfee,std::string coin,uint256 tokenid,int64_t txfee = 10000; mypk = pubkey2pk(Mypubkey()); gatewayspk = GetUnspendable(cp,0); - if ( _GetCCaddress(destaddr,EVAL_ASSETS,gatewayspk) == 0 ) + if ( _GetCCaddress(destaddr,EVAL_GATEWAYS,gatewayspk) == 0 ) { fprintf(stderr,"Gateway bind.%s (%s) cant create globaladdr\n",coin.c_str(),uint256_str(str,tokenid)); return(""); @@ -475,7 +548,7 @@ std::string GatewaysBind(uint64_t txfee,std::string coin,uint256 tokenid,int64_t } if ( CCtoken_balance(destaddr,tokenid) != totalsupply ) { - fprintf(stderr,"Gateway bind.%s (%s) globaladdr.%s token balance %.8f != %.8f\n",coin.c_str(),uint256_str(str,tokenid),cp->unspendableCCaddr,(double)CCtoken_balance(destaddr,tokenid)/COIN,(double)totalsupply/COIN); + fprintf(stderr,"Gateway bind.%s (%s) destaddr.%s globaladdr.%s token balance %.8f != %.8f\n",coin.c_str(),uint256_str(str,tokenid),destaddr,cp->unspendableCCaddr,(double)CCtoken_balance(destaddr,tokenid)/COIN,(double)totalsupply/COIN); return(""); } if ( GetTransaction(oracletxid,oracletx,hashBlock,false) == 0 || (numvouts= oracletx.vout.size()) <= 0 ) @@ -509,26 +582,38 @@ std::string GatewaysBind(uint64_t txfee,std::string coin,uint256 tokenid,int64_t uint256 GatewaysReverseScan(uint256 &txid,int32_t height,uint256 reforacletxid,uint256 batontxid) { - CTransaction tx; uint256 hash,mhash,hashBlock,oracletxid; int64_t val; int32_t numvouts; int64_t merkleht; CPubKey pk; std::vectordata; + CTransaction tx; uint256 hash,mhash,bhash,hashBlock,oracletxid; int32_t len,len2,numvouts; int64_t val,merkleht; CPubKey pk; std::vectordata; txid = zeroid; - char str[65]; fprintf(stderr,"reverse scan %s\n",uint256_str(str,batontxid)); + char str[65]; + //fprintf(stderr,"start reverse scan %s\n",uint256_str(str,batontxid)); while ( GetTransaction(batontxid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 ) { - fprintf(stderr,"reverse scan %s\n",uint256_str(str,batontxid)); - if ( DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,hash,pk,data) == 'D' && oracletxid == reforacletxid ) + //fprintf(stderr,"check %s\n",uint256_str(str,batontxid)); + if ( DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,bhash,pk,data) == 'D' && oracletxid == reforacletxid ) { + //fprintf(stderr,"decoded %s\n",uint256_str(str,batontxid)); if ( oracle_format(&hash,&merkleht,0,'I',(uint8_t *)data.data(),0,(int32_t)data.size()) == sizeof(int32_t) && merkleht == height ) { - if ( oracle_format(&hash,&val,0,'h',(uint8_t *)data.data(),sizeof(int32_t),(int32_t)data.size()) == sizeof(hash) && - oracle_format(&mhash,&val,0,'h',(uint8_t *)data.data(),(int32_t)(sizeof(int32_t)+sizeof(uint256)),(int32_t)data.size()) == sizeof(hash) && mhash != zeroid ) + len = oracle_format(&hash,&val,0,'h',(uint8_t *)data.data(),sizeof(int32_t),(int32_t)data.size()); + len2 = oracle_format(&mhash,&val,0,'h',(uint8_t *)data.data(),(int32_t)(sizeof(int32_t)+sizeof(uint256)),(int32_t)data.size()); + char str2[65]; fprintf(stderr,"found merkleht.%d len.%d len2.%d %s %s\n",(int32_t)merkleht,len,len2,uint256_str(str,hash),uint256_str(str2,mhash)); + if ( len == sizeof(hash)+sizeof(int32_t) && len2 == 2*sizeof(mhash)+sizeof(int32_t) && mhash != zeroid ) { txid = batontxid; + //fprintf(stderr,"set txid\n"); return(mhash); - } else return(zeroid); - } - batontxid = hash; + } + else + { + //fprintf(stderr,"missing hash\n"); + return(zeroid); + } + } //else fprintf(stderr,"height.%d vs search ht.%d\n",(int32_t)merkleht,(int32_t)height); + batontxid = bhash; + //fprintf(stderr,"new hash %s\n",uint256_str(str,batontxid)); } else break; } + fprintf(stderr,"end of loop\n"); return(zeroid); } @@ -584,18 +669,18 @@ int64_t GatewaysVerify(char *refdepositaddr,uint256 oracletxid,int32_t claimvout } if ( txid == cointxid ) { - fprintf(stderr,"verify proof for cointxid in merkleroot\n"); + fprintf(stderr,"verified proof for cointxid in merkleroot\n"); return(nValue); - } else fprintf(stderr,"(%s) != (%s) or txid mismatch.%d or script mismatch\n",refdepositaddr,destaddr,txid != cointxid); + } else fprintf(stderr,"(%s) != (%s) or txid %s mismatch.%d or script mismatch\n",refdepositaddr,destaddr,uint256_str(str,txid),txid != cointxid); return(0); } -int64_t GatewaysDepositval(CTransaction tx) +int64_t GatewaysDepositval(CTransaction tx,CPubKey mypk) { int32_t numvouts,height; int64_t amount; std::string coin,deposithex; std::vector publishers; std::vectortxids; uint256 bindtxid,cointxid; std::vector proof; CPubKey claimpubkey; if ( (numvouts= tx.vout.size()) > 0 ) { - if ( DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey,coin,bindtxid,publishers,txids,height,cointxid,deposithex,proof,claimpubkey,amount) == 'D' ) + if ( DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey,coin,bindtxid,publishers,txids,height,cointxid,deposithex,proof,claimpubkey,amount) == 'D' && claimpubkey == mypk ) { // coin, bindtxid, publishers fprintf(stderr,"need to validate deposittxid more\n"); @@ -639,7 +724,7 @@ std::string GatewaysDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std:: txids.push_back(txid); } } - fprintf(stderr,"m.%d of n.%d\n",m,n); + fprintf(stderr,"cointxid.%s m.%d of n.%d\n",uint256_str(str,cointxid),m,n); if ( merkleroot == zeroid || m < n/2 ) { //uint256 tmp; @@ -660,7 +745,7 @@ std::string GatewaysDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std:: } if ( AddNormalinputs(mtx,mypk,3*txfee,60) > 0 ) { - mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,mypk)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,destpub)); mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(CCtxidaddr(txidaddr,cointxid))) << OP_CHECKSIG)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysOpRet('D',coin,bindtxid,publishers,txids,height,cointxid,deposithex,proof,destpub,amount))); } @@ -670,18 +755,12 @@ std::string GatewaysDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std:: std::string GatewaysClaim(uint64_t txfee,uint256 bindtxid,std::string refcoin,uint256 deposittxid,CPubKey destpub,int64_t amount) { - CMutableTransaction mtx; CTransaction tx; CPubKey mypk,gatewayspk; struct CCcontract_info *cp,C,*assetscp,C2; uint8_t M,N,taddr,prefix,prefix2,mypriv[32]; std::string coin; std::vector msigpubkeys; int64_t totalsupply,depositamount,inputs,CCchange=0; int32_t numvouts; uint256 hashBlock,assetid,oracletxid; char str[65],depositaddr[64],coinaddr[64]; + CMutableTransaction mtx; CTransaction tx; CPubKey mypk,gatewayspk; struct CCcontract_info *cp,C; uint8_t M,N,taddr,prefix,prefix2; std::string coin; std::vector msigpubkeys; int64_t totalsupply,depositamount,inputs,CCchange=0; int32_t numvouts; uint256 hashBlock,assetid,oracletxid; char str[65],depositaddr[64],coinaddr[64],destaddr[64]; cp = CCinit(&C,EVAL_GATEWAYS); - assetscp = CCinit(&C2,EVAL_ASSETS); if ( txfee == 0 ) txfee = 10000; mypk = pubkey2pk(Mypubkey()); gatewayspk = GetUnspendable(cp,0); - _GetCCaddress(coinaddr,EVAL_ASSETS,gatewayspk); - CCaddr2set(assetscp,EVAL_ASSETS,gatewayspk,cp->CCpriv,coinaddr); - Myprivkey(mypriv); - _GetCCaddress(coinaddr,EVAL_GATEWAYS,mypk); - CCaddr3set(assetscp,EVAL_GATEWAYS,mypk,mypriv,coinaddr); if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) { fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid)); @@ -694,10 +773,10 @@ std::string GatewaysClaim(uint64_t txfee,uint256 bindtxid,std::string refcoin,ui } if ( GetTransaction(deposittxid,tx,hashBlock,false) == 0 ) { - fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid)); + fprintf(stderr,"cant find deposittxid %s\n",uint256_str(str,bindtxid)); return(""); } - if ( (depositamount= GatewaysDepositval(tx)) != amount ) + if ( (depositamount= GatewaysDepositval(tx,mypk)) != amount ) { fprintf(stderr,"invalid Gateways deposittxid %s %.8f != %.8f\n",uint256_str(str,deposittxid),(double)depositamount/COIN,(double)amount/COIN); return(""); @@ -705,35 +784,33 @@ 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,1) > 0 ) { - if ( (inputs= AddAssetInputs(assetscp,mtx,gatewayspk,assetid,amount,60)) > 0 ) + if ( (inputs= AddGatewaysInputs(cp,mtx,gatewayspk,assetid,amount,60)) > 0 ) { if ( inputs > amount ) CCchange = (inputs - amount); - mtx.vin.push_back(CTxIn(deposittxid,0,CScript())); - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,amount,mypk)); + _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 if ( CCchange != 0 ) - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,gatewayspk)); - return(FinalizeCCTx(0,assetscp,mtx,mypk,txfee,EncodeAssetOpRet('t',assetid,zeroid,0,Mypubkey()))); + mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,CCchange,gatewayspk)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeAssetOpRet('t',assetid,zeroid,0,Mypubkey()))); } } fprintf(stderr,"cant find enough inputs or mismatched total\n"); return(""); } -std::string GatewaysWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin,std::vector withdrawpub,int64_t amount) +std::string GatewaysWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount) { - CMutableTransaction mtx; CTransaction tx; CPubKey mypk,gatewayspk; struct CCcontract_info *cp,C,*assetscp,C2; uint256 assetid,hashBlock,oracletxid; int32_t numvouts; int64_t totalsupply,inputs,CCchange=0; uint8_t M,N,taddr,prefix,prefix2,mypriv[32]; std::string coin; std::vector msigpubkeys; char depositaddr[64],str[65],coinaddr[64]; + CMutableTransaction mtx; 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; + std::vector msigpubkeys; char depositaddr[64],str[65],coinaddr[64]; CScript opret; cp = CCinit(&C,EVAL_GATEWAYS); - assetscp = CCinit(&C2,EVAL_ASSETS); if ( txfee == 0 ) txfee = 10000; mypk = pubkey2pk(Mypubkey()); gatewayspk = GetUnspendable(cp,0); - _GetCCaddress(coinaddr,EVAL_ASSETS,gatewayspk); - CCaddr2set(assetscp,EVAL_ASSETS,gatewayspk,cp->CCpriv,coinaddr); - Myprivkey(mypriv); - _GetCCaddress(coinaddr,EVAL_GATEWAYS,mypk); - CCaddr3set(assetscp,EVAL_GATEWAYS,mypk,mypriv,coinaddr); if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) { fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid)); @@ -746,42 +823,49 @@ std::string GatewaysWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin } if ( AddNormalinputs(mtx,mypk,3*txfee,3) > 0 ) { - if ( (inputs= AddAssetInputs(assetscp,mtx,mypk,assetid,amount,60)) > 0 ) + if ( (inputs= AddGatewaysInputs(cp,mtx,mypk,assetid,amount,60)) > 0 ) { if ( inputs > amount ) CCchange = (inputs - amount); - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,amount,gatewayspk)); - mtx.vout.push_back(CTxOut(txfee,CScript() << withdrawpub << OP_CHECKSIG)); - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(gatewayspk)) << OP_CHECKSIG)); + mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,amount,gatewayspk)); + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(withdrawpub)) << OP_CHECKSIG)); + mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,txfee,gatewayspk)); if ( CCchange != 0 ) - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk)); - return(FinalizeCCTx(0,assetscp,mtx,mypk,txfee,EncodeAssetOpRet('t',assetid,zeroid,0,Mypubkey()))); + mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,CCchange,mypk)); + opret << OP_RETURN << E_MARSHAL(ss << cp->evalcode << 'W' << assetid << refcoin << withdrawpub << amount); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); } } fprintf(stderr,"cant find enough inputs or mismatched total\n"); return(""); } -std::string GatewaysMarkdone(uint64_t txfee,uint256 withdrawtxid) +std::string GatewaysMarkdone(uint64_t txfee,uint256 withdrawtxid,std::string refcoin,uint256 cointxid) { CMutableTransaction mtx; CScript opret; CPubKey mypk; struct CCcontract_info *cp,C; cp = CCinit(&C,EVAL_GATEWAYS); if ( txfee == 0 ) txfee = 5000; mypk = pubkey2pk(Mypubkey()); + mtx.vin.push_back(CTxIn(withdrawtxid,2,CScript())); mtx.vout.push_back(CTxOut(5000,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + opret << OP_RETURN << E_MARSHAL(ss << cp->evalcode << 'M' << cointxid << refcoin); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); } UniValue GatewaysPendingWithdraws(uint256 bindtxid,std::string refcoin) { - UniValue result(UniValue::VOBJ),pending(UniValue::VARR),obj(UniValue::VOBJ); CTransaction tx; std::string coin; CPubKey mypk,gatewayspk; std::vector msigpubkeys; uint256 hashBlock,assetid,txid,oracletxid; uint8_t M,N,taddr,prefix,prefix2; char depositaddr[64],withmarker[64],coinaddr[64],destaddr[64],str[65],withaddr[64],numstr[32],txidaddr[64],signeraddr[64]; int32_t i,n,numvouts,vout,numqueued,queueflag; int64_t totalsupply; struct CCcontract_info *cp,C; + UniValue result(UniValue::VOBJ),pending(UniValue::VARR); CTransaction tx; std::string coin,tmprefcoin; CPubKey mypk,gatewayspk,withdrawpub; std::vector msigpubkeys; + uint256 cointxid,hashBlock,assetid,txid,oracletxid; uint8_t M,N,taddr,prefix,prefix2; + char depositaddr[64],withmarker[64],coinaddr[64],destaddr[64],str[65],withaddr[64],numstr[32],txidaddr[64],signeraddr[64]; + int32_t i,n,numvouts,vout,numqueued,queueflag; int64_t totalsupply,amount,nValue; struct CCcontract_info *cp,C; std::vector > unspentOutputs; + cp = CCinit(&C,EVAL_GATEWAYS); mypk = pubkey2pk(Mypubkey()); gatewayspk = GetUnspendable(cp,0); - _GetCCaddress(coinaddr,EVAL_ASSETS,gatewayspk); + _GetCCaddress(coinaddr,EVAL_GATEWAYS,gatewayspk); if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) { fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid)); @@ -799,20 +883,23 @@ UniValue GatewaysPendingWithdraws(uint256 bindtxid,std::string refcoin) { queueflag = 1; break; - } - Getscriptaddress(withmarker,CScript() << ParseHex(HexStr(gatewayspk)) << OP_CHECKSIG); - SetCCunspents(unspentOutputs,withmarker); + } + SetCCunspents(unspentOutputs,coinaddr); numqueued = 0; for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; - if ( GetTransaction(txid,tx,hashBlock,false) != 0 ) + 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()) && + DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,assetid,tmprefcoin,withdrawpub,amount) == 'W' && myIsutxo_spentinmempool(txid,vout) == 0) { Getscriptaddress(destaddr,tx.vout[0].scriptPubKey); Getscriptaddress(withaddr,tx.vout[1].scriptPubKey); if ( strcmp(destaddr,coinaddr) == 0 ) { + UniValue obj(UniValue::VOBJ); obj.push_back(Pair("txid",uint256_str(str,txid))); CCtxidaddr(txidaddr,txid); obj.push_back(Pair("txidaddr",txidaddr)); @@ -824,7 +911,6 @@ UniValue GatewaysPendingWithdraws(uint256 bindtxid,std::string refcoin) obj.push_back(Pair("depositaddr",depositaddr)); Getscriptaddress(signeraddr,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG); obj.push_back(Pair("signeraddr",signeraddr)); - // numqueued += GatewaysAddQueue(refcoin,txid,tx.vout[1].scriptPubKey,tx.vout[0].nValue); } pending.push_back(obj); } @@ -836,3 +922,72 @@ UniValue GatewaysPendingWithdraws(uint256 bindtxid,std::string refcoin) return(result); } +UniValue GatewaysMultisig(char *txidaddr) +{ + std::string parthex,hex,refcoin; uint256 txid,hashBlock; CTransaction tx; int32_t i,maxK,K,numvouts; CPubKey signerpk; + std::vector > unspentOutputs; UniValue result(UniValue::VOBJ); + + SetCCunspents(unspentOutputs,txidaddr); + maxK=0; + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + if (GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size()) > 0 && DecodeGatewaysPartialOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,K,signerpk,refcoin,hex) == 'P' && K>maxK ) + { + maxK=K; + parthex=hex; + } + } + + BOOST_FOREACH(const CTxMemPoolEntry &e, mempool.mapTx) + { + const CTransaction &txmempool = e.GetTx(); + const uint256 &hash = txmempool.GetHash(); + + if ((numvouts=txmempool.vout.size()) > 0 && DecodeGatewaysPartialOpRet(txmempool.vout[numvouts-1].scriptPubKey,K,signerpk,refcoin,hex) == 'P' && K>maxK) + { + maxK=K; + parthex=hex; + } + } + + result.push_back(Pair("hex",parthex)); + result.push_back(Pair("number_of_signs",maxK)); + return (result); +} + +std::string GatewaysPartialSign(uint64_t txfee,uint256 txid,std::string refcoin, std::string hex) +{ + CMutableTransaction mtx; CScript opret; CPubKey mypk,txidaddrpk,signerpk; struct CCcontract_info *cp,C; CTransaction tx; + std::vector > unspentOutputs; char txidaddr[65]; + int32_t maxK,K=0; uint256 tmptxid,parttxid,hashBlock; + cp = CCinit(&C,EVAL_GATEWAYS); + if ( txfee == 0 ) + txfee = 5000; + mypk = pubkey2pk(Mypubkey()); + txidaddrpk=CCtxidaddr(txidaddr,txid); + SetCCunspents(unspentOutputs,txidaddr); + maxK=0; + if (unspentOutputs.size()==0) + { + if (AddNormalinputs(mtx,mypk,2*txfee,2)==0) fprintf(stderr,"error adding funds for partialsign\n"); + } + else + { + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + tmptxid = it->first.txhash; + if (GetTransaction(tmptxid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && DecodeGatewaysPartialOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,K,signerpk,refcoin,hex) == 'P' && K>maxK ) + { + maxK=K; + parttxid=tmptxid; + } + } + if (maxK>0) mtx.vin.push_back(CTxIn(parttxid,0,CScript())); + else fprintf(stderr,"Error finding previous partial tx\n"); + } + + mtx.vout.push_back(CTxOut(5000,CScript() << ParseHex(HexStr(txidaddrpk)) << OP_CHECKSIG)); + opret << OP_RETURN << E_MARSHAL(ss << cp->evalcode << 'P' << maxK+1 << mypk << refcoin << hex); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); +} diff --git a/src/cc/MofN.cpp b/src/cc/heir.cpp similarity index 71% rename from src/cc/MofN.cpp rename to src/cc/heir.cpp index 0a038815c..cdbc7918e 100644 --- a/src/cc/MofN.cpp +++ b/src/cc/heir.cpp @@ -13,30 +13,16 @@ * * ******************************************************************************/ -#include "CCMofN.h" +#include "CCHeir.h" /* - The idea of MofN CC is to allow non-interactive multisig, preferably in a cross chain compatible way, ie. for actual bitcoin multisig. - - full redeemscript in an initial tx with opreturn - ability to post partial signatures and construct a full transaction from M such partial signatures - a new transaction would refer to the initialtx and other partial would refer to both - - There is no need for a CC contract to use it for normal multisig as normal multisig transactions are already supported. - - In order to take advantage of CC powers, we can create a more powerful multisig using shamir's secret MofN (up to 255) algo to allow spends. Using the same non-interactive partial signing is possible. also, in addition to spending, data payload can have additional data that is also revealed when the funds are spent. - - rpc calls needed: - 1) create msig address (normal or shamir) - 2) post payment with partial sig - 3) add partial sig to 2) - 4) combine and submit M partial sigs - + The idea of Heir CC is to allow crypto inheritance. + A special 1of2 CC address is created that is freely spendable by the creator. The heir is only allowed to spend after the specified amount of idle blocks. The idea is that if the address doesnt spend any funds for a year (or whatever amount set), then it is time to allow the heir to spend. The design requires the heir to spend all the funds at once */ // start of consensus code -int64_t IsMofNvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) +int64_t IsHeirvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) { char destaddr[64]; if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) @@ -47,7 +33,7 @@ int64_t IsMofNvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) return(0); } -bool MofNExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee) +bool HeirExactAmounts(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; @@ -65,8 +51,8 @@ bool MofNExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction & { //fprintf(stderr,"vini.%d check hash and vout\n",i); if ( hashBlock == zerohash ) - return eval->Invalid("cant MofN from mempool"); - if ( (assetoshis= IsMofNvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) + return eval->Invalid("cant Heir from mempool"); + if ( (assetoshis= IsHeirvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) inputs += assetoshis; } } @@ -74,7 +60,7 @@ bool MofNExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction & for (i=0; i origpubkey; CTransaction vintx; int32_t vout,n = 0; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); @@ -139,7 +126,7 @@ int64_t AddMofNInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKe // no need to prevent dup if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { - if ( (nValue= IsMofNvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(txid,vout) == 0 ) + if ( (nValue= IsHeirvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(txid,vout) == 0 ) { if ( total != 0 && maxinputs != 0 ) mtx.vin.push_back(CTxIn(txid,vout,CScript())); @@ -154,27 +141,27 @@ int64_t AddMofNInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKe return(totalinputs); } -std::string MofNGet(uint64_t txfee,int64_t nValue) +std::string HeirGet(uint64_t txfee,int64_t nValue) { - CMutableTransaction mtx,tmpmtx; CPubKey mypk,mofnpk; int64_t inputs,CCchange=0; struct CCcontract_info *cp,C; std::string rawhex; uint32_t j; int32_t i,len; uint8_t buf[32768]; bits256 hash; - cp = CCinit(&C,EVAL_MOFN); + CMutableTransaction mtx,tmpmtx; CPubKey mypk,Heirpk; int64_t inputs,CCchange=0; struct CCcontract_info *cp,C; std::string rawhex; uint32_t j; int32_t i,len; uint8_t buf[32768]; bits256 hash; + cp = CCinit(&C,EVAL_HEIR); if ( txfee == 0 ) txfee = 10000; - mofnpk = GetUnspendable(cp,0); + Heirpk = GetUnspendable(cp,0); mypk = pubkey2pk(Mypubkey()); - if ( (inputs= AddMofNInputs(cp,mtx,mofnpk,nValue+txfee,60)) > 0 ) + if ( (inputs= AddHeirInputs(cp,mtx,Heirpk,nValue+txfee,60)) > 0 ) { if ( inputs > nValue ) CCchange = (inputs - nValue - txfee); if ( CCchange != 0 ) - mtx.vout.push_back(MakeCC1vout(EVAL_MOFN,CCchange,mofnpk)); + mtx.vout.push_back(MakeCC1vout(EVAL_HEIR,CCchange,Heirpk)); 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_MOFN << (uint8_t)'G' << j)); + rawhex = FinalizeCCTx(-1LL,cp,tmpmtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_HEIR << (uint8_t)'G' << j)); if ( (len= (int32_t)rawhex.size()) > 0 && len < 65536 ) { len >>= 1; @@ -190,35 +177,35 @@ std::string MofNGet(uint64_t txfee,int64_t nValue) } fprintf(stderr,"couldnt generate valid txid %u\n",(uint32_t)time(NULL)); return(""); - } else fprintf(stderr,"cant find mofn inputs\n"); + } else fprintf(stderr,"cant find Heir inputs\n"); return(""); } -std::string MofNFund(uint64_t txfee,int64_t funds) +std::string HeirFund(uint64_t txfee,int64_t funds) { - CMutableTransaction mtx; CPubKey mypk,mofnpk; CScript opret; struct CCcontract_info *cp,C; - cp = CCinit(&C,EVAL_MOFN); + CMutableTransaction mtx; CPubKey mypk,Heirpk; CScript opret; struct CCcontract_info *cp,C; + cp = CCinit(&C,EVAL_HEIR); if ( txfee == 0 ) txfee = 10000; mypk = pubkey2pk(Mypubkey()); - mofnpk = GetUnspendable(cp,0); + Heirpk = GetUnspendable(cp,0); if ( AddNormalinputs(mtx,mypk,funds+txfee,64) > 0 ) { - mtx.vout.push_back(MakeCC1vout(EVAL_MOFN,funds,mofnpk)); + mtx.vout.push_back(MakeCC1vout(EVAL_HEIR,funds,Heirpk)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); } return(""); } -UniValue MofNInfo() +UniValue HeirInfo() { UniValue result(UniValue::VOBJ); char numstr[64]; - CMutableTransaction mtx; CPubKey mofnpk; struct CCcontract_info *cp,C; int64_t funding; + CMutableTransaction mtx; CPubKey Heirpk; struct CCcontract_info *cp,C; int64_t funding; result.push_back(Pair("result","success")); - result.push_back(Pair("name","MofN")); - cp = CCinit(&C,EVAL_MOFN); - mofnpk = GetUnspendable(cp,0); - funding = AddMofNInputs(cp,mtx,mofnpk,0,0); + result.push_back(Pair("name","Heir")); + cp = CCinit(&C,EVAL_HEIR); + Heirpk = GetUnspendable(cp,0); + funding = AddHeirInputs(cp,mtx,Heirpk,0,0); sprintf(numstr,"%.8f",(double)funding/COIN); result.push_back(Pair("funding",numstr)); return(result); diff --git a/src/cc/lotto.cpp b/src/cc/lotto.cpp index 9fa4daa24..26ce11c7f 100644 --- a/src/cc/lotto.cpp +++ b/src/cc/lotto.cpp @@ -162,6 +162,7 @@ bool LottoValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, int64_t AddLottoInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) { + // add threshold check char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t n = 0; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); @@ -291,10 +292,11 @@ std::string LottoCreate(uint64_t txfee,char *planstr,int64_t funding,int32_t tic sbits = stringbits(planstr); if ( AddNormalinputs(mtx,mypk,funding+txfee,60) > 0 ) { - hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash); + hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash,mtx.vin[0].prevout.n,1); mtx.vout.push_back(MakeCC1vout(EVAL_LOTTO,funding,lottopk)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_LOTTO << (uint8_t)'F' << sbits << ticketsize << odds << firstheight << period << hentropy))); } + return(""); } std::string LottoTicket(uint64_t txfee,uint256 lottoid,int64_t numtickets) diff --git a/src/cc/oracles.cpp b/src/cc/oracles.cpp index a8ac77206..58c1ccaee 100644 --- a/src/cc/oracles.cpp +++ b/src/cc/oracles.cpp @@ -157,7 +157,8 @@ uint8_t DecodeOraclesData(const CScript &scriptPubKey,uint256 &oracletxid,uint25 { if ( e == EVAL_ORACLES && f == 'D' ) return(f); - } + //else fprintf(stderr,"DecodeOraclesData evalcode.%d f.%c\n",e,f); + } //else fprintf(stderr,"DecodeOraclesData not enough opereturn data\n"); return(0); } @@ -439,6 +440,7 @@ int64_t correlate_price(int32_t height,int64_t *prices,int32_t n) for (i=0; i %llu ht.%d\n",(long long)price,height); + return(price); } int64_t OracleCorrelatedPrice(int32_t height,std::vector origprices) @@ -649,8 +651,12 @@ bool OraclesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t } return eval->Invalid("unexpected OraclesValidate 'D' tx invalid"); break; + default: + fprintf(stderr,"illegal oracles funcid.(%c)\n",script[1]); + return eval->Invalid("unexpected OraclesValidate funcid"); + break; } - } + } else return eval->Invalid("unexpected oracles missing funcid"); return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts)); } return(true); @@ -665,6 +671,7 @@ int64_t AddOracleInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPub std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); SetCCunspents(unspentOutputs,coinaddr); + //fprintf(stderr,"addoracleinputs from (%s)\n",coinaddr); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; @@ -803,7 +810,7 @@ std::string OracleData(int64_t txfee,uint256 oracletxid,std::vector da mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,batonpk)); mtx.vout.push_back(CTxOut(datafee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeOraclesData('D',oracletxid,batontxid,mypk,data))); - } else fprintf(stderr,"couldnt find enough oracle inputs, limit 1 per utxo\n"); + } else fprintf(stderr,"couldnt find enough oracle inputs %s, limit 1 per utxo\n",coinaddr); } else fprintf(stderr,"couldnt add normal inputs\n"); return(""); } @@ -852,11 +859,18 @@ UniValue OracleDataSamples(uint256 reforacletxid,uint256 batontxid,int32_t num) UniValue OracleInfo(uint256 origtxid) { - UniValue result(UniValue::VOBJ),a(UniValue::VARR),obj(UniValue::VOBJ); + UniValue result(UniValue::VOBJ),a(UniValue::VARR); std::vector > unspentOutputs; CMutableTransaction mtx; CTransaction regtx,tx; std::string name,description,format; uint256 hashBlock,txid,oracletxid,batontxid; CPubKey pk; struct CCcontract_info *cp,C; int64_t datafee,funding; char str[67],markeraddr[64],numstr[64],batonaddr[64]; std::vector data; cp = CCinit(&C,EVAL_ORACLES); CCtxidaddr(markeraddr,origtxid); + if ( GetTransaction(origtxid,tx,hashBlock,false) == 0 ) + { + fprintf(stderr,"cant find oracleid\n"); + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cant find oracleid")); + return(result); + } if ( GetTransaction(origtxid,tx,hashBlock,false) != 0 ) { if ( tx.vout.size() > 0 && DecodeOraclesCreateOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,name,description,format) == 'C' ) @@ -875,6 +889,7 @@ UniValue OracleInfo(uint256 origtxid) { if ( regtx.vout.size() > 0 && DecodeOraclesOpRet(regtx.vout[regtx.vout.size()-1].scriptPubKey,oracletxid,pk,datafee) == 'R' && oracletxid == origtxid ) { + UniValue obj(UniValue::VOBJ); obj.push_back(Pair("publisher",pubkey33_str(str,(uint8_t *)pk.begin()))); Getscriptaddress(batonaddr,regtx.vout[1].scriptPubKey); batontxid = OracleBatonUtxo(10000,cp,oracletxid,batonaddr,pk,data); diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index fcf2da78e..de92da9ed 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -115,6 +115,7 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) { + // add threshold check char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); diff --git a/src/cc/pegs.cpp b/src/cc/pegs.cpp index 6df6d18d6..1de818fe9 100644 --- a/src/cc/pegs.cpp +++ b/src/cc/pegs.cpp @@ -122,6 +122,7 @@ bool PegsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, int64_t AddPegsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) { + // add threshold check char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index b732c994c..be2d422d6 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -28,67 +28,62 @@ Funds work like with dice, ie. there is a Prices plan that traders bet against. - PricesOpen -> oracletxid start with 'L' price, leverage, amount - funds are locked into global CC address + PricesFunding oracletxid, margin, priceaveraging, maxleverage, funding, longtoken, shorttoken, N [pubkeys] + + PricesBet -> oracletxid start with 'L', leverage, funding, direction + funds are locked into global CC address it can be closed at anytime by the trader for cash settlement the house account can close it if rekt Implementation Notes: In order to eliminate the need for worrying about sybil attacks, each prices plan would be able to specific pubkey(s?) for whitelisted publishers. It would be possible to have a non-whitelisted plan that would use 50% correlation between publishers. - delta neutral balancing of risk exposure + delta neutral balancing of riskexposure: fabs(long exposure - short exposure) + bet +B at leverage L + absval(sum(+BLi) - sum(-Bli)) + + validate: update riskexposure and it needs to be <= funds + + PricesProfits: limit withdraw to funds in excess of riskexposure + PricesFinish: payout (if winning) and update riskexposure + need long/short exposure assets + + funding -> 1of2 CC global CC address and dealer address, exposure tokens to global 1of2 assets CC address + pricebet -> user funds and exposure token to 1of2 address. + pricewin -> winnings from dealer funds, exposure token back to global address + priceloss -> exposuretoken back to global address + + exposure address, funds address + + */ // start of consensus code -int64_t IsPricesvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) +int64_t PricesOraclePrice(int64_t &rektprice,uint64_t mode,uint256 oracletxid,std::vectorpubkeys,int32_t dir,int64_t amount,int32_t leverage) { - 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); + int64_t price; + // howto ensure price when block it confirms it not known + // get price from oracle + current chaintip + // normalize leveraged amount + if ( dir > 0 ) + rektprice = price * leverage / (leverage-1); + else rektprice = price * (leverage-1) / leverage; + return(price); } -bool PricesExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee) +CScript EncodePricesFundingOpRet(uint8_t funcid,CPubKey planpk,uint256 oracletxid,uint256 longtoken,uint256 shorttoken,int32_t millimargin,uint64_t mode,int32_t maxleverage,std::vector pubkeys,uint256 bettoken) { - static uint256 zerohash; - CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis; - numvins = tx.vin.size(); - numvouts = tx.vout.size(); - for (i=0; iismyvin)(tx.vin[i].scriptSig) != 0 ) - { - //fprintf(stderr,"vini.%d check mempool\n",i); - if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) - return eval->Invalid("cant find vinTx"); - else - { - //fprintf(stderr,"vini.%d check hash and vout\n",i); - if ( hashBlock == zerohash ) - return eval->Invalid("cant Prices from mempool"); - if ( (assetoshis= IsPricesvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) - inputs += assetoshis; - } - } - } - for (i=0; iInvalid("mismatched inputs != outputs + txfee"); - } - else return(true); + CScript opret; + fprintf(stderr,"implement EncodePricesFundingOpRet\n"); + return(opret); +} + +uint8_t DecodePricesFundingOpRet(CScript scriptPubKey,CPubKey &planpk,uint256 &oracletxid,uint256 &longtoken,uint256 &shorttoken,int32_t &millimargin,uint64_t &mode,int32_t &maxleverage,std::vector &pubkeys,uint256 &bettoken) +{ + fprintf(stderr,"implement DecodePricesFundingOpRet\n"); + return(0); } bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) @@ -111,12 +106,12 @@ bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx } } //fprintf(stderr,"check amounts\n"); - if ( PricesExactAmounts(cp,eval,tx,1,10000) == false ) + //if ( PricesExactAmounts(cp,eval,tx,1,10000) == false ) { fprintf(stderr,"Pricesget invalid amount\n"); return false; } - else + //else { txid = tx.GetHash(); memcpy(hash,&txid,sizeof(hash)); @@ -132,20 +127,21 @@ bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx // helper functions for rpc calls in rpcwallet.cpp -int64_t AddPricesInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) +int64_t AddTokensInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char *destaddr,uint256 tolenid,int64_t total,int32_t maxinputs) { - char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; + // add threshold check + int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; std::vector > unspentOutputs; - GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,destaddr); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; - // no need to prevent dup - if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) + // need to prevent dup + if ( GetTransaction(txid,vintx,hashBlock,false) != 0 && vout < vintx.vout.size() ) { - if ( (nValue= IsPricesvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(txid,vout) == 0 ) + // need to verify assetid + if ( (nValue= vintx.vout[vout].nValue) >= 10000 && myIsutxo_spentinmempool(txid,vout) == 0 ) { if ( total != 0 && maxinputs != 0 ) mtx.vin.push_back(CTxIn(txid,vout,CScript())); @@ -160,49 +156,18 @@ int64_t AddPricesInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPub return(totalinputs); } -#ifdef later -UniValue PricesInfo(uint256 pricesid) -{ - UniValue result(UniValue::VOBJ); CPubKey pricepk; uint256 hashBlock,oracletxid; CTransaction vintx; int64_t minbet,maxbet,maxodds; uint64_t funding; char numstr[65]; struct CCcontract_info *cp,C; - if ( GetTransaction(pricesid,vintx,hashBlock,false) == 0 ) - { - fprintf(stderr,"cant find fundingtxid\n"); - ERR_RESULT("cant find fundingtxid"); - return(result); - } - if ( vintx.vout.size() > 0 && DecodePricesFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,oracletxid,minbet,maxbet,maxodds) == 0 ) - { - fprintf(stderr,"fundingtxid isnt price creation txid\n"); - ERR_RESULT("fundingtxid isnt price creation txid"); - return(result); - } - result.push_back(Pair("result","success")); - result.push_back(Pair("pricesid",uint256_str(str,pricesid))); - result.push_back(Pair("oracletxid",uint256_str(str,oracletxid))); - sprintf(numstr,"%.8f",(double)minbet/COIN); - result.push_back(Pair("minbet",numstr)); - sprintf(numstr,"%.8f",(double)maxbet/COIN); - result.push_back(Pair("maxbet",numstr)); - result.push_back(Pair("maxodds",maxodds)); - cp = CCinit(&C,EVAL_PRICES); - pricepk = GetUnspendable(cp,0); - funding = PricePlanFunds(cp,pricepk,pricesid); - sprintf(numstr,"%.8f",(double)funding/COIN); - result.push_back(Pair("funding",numstr)); - return(result); -} - UniValue PricesList() { - UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; uint256 txid,hashBlock,oracletxid; CTransaction vintx; int64_t minbet,maxbet,maxodds; char str[65]; + UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; uint64_t mode; int32_t margin,maxleverage; std::vectorpubkeys; uint256 txid,hashBlock,oracletxid,longtoken,shorttoken,bettoken; CPubKey planpk,pricespk; char str[65]; CTransaction vintx; cp = CCinit(&C,EVAL_PRICES); + pricespk = GetUnspendable(cp,0); SetCCtxids(addressIndex,cp->normaladdr); for (std::vector >::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 && DecodePricesFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,oracletxid,minbet,maxbet,maxodds) != 0 ) + if ( vintx.vout.size() > 0 && DecodePricesFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' ) { result.push_back(uint256_str(str,txid)); } @@ -211,59 +176,161 @@ UniValue PricesList() return(result); } -std::string PricesCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks) +// longtoken satoshis limits long exposure +// shorttoken satoshis limits short exposure +// both must be in the 1of2 CC address with its total supply +// bettoken +std::string PricesCreateFunding(uint64_t txfee,uint256 bettoken,uint256 oracletxid,uint64_t margin,uint64_t mode,uint256 longtoken,uint256 shorttoken,int32_t maxleverage,int64_t funding,std::vector pubkeys) { - CMutableTransaction mtx; uint256 zero; CScript fundingPubKey; CPubKey mypk,pricepk; int64_t a,b,c,d; uint64_t sbits; struct CCcontract_info *cp,C; - if ( funds < 0 || minbet < 0 || maxbet < 0 || maxodds < 1 || maxodds > 9999 || timeoutblocks < 0 || timeoutblocks > 1440 ) + CMutableTransaction mtx; CTransaction oracletx; int64_t fullsupply,inputs,CCchange=0; uint256 hashBlock; char str[65],coinaddr[64],houseaddr[64]; CPubKey mypk,pricespk; int32_t i,N,numvouts; struct CCcontract_info *cp,C; + if ( funding < 100*COIN || maxleverage <= 0 || maxleverage > 10000 ) { CCerror = "invalid parameter error"; fprintf(stderr,"%s\n", CCerror.c_str() ); return(""); } - if ( funds < 100*COIN ) + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + if ( (N= (int32_t)pubkeys.size()) || N > 15 ) { - CCerror = "price plan needs at least 100 coins"; - fprintf(stderr,"%s\n", CCerror.c_str() ); + fprintf(stderr,"too many pubkeys N.%d\n",N); return(""); } - memset(&zero,0,sizeof(zero)); - if ( (cp= Pricesinit(fundingPubKey,zero,&C,planstr,txfee,mypk,pricepk,sbits,a,b,c,d)) == 0 ) + for (i=0; i 0 ) + if ( CCtoken_balance(houseaddr,longtoken) != CCfullsupply(longtoken) ) + { + fprintf(stderr,"PricesCreateFunding (%s) globaladdr.%s token balance %.8f != %.8f\n",uint256_str(str,longtoken),houseaddr,(double)CCtoken_balance(houseaddr,longtoken)/COIN,(double)CCfullsupply(longtoken)/COIN); + return(""); + } + if ( CCtoken_balance(houseaddr,shorttoken) != CCfullsupply(shorttoken) ) + { + fprintf(stderr,"PricesCreateFunding (%s) globaladdr.%s token balance %.8f != %.8f\n",uint256_str(str,longtoken),houseaddr,(double)CCtoken_balance(houseaddr,longtoken)/COIN,(double)CCfullsupply(shorttoken)/COIN); + return(""); + } + if ( GetTransaction(oracletxid,oracletx,hashBlock,false) == 0 || (numvouts= oracletx.vout.size()) <= 0 ) + { + fprintf(stderr,"cant find oracletxid %s\n",uint256_str(str,oracletxid)); + return(""); + } + fprintf(stderr,"error check bettoken\n"); + if ( AddNormalinputs(mtx,mypk,3*txfee,3) > 0 ) { - mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,pricepk)); mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(pricepk)) << OP_CHECKSIG)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesFundingOpRet('F',sbits,minbet,maxbet,maxodds,timeoutblocks))); + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(pricespk)) << OP_CHECKSIG)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesFundingOpRet('F',mypk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken))); + } + else + { + CCerror = "cant find enough inputs"; + fprintf(stderr,"%s\n", CCerror.c_str() ); } - CCerror = "cant find enough inputs"; - fprintf(stderr,"%s\n", CCerror.c_str() ); return(""); } -std::string PricesAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount) +UniValue PricesInfo(uint256 fundingtxid) { - CMutableTransaction mtx; CScript fundingPubKey,scriptPubKey; CPubKey mypk,pricepk; struct CCcontract_info *cp,C; int64_t minbet,maxbet,maxodds; - if ( amount < 0 ) + UniValue result(UniValue::VOBJ),a(UniValue::VARR); CPubKey pricespk,planpk; uint256 hashBlock,oracletxid,longtoken,shorttoken,bettoken; CTransaction vintx; int64_t balance,supply,exposure; uint64_t funding,mode; int32_t i,margin,maxleverage; char numstr[65],houseaddr[64],exposureaddr[64],str[65]; std::vectorpubkeys; struct CCcontract_info *cp,C; + cp = CCinit(&C,EVAL_PRICES); + pricespk = GetUnspendable(cp,0); + if ( GetTransaction(fundingtxid,vintx,hashBlock,false) == 0 ) + { + fprintf(stderr,"cant find fundingtxid\n"); + ERR_RESULT("cant find fundingtxid"); + return(result); + } + if ( vintx.vout.size() > 0 && DecodePricesFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' ) + { + result.push_back(Pair("result","success")); + result.push_back(Pair("fundingtxid",uint256_str(str,fundingtxid))); + result.push_back(Pair("bettoken",uint256_str(str,bettoken))); + result.push_back(Pair("oracletxid",uint256_str(str,oracletxid))); + sprintf(numstr,"%.3f",(double)margin/1000); + result.push_back(Pair("profitmargin",numstr)); + result.push_back(Pair("maxleverage",maxleverage)); + result.push_back(Pair("mode",(int64_t)mode)); + for (i=0; ipubkeys; + if ( amount < 10000 ) { CCerror = "amount must be positive"; fprintf(stderr,"%s\n", CCerror.c_str() ); return(""); } - if ( (cp= Pricesinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,pricepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 ) - return(""); - scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG; - if ( scriptPubKey == fundingPubKey ) + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + GetCCaddress(cp,myaddr,mypk); + if ( GetTransaction(fundingtxid,tx,hashBlock,false) == 0 ) { - if ( AddNormalinputs(mtx,mypk,amount+2*txfee,60) > 0 ) + fprintf(stderr,"cant find fundingtxid\n"); + return(""); + } + if ( tx.vout.size() > 0 && DecodePricesFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' && bettoken == refbettoken ) + { + GetCCaddress1of2(cp,houseaddr,pricespk,planpk); + if ( AddNormalinputs(mtx,mypk,2*txfee,3) > 0 ) { - mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,pricepk)); - mtx.vout.push_back(CTxOut(txfee,fundingPubKey)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesOpRet('E',sbits,fundingtxid,hentropy,zeroid))); + if ( (inputs= AddTokensInputs(cp,mtx,myaddr,bettoken,amount,60)) >= amount ) + { + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,amount,pricespk,planpk)); + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(planpk)) << OP_CHECKSIG)); + if ( inputs > amount+txfee ) + 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()))); + } + else + { + CCerror = "cant find enough bet inputs"; + fprintf(stderr,"%s\n", CCerror.c_str() ); + } } else { @@ -271,66 +338,106 @@ std::string PricesAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,in fprintf(stderr,"%s\n", CCerror.c_str() ); } } - else - { - CCerror = "only fund creator can add more funds (entropy)"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - } return(""); } -std::string PricesBet(uint64_t txfee,uint256 pricesid,int64_t bet,int32_t odds) +std::string PricesBet(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,int64_t amount,int32_t leverage) { - CMutableTransaction mtx; CScript fundingPubKey; CPubKey mypk,pricepk; int64_t funding,minbet,maxbet,maxodds; struct CCcontract_info *cp,C; - if ( bet < 0 ) + CMutableTransaction mtx; struct CCcontract_info *cp,C; CPubKey pricespk,planpk,mypk; uint256 hashBlock,oracletxid,longtoken,shorttoken,tokenid,bettoken; CTransaction tx; int64_t balance,supply,exposure,inputs,inputs2,longexposure,netexposure,shortexposure,CCchange = 0,CCchange2 = 0; uint64_t funding,mode; int32_t dir,margin,maxleverage; char houseaddr[64],myaddr[64],exposureaddr[64]; std::vectorpubkeys; + if ( amount < 0 ) { - CCerror = "bet must be positive"; - fprintf(stderr,"%s\n", CCerror.c_str() ); + amount = -amount; + dir = -1; + } else dir = 1; + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + GetCCaddress(cp,myaddr,mypk); + if ( GetTransaction(fundingtxid,tx,hashBlock,false) == 0 ) + { + fprintf(stderr,"cant find fundingtxid\n"); return(""); } - if ( odds < 1 || odds > 9999 ) + if ( tx.vout.size() > 0 && DecodePricesFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' && bettoken == refbettoken ) { - CCerror = "odds must be between 1 and 9999"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - if ( (cp= Pricesinit(fundingPubKey,pricesid,&C,txfee,mypk,pricepk,minbet,maxbet,maxodds)) == 0 ) - return(""); - if ( bet < minbet || bet > maxbet || odds > maxodds ) - { - CCerror = strprintf("Price plan %s illegal bet %.8f: minbet %.8f maxbet %.8f or odds %d vs max.%d\n",planstr,(double)bet/COIN,(double)minbet/COIN,(double)maxbet/COIN,(int32_t)odds,(int32_t)maxodds); - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - if ( (funding= PricesPlanFunds(cp,pricepk,pricesid)) >= 2*bet*odds+txfee ) - { - if ( myIsutxo_spentinmempool(entropytxid,0) != 0 ) + if ( leverage > maxleverage || leverage < 1 ) { - CCerror = "entropy txid is spent"; - fprintf(stderr,"%s\n", CCerror.c_str() ); + fprintf(stderr,"illegal leverage\n"); return(""); } - if ( AddNormalinputs(mtx,mypk,bet+2*txfee+odds,60) > 0 ) + GetCCaddress1of2(cp,houseaddr,pricespk,planpk); + GetCCaddress1of2(cp,exposureaddr,pricespk,pricespk); + if ( dir < 0 ) + tokenid = shorttoken; + else tokenid = longtoken; + exposure = leverage * amount; + longexposure = CCtoken_balance(exposureaddr,longtoken); + shortexposure = CCtoken_balance(exposureaddr,shorttoken); + netexposure = (longexposure - shortexposure + exposure*dir); + if ( netexposure < 0 ) + netexposure = -netexposure; + balance = CCtoken_balance(myaddr,bettoken) / COIN; + if ( balance < netexposure*9/10 ) // 10% extra room for dynamically closed bets in wrong direction { - mtx.vout.push_back(MakeCC1vout(cp->evalcode,entropyval,pricepk)); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,bet,pricepk)); - mtx.vout.push_back(CTxOut(txfee+odds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesOpRet('B',pricesid))); - } else fprintf(stderr,"cant find enough normal inputs for %.8f, plan funding %.8f\n",(double)bet/COIN,(double)funding/COIN); + fprintf(stderr,"balance %lld < 90%% netexposure %lld, refuse bet\n",(long long)balance,(long long)netexposure); + return(""); + } + if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 ) + { + if ( (inputs= AddTokensInputs(cp,mtx,houseaddr,tokenid,exposure,30)) >= exposure ) + { + if ( (inputs2= AddTokensInputs(cp,mtx,myaddr,bettoken,amount,30)) >= amount ) + { + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,amount,pricespk,planpk)); + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,exposure,pricespk,pricespk)); + if ( inputs > exposure+txfee ) + CCchange = (inputs - exposure); + if ( inputs2 > amount+txfee ) + CCchange2 = (inputs2 - amount); + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,CCchange,pricespk,planpk)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange2,mypk)); + // add addr2 and addr3 + //return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesExtra('T',tokenid,bettoken,zeroid,dir*leverage))); + CScript opret; + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); + } + else + { + fprintf(stderr,"cant find enough bettoken inputs\n"); + return(""); + } + } + else + { + fprintf(stderr,"cant find enough exposure inputs\n"); + return(""); + } + } + else + { + CCerror = "cant find enough inputsB"; + fprintf(stderr,"%s\n", CCerror.c_str() ); + } } - if ( entropyval == 0 && funding != 0 ) - CCerror = "cant find price entropy inputs"; - else CCerror = "cant find price input"; - fprintf(stderr,"%s\n", CCerror.c_str() ); return(""); } -std::string PricesBetFinish(int32_t *resultp,uint64_t txfee,uint256 pricesid,uint256 bettxid) +UniValue PricesStatus(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,uint256 bettxid) +{ + UniValue result(UniValue::VOBJ); + // get height of bettxid + // get price and rekt + // get current height and price + // what about if rekt in the past? + return(result); +} + +std::string PricesFinish(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,uint256 bettxid) { - *resultp = -1; - CCerror = "couldnt find bettx or entropytx"; - fprintf(stderr,"%s\n", CCerror.c_str() ); return(""); } -#endif + + diff --git a/src/cc/rewards.cpp b/src/cc/rewards.cpp index 0f524756d..95f032e8e 100644 --- a/src/cc/rewards.cpp +++ b/src/cc/rewards.cpp @@ -287,8 +287,12 @@ bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t return eval->Invalid("unlock tx vout.2 isnt 0"); preventCCvouts = 1; break; + default: + fprintf(stderr,"illegal rewards funcid.(%c)\n",funcid); + return eval->Invalid("unexpected rewards funcid"); + break; } - } + } else return eval->Invalid("unexpected rewards missing funcid"); return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts)); } return(true); @@ -325,15 +329,16 @@ static uint64_t myIs_unlockedtx_inmempool(uint256 &txid,int32_t &vout,uint64_t r // 'L' vs 'F' and 'A' int64_t AddRewardsInputs(CScript &scriptPubKey,uint64_t maxseconds,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs,uint64_t refsbits,uint256 reffundingtxid) { - char coinaddr[64],str[65]; uint64_t sbits,nValue,totalinputs = 0; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t numblocks,j,vout,n = 0; uint8_t funcid; + char coinaddr[64],str[65]; uint64_t threshold,sbits,nValue,totalinputs = 0; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t numblocks,j,vout,n = 0; uint8_t funcid; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); SetCCunspents(unspentOutputs,coinaddr); + threshold = total/maxinputs; for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; - if ( it->second.satoshis < 1000000 ) + if ( it->second.satoshis < threshold ) continue; //fprintf(stderr,"(%s) %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); for (j=0; j origpubkey; CTransaction vintx; int32_t vout,n = 0; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 2e4414a40..526d4f7c9 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -82,7 +82,6 @@ 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 std::string VERUS_CHEATCATCHER; const arith_uint256 maxUint = UintToArith256(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); diff --git a/src/cheatcatcher.cpp b/src/cheatcatcher.cpp deleted file mode 100644 index ab4af2141..000000000 --- a/src/cheatcatcher.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/******************************************************************** - * (C) 2018 Michael Toutonghi - * - * Distributed under the MIT software license, see the accompanying - * file COPYING or http://www.opensource.org/licenses/mit-license.php. - * - * This supports code to catch nothing at stake cheaters who stake - * on multiple forks. - * - */ - -#include "cc/StakeGuard.h" -#include "script/script.h" -#include "main.h" -#include "hash.h" -#include "cheatcatcher.h" -#include "streams.h" - -using namespace std; - -CCheatList cheatList; -boost::optional cheatCatcher; - -uint32_t CCheatList::Prune(uint32_t height) -{ - uint32_t count = 0; - pair::iterator, multimap::iterator> range; - vector toPrune; - - if (height > 0 && NetworkUpgradeActive(height, Params().GetConsensus(), Consensus::UPGRADE_SAPLING)) - { - LOCK(cs_cheat); - for (auto it = orderedCheatCandidates.begin(); it != orderedCheatCandidates.end() && it->second.height <= height; it--) - { - toPrune.push_back(&it->second); - } - count = toPrune.size(); - for (auto ptxHolder : toPrune) - { - Remove(*ptxHolder); - } - } - return count; // return how many removed -} - -bool GetStakeParams(const CTransaction &stakeTx, CStakeParams &stakeParams); - -bool CCheatList::IsHeightOrGreaterInList(uint32_t height) -{ - auto range = orderedCheatCandidates.equal_range(height); - //printf("IsHeightOrGreaterInList: %s\n", range.second == orderedCheatCandidates.end() ? "false" : "true"); - return (range.first != orderedCheatCandidates.end() || range.second != orderedCheatCandidates.end()); -} - -bool CCheatList::IsCheatInList(const CTransaction &tx, CTransaction *cheatTx) -{ - // for a tx to be cheat, it needs to spend the same UTXO and be for a different prior block - // the list should be pruned before this call - // we return the first valid cheat we find - CVerusHashWriter hw = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION); - - hw << tx.vin[0].prevout.hash; - hw << tx.vin[0].prevout.n; - uint256 utxo = hw.GetHash(); - - pair::iterator, multimap::iterator> range; - CStakeParams p, s; - - if (GetStakeParams(tx, p)) - { - LOCK(cs_cheat); - range = indexedCheatCandidates.equal_range(utxo); - - //printf("IsCheatInList - found candidates: %s\n", range.first == range.second ? "false" : "true"); - - for (auto it = range.first; it != range.second; it++) - { - CTransaction &cTx = it->second->tx; - //printf("cTx::opret : %s\n", cTx.vout[1].scriptPubKey.ToString().c_str()); - - // need both parameters to check - if (GetStakeParams(cTx, s)) - { - if (p.prevHash != s.prevHash && s.blkHeight >= p.blkHeight) - { - *cheatTx = cTx; - return true; - } - } - } - } - return false; -} - -bool CCheatList::IsUTXOInList(COutPoint _utxo, uint32_t height) -{ - // for a tx to be cheat, it needs to spend the same UTXO and be for a different prior block - // the list should be pruned before this call - // we return the first valid cheat we find - CVerusHashWriter hw = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION); - - hw << _utxo.hash; - hw << _utxo.n; - uint256 utxo = hw.GetHash(); - - pair::iterator, multimap::iterator> range; - CStakeParams p, s; - - LOCK(cs_cheat); - range = indexedCheatCandidates.equal_range(utxo); - - for (auto it = range.first; it != range.second; it++) - { - CTransaction &cTx = it->second->tx; - //printf("cTx::opret : %s\n", cTx.vout[1].scriptPubKey.ToString().c_str()); - - // need both parameters to check - if (GetStakeParams(cTx, s)) - { - if (s.blkHeight >= height) - { - return true; - } - } - } - return false; -} - -bool CCheatList::Add(const CTxHolder &txh) -{ - if (NetworkUpgradeActive(txh.height, Params().GetConsensus(), Consensus::UPGRADE_SAPLING)) - { - LOCK(cs_cheat); - auto it = orderedCheatCandidates.insert(pair(txh.height, txh)); - indexedCheatCandidates.insert(pair(txh.utxo, &it->second)); - //printf("CCheatList::Add orderedCheatCandidates.size: %d, indexedCheatCandidates.size: %d\n", (int)orderedCheatCandidates.size(), (int)indexedCheatCandidates.size()); - } -} - -void CCheatList::Remove(const CTxHolder &txh) -{ - // first narrow by source tx, then compare with tx hash - uint32_t count; - pair::iterator, multimap::iterator> range; - vector::iterator> utxoPrune; - vector::iterator> heightPrune; - - { - LOCK(cs_cheat); - range = indexedCheatCandidates.equal_range(txh.utxo); - for (auto it = range.first; it != range.second; it++) - { - uint256 hash = txh.tx.GetHash(); - if (hash == it->second->tx.GetHash()) - { - auto hrange = orderedCheatCandidates.equal_range(it->second->height); - for (auto hit = hrange.first; hit != hrange.second; hit++) - { - if (hit->second.tx.GetHash() == hash) - { - // add and remove them together - utxoPrune.push_back(it); - heightPrune.push_back(hit); - } - } - } - } - for (auto it : utxoPrune) - { - indexedCheatCandidates.erase(it); - } - for (auto it : heightPrune) - { - orderedCheatCandidates.erase(it); - } - } -} diff --git a/src/cheatcatcher.h b/src/cheatcatcher.h deleted file mode 100644 index 7ae6c4e54..000000000 --- a/src/cheatcatcher.h +++ /dev/null @@ -1,81 +0,0 @@ -/******************************************************************** - * (C) 2018 Michael Toutonghi - * - * Distributed under the MIT software license, see the accompanying - * file COPYING or http://www.opensource.org/licenses/mit-license.php. - * - * This supports code to catch nothing at stake cheaters who stake - * on multiple forks. - * - */ - -#ifndef CHEATCATCHER_H -#define CHEATCATCHER_H - -#include - -#include "streams.h" -#include "primitives/transaction.h" -#include "script/script.h" -#include "uint256.h" - -#include -#include - -class CTxHolder -{ - public: - uint256 utxo; - uint32_t height; - CTransaction tx; - CTxHolder(const CTransaction &_tx, uint32_t _height) : height(_height), tx(_tx) { - CVerusHashWriter hw = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION); - - hw << tx.vin[0].prevout.hash; - hw << tx.vin[0].prevout.n; - utxo = hw.GetHash(); - } - - CTxHolder& operator=(const CTxHolder& txh) - { - utxo = txh.utxo; - height = txh.height; - tx = txh.tx; - } -}; - - -class CCheatList -{ - private: - std::multimap orderedCheatCandidates; - std::multimap indexedCheatCandidates; - CCriticalSection cs_cheat; - - public: - CCheatList() {} - - // prune all transactions in the list below height - uint32_t Prune(uint32_t height); - - // check to see if a transaction that could be a cheat for the passed transaction is in our list - bool IsCheatInList(const CTransaction &tx, CTransaction *pcheatTx); - - // checks if the out point is in the list - bool IsUTXOInList(COutPoint _utxo, uint32_t height); - - // check to see if there are cheat candidates of the same or greater block height in list - bool IsHeightOrGreaterInList(uint32_t height); - - // add a potential cheat transaction to the list. we do this for all stake transactions from orphaned stakes - bool Add(const CTxHolder &txh); - - // remove a transaction from the the list - void Remove(const CTxHolder &txh); -}; - - -extern CCheatList cheatList; -extern boost::optional cheatCatcher; - -#endif \ No newline at end of file diff --git a/src/dpowassets b/src/dpowassets deleted file mode 100755 index 88e6f72b8..000000000 --- a/src/dpowassets +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash -set -x -source pubkey.txt -echo $pubkey -sleep 3 - -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"REVS\",\"pubkey\":\"$pubkey\"}" - -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"SUPERNET\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"DEX\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"PANGEA\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"JUMBLR\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"BET\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"CRYPTO\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"HODL\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"MSHARK\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"BOTS\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"MGW\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"COQUI\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"WLC\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"KV\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"CEAL\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"MESH\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"MNZ\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"AXO\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"ETOMIC\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"BTCH\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"CHAIN\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"NINJA\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"OOT\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"BNTN\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"PRLPAY\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"DSEC\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"GLXT\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"EQL\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"ZILLA\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"CHIPS\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"GAME\",\"freq\":5,\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"RFOX\",\"freq\":10,\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"VRSC\",\"freq\":10,\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"SEC\",\"pubkey\":\"$pubkey\"}" -curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"CCL\",\"pubkey\":\"$pubkey\"}" diff --git a/src/fiat/mgnx b/src/fiat/mgnx new file mode 100755 index 000000000..9bd85336e --- /dev/null +++ b/src/fiat/mgnx @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=MGNX $1 $2 $3 $4 $5 $6 diff --git a/src/fiat/pgt b/src/fiat/pgt new file mode 100755 index 000000000..8e124bbd1 --- /dev/null +++ b/src/fiat/pgt @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=PGT $1 $2 $3 $4 $5 $6 diff --git a/src/fiat/pirate b/src/fiat/pirate new file mode 100755 index 000000000..9314209da --- /dev/null +++ b/src/fiat/pirate @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=PIRATE $1 $2 $3 $4 $5 $6 diff --git a/src/init.cpp b/src/init.cpp index 019718e70..bf5b82216 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -76,7 +76,6 @@ using namespace std; extern void ThreadSendAlert(); extern int32_t KOMODO_LOADINGBLOCKS; extern bool VERUS_MINTBLOCKS; -extern std::string VERUS_CHEATCATCHER; ZCJoinSplit* pzcashParams = NULL; @@ -1869,8 +1868,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) StartNode(threadGroup, scheduler); - VERUS_CHEATCATCHER = GetArg("-cheatcatcher", ""); - #ifdef ENABLE_MINING // Generate coins in the background #ifdef ENABLE_WALLET diff --git a/src/komodo-tx.cpp b/src/komodo-tx.cpp index 87cbf714f..9683c2988 100644 --- a/src/komodo-tx.cpp +++ b/src/komodo-tx.cpp @@ -40,7 +40,6 @@ uint64_t komodo_accrued_interest(int32_t *txheightp,uint32_t *locktimep,uint256 static bool fCreateBlank; static std::map registers; static const int CONTINUE_EXECUTION=-1; -boost::optional cheatCatcher; // // This function returns either one of EXIT_ codes when it's expected to stop the process or diff --git a/src/komodo.h b/src/komodo.h index 6d67e1b33..b0a122fdd 100644 --- a/src/komodo.h +++ b/src/komodo.h @@ -112,7 +112,7 @@ int32_t komodo_parsestatefile(struct komodo_state *sp,FILE *fp,char *symbol,char errs++; if ( fread(&MoMdepth,1,sizeof(MoMdepth),fp) != sizeof(MoMdepth) ) errs++; - if ( 1 && ASSETCHAINS_SYMBOL[0] != 0 && sp != 0 ) + if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 && sp != 0 ) printf("%s load[%s.%d -> %s] NOTARIZED %d %s MoM.%s %d CCid.%u\n",ASSETCHAINS_SYMBOL,symbol,sp->NUM_NPOINTS,dest,notarized_height,notarized_hash.ToString().c_str(),MoM.ToString().c_str(),MoMdepth&0xffff,(MoMdepth>>16)&0xffff); } else @@ -257,7 +257,7 @@ int32_t komodo_parsestatefiledata(struct komodo_state *sp,uint8_t *filedata,long errs++; if ( memread(&MoMdepth,sizeof(MoMdepth),filedata,&fpos,datalen) != sizeof(MoMdepth) ) errs++; - if ( 1 && ASSETCHAINS_SYMBOL[0] != 0 && sp != 0 ) + if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 && sp != 0 ) printf("%s load[%s.%d -> %s] NOTARIZED %d %s MoM.%s %d CCid.%u\n",ASSETCHAINS_SYMBOL,symbol,sp->NUM_NPOINTS,dest,notarized_height,notarized_hash.ToString().c_str(),MoM.ToString().c_str(),MoMdepth&0xffff,(MoMdepth>>16)&0xffff); } else @@ -851,7 +851,7 @@ void komodo_connectblock(CBlockIndex *pindex,CBlock& block) printf("%02x",scriptPubKey[k]); printf(" scriptPubKey doesnt match any notary vini.%d of %d\n",j,numvins); } - } else printf("cant get scriptPubKey for ht.%d txi.%d vin.%d\n",height,i,j); + } //else printf("cant get scriptPubKey for ht.%d txi.%d vin.%d\n",height,i,j); } numvalid = bitweight(signedmask); if ( (((height < 90000 || (signedmask & 1) != 0) && numvalid >= KOMODO_MINRATIFY) || diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index b9e9efdf4..0c69621ac 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -389,7 +389,7 @@ int32_t notarizedtxid_height(char *dest,char *txidstr,int32_t *kmdnotarized_heig { if ( (item= jobj(json,(char *)"result")) != 0 ) { - txid_confirmations = jint(item,(char *)"confirmations"); + txid_confirmations = jint(item,(char *)"rawconfirmations"); if ( txid_confirmations > 0 && height > txid_confirmations ) txid_height = height - txid_confirmations; else txid_height = height; @@ -1099,18 +1099,33 @@ int32_t komodo_validate_interest(const CTransaction &tx,int32_t txheight,uint32_ PoS stake must be without txfee and in the last tx in the block at vout[0] */ -uint64_t komodo_commission(const CBlock *pblock) +CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams); + +uint64_t komodo_commission(const CBlock *pblock,int32_t height) { - int32_t i,j,n=0,txn_count; uint64_t commission,total = 0; + int32_t i,j,n=0,txn_count; int64_t nSubsidy; uint64_t commission,total = 0; txn_count = pblock->vtx.size(); - for (i=0; ivtx[i].vout.size(); + nSubsidy = GetBlockSubsidy(height,Params().GetConsensus()); + fprintf(stderr,"ht.%d nSubsidy %.8f prod %llu\n",height,(double)nSubsidy/COIN,(long long)(nSubsidy * ASSETCHAINS_COMMISSION)); + return((nSubsidy * ASSETCHAINS_COMMISSION) / COIN); + n = pblock->vtx[0].vout.size(); for (j=0; jvtx[0].vout[j].nValue; + } + else + { + for (i=0; ivtx[i].vout[j].nValue; + n = pblock->vtx[i].vout.size(); + for (j=0; jvtx[i].vout[j].nValue; + } } } //fprintf(stderr,"txn.%d n.%d commission total %.8f -> %.8f\n",txn_count,n,dstr(total),dstr((total * ASSETCHAINS_COMMISSION) / COIN)); @@ -1157,7 +1172,7 @@ int8_t komodo_segid(int32_t nocache,int32_t height) return(segid); } -int32_t komodo_segids(uint8_t *hashbuf,int32_t height,int32_t n) +void komodo_segids(uint8_t *hashbuf,int32_t height,int32_t n) { static uint8_t prevhashbuf[100]; static int32_t prevheight; int32_t i; @@ -1277,7 +1292,7 @@ uint32_t komodo_stake(int32_t validateflag,arith_uint256 bnTarget,int32_t nHeigh arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc) { - int32_t oldflag = 0; + 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) ) @@ -1296,23 +1311,23 @@ arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t he { n++; percPoS++; - if ( ASSETCHAINS_STAKED < 100 ) + if ( dispflag != 0 && ASSETCHAINS_STAKED < 100 ) fprintf(stderr,"0"); } else { - if ( ASSETCHAINS_STAKED < 100 ) + if ( dispflag != 0 && ASSETCHAINS_STAKED < 100 ) fprintf(stderr,"1"); sum += UintToArith256(pindex->GetBlockHash()); m++; } } - if ( ASSETCHAINS_STAKED < 100 && (i % 10) == 9 ) + if ( dispflag != 0 && ASSETCHAINS_STAKED < 100 && (i % 10) == 9 ) fprintf(stderr," %d, ",percPoS); } if ( m+n < 100 ) percPoS = ((percPoS * n) + (goalperc * (100-n))) / 100; - if ( ASSETCHAINS_STAKED < 100 ) + if ( dispflag != 0 && ASSETCHAINS_STAKED < 100 ) fprintf(stderr," -> %d%% percPoS vs goalperc.%d ht.%d\n",percPoS,goalperc,height); *percPoSp = percPoS; if ( m > 0 ) @@ -1325,12 +1340,10 @@ arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t he percPoS = 1; if ( percPoS < goalperc ) // increase PoW diff -> lower bnTarget { - //if ( oldflag != 0 ) - // bnTarget = (ave * arith_uint256(percPoS * percPoS)) / arith_uint256(goalperc * goalperc * goalperc); if ( oldflag != 0 ) bnTarget = (ave / arith_uint256(goalperc * goalperc * goalperc)) * arith_uint256(percPoS * percPoS); else bnTarget = (ave / arith_uint256(goalperc * goalperc * goalperc * goalperc)) * arith_uint256(percPoS * percPoS); - if ( ASSETCHAINS_STAKED < 100 ) + if ( dispflag != 0 && ASSETCHAINS_STAKED < 100 ) { for (i=31; i>=24; i--) fprintf(stderr,"%02x",((uint8_t *)&ave)[i]); @@ -1348,7 +1361,6 @@ arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t he if ( oldflag != 0 ) { bnTarget = ((ave * arith_uint256(goalperc)) + (easydiff * arith_uint256(percPoS))) / arith_uint256(percPoS + goalperc); - //bnTarget = (bnTarget * arith_uint256(percPoS * percPoS * percPoS)) / arith_uint256(goalperc * goalperc); bnTarget = (bnTarget / arith_uint256(goalperc * goalperc)) * arith_uint256(percPoS * percPoS * percPoS); } else bnTarget = (ave / arith_uint256(goalperc * goalperc)) * arith_uint256(percPoS * percPoS * percPoS); @@ -1360,7 +1372,7 @@ arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t he if ( bnTarget < ave ) bnTarget = ave; } - if ( 1 ) + if ( dispflag != 0 ) { for (i=31; i>=24; i--) fprintf(stderr,"%02x",((uint8_t *)&ave)[i]); @@ -1446,7 +1458,7 @@ int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_ bnTarget = komodo_PoWtarget(&PoSperc,bnTarget,height,ASSETCHAINS_STAKED); if ( bhash < bnTarget ) { - fprintf(stderr,"ht.%d isPoS but meets PoW diff!\n",height); + //fprintf(stderr,"ht.%d isPoS but meets PoW diff!\n",height); isPoS = 0; } } @@ -1459,7 +1471,6 @@ int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_ bool GetStakeParams(const CTransaction &stakeTx, CStakeParams &stakeParams); bool ValidateMatchingStake(const CTransaction &ccTx, uint32_t voutNum, const CTransaction &stakeTx, bool &cheating); -bool ValidateStakeTransaction(const CTransaction &stakeTx, CStakeParams &stakeParams, bool validateSig = true); // for now, we will ignore slowFlag in the interest of keeping success/fail simpler for security purposes bool verusCheckPOSBlock(int32_t slowflag, CBlock *pblock, int32_t height) @@ -1496,11 +1507,10 @@ bool verusCheckPOSBlock(int32_t slowflag, CBlock *pblock, int32_t height) { bool validHash = (value != 0); bool enablePOSNonce = CPOSNonce::NewPOSActive(height); - bool newPOSEnforcement = enablePOSNonce && (Params().GetConsensus().vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight <= height); uint256 rawHash; arith_uint256 posHash; - if (validHash && newPOSEnforcement) + if (validHash && enablePOSNonce) { validHash = pblock->GetRawVerusPOSHash(rawHash, height); posHash = UintToArith256(rawHash) / value; @@ -1510,29 +1520,6 @@ bool verusCheckPOSBlock(int32_t slowflag, CBlock *pblock, int32_t height) printf("ERROR: invalid nonce value for PoS block\nnNonce: %s\nrawHash: %s\nposHash: %s\nvalue: %lu\n", pblock->nNonce.GetHex().c_str(), rawHash.GetHex().c_str(), posHash.GetHex().c_str(), value); } - // make sure prev block hash and block height are correct - CStakeParams p; - if (validHash && (validHash = GetStakeParams(pblock->vtx[txn_count-1], p))) - { - for (int i = 0; validHash && i < pblock->vtx[0].vout.size(); i++) - { - validHash = false; - if (ValidateMatchingStake(pblock->vtx[0], i, pblock->vtx[txn_count-1], validHash) && !validHash) - { - if ((p.prevHash == pblock->hashPrevBlock) && (int32_t)p.blkHeight == height) - { - validHash = true; - } - else - { - printf("ERROR: invalid block data for stake tx\nblkHash: %s\ntxBlkHash: %s\nblkHeight: %d, txBlkHeight: %d\n", - pblock->hashPrevBlock.GetHex().c_str(), p.prevHash.GetHex().c_str(), height, p.blkHeight); - validHash = false; - } - } - else validHash = false; - } - } } if (validHash) { @@ -1573,7 +1560,7 @@ bool verusCheckPOSBlock(int32_t slowflag, CBlock *pblock, int32_t height) //printf("after nNonce: %s, height: %d\n", nonce.GetHex().c_str(), height); //printf("POShash: %s\n\n", hash.GetHex().c_str()); - if ((!newPOSEnforcement || posHash == hash) && hash <= target) + if (posHash == hash && hash <= target) { BlockMap::const_iterator it = mapBlockIndex.find(blkHash); if ((it == mapBlockIndex.end()) || @@ -1616,48 +1603,30 @@ bool verusCheckPOSBlock(int32_t slowflag, CBlock *pblock, int32_t height) } } if ( nonceOK && ExtractDestination(pblock->vtx[txn_count-1].vout[0].scriptPubKey, voutaddress) && - ExtractDestination(tx.vout[voutNum].scriptPubKey, destaddress) && - CScriptExt::ExtractVoutDestination(pblock->vtx[0], 0, cbaddress) ) + ExtractDestination(tx.vout[voutNum].scriptPubKey, destaddress) ) { strcpy(voutaddr, CBitcoinAddress(voutaddress).ToString().c_str()); strcpy(destaddr, CBitcoinAddress(destaddress).ToString().c_str()); - strcpy(cbaddr, CBitcoinAddress(cbaddress).ToString().c_str()); - if (newPOSEnforcement) - { - if (!strcmp(destaddr, voutaddr)) - { - // allow delegation of stake, but require all ouputs to be - // crypto conditions - CStakeParams p; - - // validatestake transaction sets the pubkey of the stake output - // if it has no override into the pubkey - if (ValidateStakeTransaction(pblock->vtx[txn_count-1], p, false)) - { - COptCCParams cpp; - // loop through all outputs to make sure they are sent to the proper pubkey - isPOS = true; - for (auto vout : pblock->vtx[0].vout) - { - txnouttype tp; - std::vector> vvch = std::vector>(); - // solve all outputs to check that destinations all go only to the pk - // specified in the stake params - if (!Solver(vout.scriptPubKey, tp, vvch) || - tp != TX_CRYPTOCONDITION || - vvch.size() < 2 || - p.pk != CPubKey(vvch[1])) - { - isPOS = false; - break; - } - } - } - } - } - else if ( !strcmp(destaddr,voutaddr) && ( !strcmp(destaddr,cbaddr) || (height < 17840)) ) + if ( !strcmp(destaddr,voutaddr) ) { isPOS = true; + CTransaction &cbtx = pblock->vtx[0]; + for (int i = 0; i < cbtx.vout.size(); i++) + { + if (CScriptExt::ExtractVoutDestination(cbtx, i, cbaddress)) + { + strcpy(cbaddr, CBitcoinAddress(cbaddress).ToString().c_str()); + if (!strcmp(destaddr,cbaddr)) + continue; + } + else + { + if (cbtx.vout[i].scriptPubKey.IsOpReturn()) + continue; + isPOS = false; + break; + } + } } else { @@ -1681,17 +1650,39 @@ 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; + int64_t checktoshis=0; uint8_t *script,scripthex[8192]; int32_t scriptlen,matched = 0; if ( ASSETCHAINS_COMMISSION != 0 ) { - checktoshis = komodo_commission(pblock); - if ( checktoshis > 10000 && pblock->vtx[0].vout.size() != 2 ) + checktoshis = komodo_commission(pblock,height); + fprintf(stderr,"height.%d commission %.8f\n",height,(double)checktoshis/COIN); + /*if ( checktoshis > 10000 && pblock->vtx[0].vout.size() != 2 ) jl777: not sure why this was here return(-1); - else if ( checktoshis != 0 ) + else*/ if ( checktoshis != 0 ) { script = (uint8_t *)&pblock->vtx[0].vout[1].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[1].scriptPubKey.size(); + if ( ASSETCHAINS_SCRIPTPUB.size() > 1 ) + { + if ( ASSETCHAINS_SCRIPTPUB.size()/2 == scriptlen && scriptlen < sizeof(scripthex) ) + { + decode_hex(scripthex,scriptlen,(char *)ASSETCHAINS_SCRIPTPUB.c_str()); + if ( memcmp(scripthex,script,scriptlen) == 0 ) + matched = scriptlen; + } + } + else if ( scriptlen == 35 && script[0] == 33 && script[34] == OP_CHECKSIG && memcmp(script+1,ASSETCHAINS_OVERRIDE_PUBKEY33,33) == 0 ) + matched = 35; + else if ( scriptlen == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 && script[2] == 20 && script[23] == OP_EQUALVERIFY && script[24] == OP_CHECKSIG && memcmp(script+3,ASSETCHAINS_OVERRIDE_PUBKEYHASH,20) == 0 ) + 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); return(-1); + + } if ( pblock->vtx[0].vout[1].nValue != checktoshis ) { fprintf(stderr,"ht.%d checktoshis %.8f vs actual vout[1] %.8f\n",height,dstr(checktoshis),dstr(pblock->vtx[0].vout[1].nValue)); diff --git a/src/komodo_defs.h b/src/komodo_defs.h index 3929bdc4b..71fc9f815 100644 --- a/src/komodo_defs.h +++ b/src/komodo_defs.h @@ -10,5 +10,8 @@ #define IGUANA_MAXSCRIPTSIZE 10001 #define KOMODO_MAXMEMPOOLTIME 3600 // affects consensus #define CRYPTO777_PUBSECPSTR "020e46e79a2a8d12b9b5d12c7a91adb4e454edfae43c0a0cb805427d2ac7613fd9" +#define KOMODO_FIRSTFUNGIBLEID 100 + +extern uint8_t ASSETCHAINS_TXPOW; #endif diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 7e85d80fa..28c7855b6 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1382,8 +1382,9 @@ void komodo_passport_iteration() } if ( komodo_chainactive_timestamp() > lastinterest ) { - komodo_interestsum(); - komodo_longestchain(); + if ( ASSETCHAINS_SYMBOL[0] == 0 ) + komodo_interestsum(); + //komodo_longestchain(); lastinterest = komodo_chainactive_timestamp(); } refsp = komodo_stateptr(symbol,dest); diff --git a/src/komodo_globals.h b/src/komodo_globals.h index ddea32be7..31bbb8a54 100644 --- a/src/komodo_globals.h +++ b/src/komodo_globals.h @@ -29,6 +29,7 @@ uint64_t komodo_paxtotal(); int32_t komodo_longestchain(); uint64_t komodo_maxallowed(int32_t baseid); int32_t komodo_bannedset(int32_t *indallvoutsp,uint256 *array,int32_t max); +bool pubkey2addr(char *destaddr,uint8_t *pubkey33); pthread_mutex_t komodo_mutex; @@ -46,13 +47,13 @@ int COINBASE_MATURITY = _COINBASE_MATURITY;//100; int32_t KOMODO_MININGTHREADS = 0,IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,ASSETCHAINS_SEED,KOMODO_ON_DEMAND,KOMODO_EXTERNAL_NOTARIES,KOMODO_PASSPORT_INITDONE,KOMODO_PAX,KOMODO_EXCHANGEWALLET,KOMODO_REWIND,KOMODO_CONNECTING = -1; int32_t KOMODO_INSYNC,KOMODO_LASTMINED,prevKOMODO_LASTMINED,KOMODO_CCACTIVATE,JUMBLR_PAUSE = 1; -std::string VERUS_CHEATCATCHER, NOTARY_PUBKEY,ASSETCHAINS_NOTARIES,ASSETCHAINS_OVERRIDE_PUBKEY,DONATION_PUBKEY; -uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33],ASSETCHAINS_PUBLIC,ASSETCHAINS_PRIVATE; +std::string NOTARY_PUBKEY,ASSETCHAINS_NOTARIES,ASSETCHAINS_OVERRIDE_PUBKEY,DONATION_PUBKEY,ASSETCHAINS_SCRIPTPUB; +uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEYHASH[20],ASSETCHAINS_PUBLIC,ASSETCHAINS_PRIVATE,ASSETCHAINS_TXPOW,ASSETCHAINS_FOUNDERS; bool VERUS_MINTBLOCKS; char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN],ASSETCHAINS_USERPASS[4096]; uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT; -uint32_t ASSETCHAIN_INIT,ASSETCHAINS_CC,KOMODO_STOPAT; +uint32_t ASSETCHAIN_INIT,ASSETCHAINS_CC,KOMODO_STOPAT,KOMODO_DPOWCONFS = 1; uint32_t ASSETCHAINS_MAGIC = 2387029918; int64_t ASSETCHAINS_GENESISTXVAL = 5000000000; @@ -94,7 +95,7 @@ uint64_t KOMODO_INTERESTSUM,KOMODO_WALLETBALANCE; uint64_t ASSETCHAINS_COMMISSION,ASSETCHAINS_STAKED,ASSETCHAINS_SUPPLY = 10; uint32_t KOMODO_INITDONE; -char KMDUSERPASS[8192],BTCUSERPASS[8192]; uint16_t KMD_PORT = 7771,BITCOIND_RPCPORT = 7771; +char KMDUSERPASS[8192+512+1],BTCUSERPASS[8192]; uint16_t KMD_PORT = 7771,BITCOIND_RPCPORT = 7771; uint64_t PENDING_KOMODO_TX; extern int32_t KOMODO_LOADINGBLOCKS; unsigned int MAX_BLOCK_SIGOPS = 20000; diff --git a/src/komodo_kv.h b/src/komodo_kv.h index 37e11d06d..834584e44 100644 --- a/src/komodo_kv.h +++ b/src/komodo_kv.h @@ -117,7 +117,7 @@ void komodo_kvupdate(uint8_t *opretbuf,int32_t opretlen,uint64_t value) } valueptr = &key[keylen]; fee = komodo_kvfee(flags,opretlen,keylen); - //printf("fee %.8f vs %.8f flags.%d keylen.%d valuesize.%d height.%d (%02x %02x %02x) (%02x %02x %02x)\n",(double)fee/COIN,(double)value/COIN,flags,keylen,valuesize,height,key[0],key[1],key[2],valueptr[0],valueptr[1],valueptr[2]); + //fprintf(stderr,"fee %.8f vs %.8f flags.%d keylen.%d valuesize.%d height.%d (%02x %02x %02x) (%02x %02x %02x)\n",(double)fee/COIN,(double)value/COIN,flags,keylen,valuesize,height,key[0],key[1],key[2],valueptr[0],valueptr[1],valueptr[2]); if ( value >= fee ) { coresize = (int32_t)(sizeof(flags)+sizeof(height)+sizeof(keylen)+sizeof(valuesize)+keylen+valuesize+1); diff --git a/src/komodo_notary.h b/src/komodo_notary.h index 4711a9afc..8c51c7924 100644 --- a/src/komodo_notary.h +++ b/src/komodo_notary.h @@ -197,6 +197,7 @@ const char *Notaries_elected1[][2] = {"webworker01_NA", "03bb7d005e052779b1586f071834c5facbb83470094cff5112f0072b64989f97d7" }, {"xrobesx_NA", "03f0cc6d142d14a40937f12dbd99dbd9021328f45759e26f1877f2a838876709e1" }, }; +#define CRYPTO777_PUBSECPSTR "020e46e79a2a8d12b9b5d12c7a91adb4e454edfae43c0a0cb805427d2ac7613fd9" int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp) { @@ -451,6 +452,21 @@ int32_t komodo_notarized_height(int32_t *prevMoMheightp,uint256 *hashp,uint256 * } } +int32_t komodo_dpowconfs(int32_t txheight,int32_t numconfs) +{ + char symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; struct komodo_state *sp; + if ( KOMODO_DPOWCONFS != 0 && txheight > 0 && numconfs > 0 && (sp= komodo_stateptr(symbol,dest)) != 0 ) + { + if ( sp->NOTARIZED_HEIGHT > 0 ) + { + if ( txheight < sp->NOTARIZED_HEIGHT ) + return(numconfs); + else return(1); + } + } + return(numconfs); +} + int32_t komodo_MoMdata(int32_t *notarized_htp,uint256 *MoMp,uint256 *kmdtxidp,int32_t height,uint256 *MoMoMp,int32_t *MoMoMoffsetp,int32_t *MoMoMdepthp,int32_t *kmdstartip,int32_t *kmdendip) { struct notarized_checkpoint *np = 0; diff --git a/src/komodo_utils.h b/src/komodo_utils.h index 9cb6cd764..8a8e4fe58 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -1645,12 +1645,11 @@ uint64_t komodo_ac_block_subsidy(int nHeight) } extern int64_t MAX_MONEY; -extern std::string VERUS_CHEATCATCHER; 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[256],*extraptr=0; FILE *fp; uint64_t val; uint16_t port; int32_t i,baseid,len,n,extralen = 0; + 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); if ( GetBoolArg("-gen", false) != 0 )\ @@ -1762,9 +1761,12 @@ void komodo_args(char *argv0) } MAX_BLOCK_SIGOPS = 60000; + ASSETCHAINS_TXPOW = GetArg("-ac_txpow",0) & 3; + ASSETCHAINS_FOUNDERS = GetArg("-ac_founders",0) & 1; ASSETCHAINS_SUPPLY = GetArg("-ac_supply",10); ASSETCHAINS_COMMISSION = GetArg("-ac_perc",0); ASSETCHAINS_OVERRIDE_PUBKEY = GetArg("-ac_pubkey",""); + ASSETCHAINS_SCRIPTPUB = GetArg("-ac_script",""); if ( (ASSETCHAINS_STAKED= GetArg("-ac_staked",0)) > 100 ) ASSETCHAINS_STAKED = 100; @@ -1782,16 +1784,33 @@ void komodo_args(char *argv0) { ASSETCHAINS_OVERWINTER = GetArg("-ac_overwinter", ASSETCHAINS_SAPLING); } - - if ( strlen(ASSETCHAINS_OVERRIDE_PUBKEY.c_str()) == 66 && ASSETCHAINS_COMMISSION > 0 && ASSETCHAINS_COMMISSION <= 100000000 ) - decode_hex(ASSETCHAINS_OVERRIDE_PUBKEY33,33,(char *)ASSETCHAINS_OVERRIDE_PUBKEY.c_str()); - else if ( ASSETCHAINS_COMMISSION != 0 ) + if ( strlen(ASSETCHAINS_OVERRIDE_PUBKEY.c_str()) == 66 || ASSETCHAINS_SCRIPTPUB.size() > 1 ) { - ASSETCHAINS_COMMISSION = 0; - printf("ASSETCHAINS_COMMISSION needs an ASSETCHAINS_OVERRIDE_PUBKEY and cant be more than 100000000 (100%%)\n"); + if ( strlen(ASSETCHAINS_OVERRIDE_PUBKEY.c_str()) == 66 ) + { + 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 && ASSETCHAINS_FOUNDERS != 0 ) + { + ASSETCHAINS_COMMISSION = 53846154; // maps to 35% + printf("ASSETCHAINS_COMMISSION defaulted to 35%% when founders reward active\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 ) + else + { + if ( ASSETCHAINS_COMMISSION != 0 ) + { + ASSETCHAINS_COMMISSION = 0; + printf("ASSETCHAINS_COMMISSION needs an ASETCHAINS_OVERRIDE_PUBKEY and cant be more than 100000000 (100%%)\n"); + } + if ( ASSETCHAINS_FOUNDERS != 0 ) + { + ASSETCHAINS_FOUNDERS = 0; + printf("ASSETCHAINS_FOUNDERS needs an ASETCHAINS_OVERRIDE_PUBKEY\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 ) { printf("perc.%llu\n",(long long)ASSETCHAINS_COMMISSION); @@ -1838,17 +1857,34 @@ 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); + 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; extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(val),(void *)&val); + if ( ASSETCHAINS_FOUNDERS != 0 ) + extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_FOUNDERS),(void *)&ASSETCHAINS_FOUNDERS); + if ( ASSETCHAINS_SCRIPTPUB.size() > 1 ) + extralen += iguana_rwnum(1,&extraptr[extralen],(int32_t)ASSETCHAINS_SCRIPTPUB.size(),(void *)ASSETCHAINS_SCRIPTPUB.c_str()); } addn = GetArg("-seednode",""); if ( strlen(addn.c_str()) > 0 ) ASSETCHAINS_SEED = 1; + strncpy(ASSETCHAINS_SYMBOL,name.c_str(),sizeof(ASSETCHAINS_SYMBOL)-1); MAX_MONEY = komodo_max_money(); + if ( (baseid = komodo_baseid(ASSETCHAINS_SYMBOL)) >= 0 && baseid < 32 ) + { + //komodo_maxallowed(baseid); + printf("baseid.%d MAX_MONEY.%s %.8f\n",baseid,ASSETCHAINS_SYMBOL,(double)MAX_MONEY/SATOSHIDEN); + } + + if ( ASSETCHAINS_CC >= KOMODO_FIRSTFUNGIBLEID && MAX_MONEY < 1000000LL*SATOSHIDEN ) + MAX_MONEY = 1000000LL*SATOSHIDEN; + if ( MAX_MONEY <= 0 || MAX_MONEY > 1000000000LL*SATOSHIDEN ) + MAX_MONEY = 1000000000LL*SATOSHIDEN; + + //fprintf(stderr,"MAX_MONEY %llu %.8f\n",(long long)MAX_MONEY,(double)MAX_MONEY/SATOSHIDEN); //printf("baseid.%d MAX_MONEY.%s %.8f\n",baseid,ASSETCHAINS_SYMBOL,(double)MAX_MONEY/SATOSHIDEN); ASSETCHAINS_P2PPORT = komodo_port(ASSETCHAINS_SYMBOL,ASSETCHAINS_SUPPLY,&ASSETCHAINS_MAGIC,extraptr,extralen); while ( (dirname= (char *)GetDataDir(false).string().c_str()) == 0 || dirname[0] == 0 ) @@ -1865,6 +1901,11 @@ void komodo_args(char *argv0) { int32_t komodo_baseid(char *origbase); extern int COINBASE_MATURITY; + if ( strcmp(ASSETCHAINS_SYMBOL,"KMD") == 0 ) + { + fprintf(stderr,"cant have assetchain named KMD\n"); + exit(0); + } if ( (port= komodo_userpass(ASSETCHAINS_USERPASS,ASSETCHAINS_SYMBOL)) != 0 ) ASSETCHAINS_RPCPORT = port; else komodo_configfile(ASSETCHAINS_SYMBOL,ASSETCHAINS_P2PPORT + 1); @@ -1933,11 +1974,20 @@ void komodo_args(char *argv0) break; } } + int32_t dpowconfs = KOMODO_DPOWCONFS; if ( ASSETCHAINS_SYMBOL[0] != 0 ) { BITCOIND_RPCPORT = GetArg("-rpcport", ASSETCHAINS_RPCPORT); //fprintf(stderr,"(%s) port.%u chain params initialized\n",ASSETCHAINS_SYMBOL,BITCOIND_RPCPORT); + if ( strcmp("PIRATE",ASSETCHAINS_SYMBOL) == 0 && ASSETCHAINS_HALVING[0] == 77777 ) + { + ASSETCHAINS_HALVING[0] *= 5; + fprintf(stderr,"PIRATE halving changed to %d %.1f days\n",(int32_t)ASSETCHAINS_HALVING[0],(double)ASSETCHAINS_HALVING[0]/1440); + } + else if ( strcmp("VRSC",ASSETCHAINS_SYMBOL) == 0 ) + dpowconfs = 0; } else BITCOIND_RPCPORT = GetArg("-rpcport", BaseParams().RPCPort()); + KOMODO_DPOWCONFS = GetArg("-dpowconfs",dpowconfs); } void komodo_nameset(char *symbol,char *dest,char *source) diff --git a/src/main.cpp b/src/main.cpp index 35a5e0197..b34234063 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -67,6 +67,8 @@ extern int32_t KOMODO_LOADINGBLOCKS,KOMODO_LONGESTCHAIN,KOMODO_INSYNC,KOMODO_CON int32_t KOMODO_NEWBLOCKS; int32_t komodo_block2pubkey33(uint8_t *pubkey33,CBlock *block); void komodo_broadcast(CBlock *pblock,int32_t limit); +void komodo_broadcast(CBlock *pblock,int32_t limit); +bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey); BlockMap mapBlockIndex; CChain chainActive; @@ -101,6 +103,7 @@ unsigned int expiryDelta = DEFAULT_TX_EXPIRY_DELTA; CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); CTxMemPool mempool(::minRelayTxFee); +CTxMemPool tmpmempool(::minRelayTxFee); struct COrphanTx { CTransaction tx; @@ -124,7 +127,7 @@ const string strMessageMagic = "Komodo Signed Message:\n"; // Internal stuff namespace { - + struct CBlockIndexWorkComparator { bool operator()(CBlockIndex *pa, CBlockIndex *pb) const { @@ -135,19 +138,19 @@ namespace { // ... then by earliest time received, ... if (pa->nSequenceId < pb->nSequenceId) return false; if (pa->nSequenceId > pb->nSequenceId) return true; - + // Use pointer address as tie breaker (should only happen with blocks // loaded from disk, as those all have id 0). if (pa < pb) return false; if (pa > pb) return true; - + // Identical blocks. return false; } }; - + CBlockIndex *pindexBestInvalid; - + /** * The set of all CBlockIndex entries with BLOCK_VALID_TRANSACTIONS (for itself and all ancestors) and * as good as our current tip or better. Entries may be failed, though, and pruning nodes may be @@ -162,7 +165,7 @@ namespace { * Pruned nodes may have entries where B is missing data. */ multimap mapBlocksUnlinked; - + CCriticalSection cs_LastBlockFile; std::vector vinfoBlockFile; int nLastBlockFile = 0; @@ -171,7 +174,7 @@ namespace { * or if we allocate more file space when we're in prune mode */ bool fCheckForPruning = false; - + /** * Every received block is assigned a unique and increasing identifier, so we * know which one to give priority in case of a fork. @@ -179,14 +182,14 @@ namespace { CCriticalSection cs_nBlockSequenceId; /** Blocks loaded from disk are assigned id 0, so start the counter at 1. */ uint32_t nBlockSequenceId = 1; - + /** * Sources of received blocks, saved to be able to send them reject * messages or ban them when processing happens afterwards. Protected by * cs_main. */ map mapBlockSource; - + /** * Filter for transactions that were recently rejected by * AcceptToMemoryPool. These are not rerequested until the chain tip @@ -209,7 +212,7 @@ namespace { */ boost::scoped_ptr recentRejects; uint256 hashRecentRejectsChainTip; - + /** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */ struct QueuedBlock { uint256 hash; @@ -219,16 +222,16 @@ namespace { int64_t nTimeDisconnect; //! The timeout for this block request (for disconnecting a slow peer) }; map::iterator> > mapBlocksInFlight; - + /** Number of blocks in flight with validated headers. */ int nQueuedValidatedHeaders = 0; - + /** Number of preferable block download peers. */ int nPreferredDownload = 0; - + /** Dirty block index entries. */ set setDirtyBlockIndex; - + /** Dirty block file entries. */ set setDirtyFileInfo; } // anon namespace @@ -239,13 +242,13 @@ namespace { // namespace { - + struct CBlockReject { unsigned char chRejectCode; string strRejectReason; uint256 hashBlock; }; - + /** * Maintain validation-specific state about nodes, protected by cs_main, instead * by CNode's own locks. This simplifies asynchronous operation, where @@ -280,7 +283,7 @@ namespace { int nBlocksInFlightValidHeaders; //! Whether we consider this a preferred download peer. bool fPreferredDownload; - + CNodeState() { fCurrentlyConnected = false; nMisbehavior = 0; @@ -295,10 +298,10 @@ namespace { fPreferredDownload = false; } }; - + /** Map maintaining per-node state. Requires cs_main. */ map mapNodeState; - + // Requires cs_main. CNodeState *State(NodeId pnode) { map::iterator it = mapNodeState.find(pnode); @@ -306,66 +309,66 @@ namespace { return NULL; return &it->second; } - + int GetHeight() { return chainActive.LastTip()->GetHeight(); } - + void UpdatePreferredDownload(CNode* node, CNodeState* state) { nPreferredDownload -= state->fPreferredDownload; - + // Whether this node should be marked as a preferred download node. state->fPreferredDownload = (!node->fInbound || node->fWhitelisted) && !node->fOneShot && !node->fClient; - + nPreferredDownload += state->fPreferredDownload; } - + // Returns time at which to timeout block request (nTime in microseconds) int64_t GetBlockTimeout(int64_t nTime, int nValidatedQueuedBefore, const Consensus::Params &consensusParams) { return nTime + 500000 * consensusParams.nPowTargetSpacing * (4 + nValidatedQueuedBefore); } - + void InitializeNode(NodeId nodeid, const CNode *pnode) { LOCK(cs_main); CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second; state.name = pnode->addrName; state.address = pnode->addr; } - + void FinalizeNode(NodeId nodeid) { LOCK(cs_main); CNodeState *state = State(nodeid); - + if (state->fSyncStarted) nSyncStarted--; - + if (state->nMisbehavior == 0 && state->fCurrentlyConnected) { AddressCurrentlyConnected(state->address); } - + BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) mapBlocksInFlight.erase(entry.hash); EraseOrphansFor(nodeid); nPreferredDownload -= state->fPreferredDownload; - + mapNodeState.erase(nodeid); } - + void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) { /* int expired = pool.Expire(GetTime() - age); if (expired != 0) LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired); - + std::vector vNoSpendsRemaining; pool.TrimToSize(limit, &vNoSpendsRemaining); BOOST_FOREACH(const uint256& removed, vNoSpendsRemaining) pcoinsTip->Uncache(removed);*/ } - + // Requires cs_main. // Returns a bool indicating whether we requested this block. bool MarkBlockAsReceived(const uint256& hash) { @@ -382,15 +385,15 @@ namespace { } return false; } - + // Requires cs_main. void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL) { CNodeState *state = State(nodeid); assert(state != NULL); - + // Make sure it's not listed somewhere already. MarkBlockAsReceived(hash); - + int64_t nNow = GetTimeMicros(); QueuedBlock newentry = {hash, pindex, nNow, pindex != NULL, GetBlockTimeout(nNow, nQueuedValidatedHeaders, consensusParams)}; nQueuedValidatedHeaders += newentry.fValidatedHeaders; @@ -399,12 +402,12 @@ namespace { state->nBlocksInFlightValidHeaders += newentry.fValidatedHeaders; mapBlocksInFlight[hash] = std::make_pair(nodeid, it); } - + /** Check whether the last unknown block a peer advertized is not yet known. */ void ProcessBlockAvailability(NodeId nodeid) { CNodeState *state = State(nodeid); assert(state != NULL); - + if (!state->hashLastUnknownBlock.IsNull()) { BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock); if (itOld != mapBlockIndex.end() && itOld->second != 0 && (itOld->second->chainPower > CChainPower())) @@ -415,14 +418,14 @@ namespace { } } } - + /** Update tracking information about which blocks a peer is assumed to have. */ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) { CNodeState *state = State(nodeid); assert(state != NULL); - + /*ProcessBlockAvailability(nodeid); - + BlockMap::iterator it = mapBlockIndex.find(hash); if (it != mapBlockIndex.end() && it->second->nChainWork > 0) { // An actually better block was announced. @@ -434,7 +437,7 @@ namespace { state->hashLastUnknownBlock = hash; } } - + /** Find the last common ancestor two blocks have. * Both pa and pb must be non-NULL. */ CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) { @@ -443,27 +446,27 @@ namespace { } else if (pb->GetHeight() > pa->GetHeight()) { pb = pb->GetAncestor(pa->GetHeight()); } - + while (pa != pb && pa && pb) { pa = pa->pprev; pb = pb->pprev; } - + // Eventually all chain branches meet at the genesis block. assert(pa == pb); return pa; } - + /** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has * at most count entries. */ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector& vBlocks, NodeId& nodeStaller) { if (count == 0) return; - + vBlocks.reserve(vBlocks.size() + count); CNodeState *state = State(nodeid); assert(state != NULL); - + // Make sure pindexBestKnownBlock is up to date, we'll need it. ProcessBlockAvailability(nodeid); @@ -471,19 +474,19 @@ namespace { // This peer has nothing interesting. return; } - + if (state->pindexLastCommonBlock == NULL) { // Bootstrap quickly by guessing a parent of our best tip is the forking point. // Guessing wrong in either direction is not a problem. state->pindexLastCommonBlock = chainActive[std::min(state->pindexBestKnownBlock->GetHeight(), chainActive.Height())]; } - + // If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor // of its current tip anymore. Go back enough to fix that. state->pindexLastCommonBlock = LastCommonAncestor(state->pindexLastCommonBlock, state->pindexBestKnownBlock); if (state->pindexLastCommonBlock == state->pindexBestKnownBlock) return; - + std::vector vToFetch; CBlockIndex *pindexWalk = state->pindexLastCommonBlock; // Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last @@ -503,7 +506,7 @@ namespace { for (unsigned int i = nToFetch - 1; i > 0; i--) { vToFetch[i - 1] = vToFetch[i]->pprev; } - + // Iterate over those blocks in vToFetch (in forward direction), adding the ones that // are not yet downloaded and not in flight to vBlocks. In the meantime, update // pindexLastCommonBlock as long as all ancestors are already downloaded, or if it's @@ -537,7 +540,7 @@ namespace { } } } - + } // anon namespace bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { @@ -627,7 +630,7 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(c uint256 hash = tx.GetHash(); if (mapOrphanTransactions.count(hash)) return false; - + // Ignore big transactions, to avoid a // send-big-orphans memory exhaustion attack. If a peer has a legitimate // large transaction with a missing parent then we assume @@ -641,12 +644,12 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(c LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString()); return false; } - + mapOrphanTransactions[hash].tx = tx; mapOrphanTransactions[hash].fromPeer = peer; BOOST_FOREACH(const CTxIn& txin, tx.vin) mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash); - + LogPrint("mempool", "stored orphan tx %s (mapsz %u prevsz %u)\n", hash.ToString(), mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size()); return true; @@ -727,7 +730,7 @@ bool IsStandardTx(const CTransaction& tx, string& reason, const int nHeight) return false; } } - + BOOST_FOREACH(const CTxIn& txin, tx.vin) { // Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed @@ -746,7 +749,7 @@ bool IsStandardTx(const CTransaction& tx, string& reason, const int nHeight) return false; } } - + unsigned int v=0,nDataOut = 0; txnouttype whichType; BOOST_FOREACH(const CTxOut& txout, tx.vout) @@ -757,7 +760,7 @@ bool IsStandardTx(const CTransaction& tx, string& reason, const int nHeight) //fprintf(stderr,">>>>>>>>>>>>>>> vout.%d nDataout.%d\n",v,nDataOut); return false; } - + if (whichType == TX_NULL_DATA) { if ( txout.scriptPubKey.size() > IGUANA_MAXSCRIPTSIZE ) @@ -777,13 +780,13 @@ bool IsStandardTx(const CTransaction& tx, string& reason, const int nHeight) } v++; } - + // only one OP_RETURN txout is permitted if (nDataOut > 1) { reason = "multi-op-return"; return false; } - + return true; } @@ -798,7 +801,7 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) { if ( txin.nSequence == 0xfffffffe && (((int64_t)tx.nLockTime >= LOCKTIME_THRESHOLD && (int64_t)tx.nLockTime > nBlockTime) || ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD && (int64_t)tx.nLockTime > nBlockHeight)) ) { - + } else if (!txin.IsFinal()) { @@ -820,7 +823,7 @@ bool IsExpiredTx(const CTransaction &tx, int nBlockHeight) bool CheckFinalTx(const CTransaction &tx, int flags) { AssertLockHeld(cs_main); - + // By convention a negative value for flags indicates that the // current network-enforced consensus rules should be used. In // a future soft-fork scenario that would mean checking which @@ -828,7 +831,7 @@ bool CheckFinalTx(const CTransaction &tx, int flags) // appropriate flags. At the present time no soft-forks are // scheduled, so no flags are set. flags = std::max(flags, 0); - + // CheckFinalTx() uses chainActive.Height()+1 to evaluate // nLockTime because when IsFinalTx() is called within // CBlock::AcceptBlock(), the height of the block *being* @@ -836,7 +839,7 @@ bool CheckFinalTx(const CTransaction &tx, int flags) // transaction can be part of the *next* block, we need to call // IsFinalTx() with one more than chainActive.Height(). const int nBlockHeight = chainActive.Height() + 1; - + // Timestamps on the other hand don't get any special treatment, // because we can't know what timestamp the next block will have, // and there aren't timestamp applications where it matters. @@ -844,7 +847,7 @@ bool CheckFinalTx(const CTransaction &tx, int flags) const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST) ? chainActive.Tip()->GetMedianTimePast() : GetAdjustedTime(); - + return IsFinalTx(tx, nBlockHeight, nBlockTime); } @@ -868,7 +871,7 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, for (unsigned int i = 0; i < tx.vin.size(); i++) { const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]); - + vector > vSolutions; txnouttype whichType; // get the scriptPubKey corresponding to this input: @@ -880,7 +883,7 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions); if (nArgsExpected < 0) return false; - + // Transactions with extra stuff in their scriptSigs are // non-standard. Note that this EvalScript() call will // be quick, because if there are any operations @@ -891,7 +894,7 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, //printf("Checking script: %s\n", tx.vin[i].scriptSig.ToString().c_str()); if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), consensusBranchId)) return false; - + if (whichType == TX_SCRIPTHASH) { if (stack.empty()) @@ -914,11 +917,11 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, return (sigops <= MAX_P2SH_SIGOPS); } } - + if (stack.size() != (unsigned int)nArgsExpected) return false; } - + return true; } @@ -940,7 +943,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in { if (tx.IsCoinBase() || tx.IsCoinImport()) return 0; - + unsigned int nSigOps = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) { @@ -1074,7 +1077,7 @@ bool ContextualCheckTransaction( return state.DoS(dosLevel, error("ContextualCheckTransaction: overwinter is active"), REJECT_INVALID, "tx-overwinter-active"); } - + // Check that all transactions are unexpired if (IsExpiredTx(tx, nHeight)) { // Don't increase banscore if the transaction only just expired @@ -1114,7 +1117,7 @@ bool ContextualCheckTransaction( if (!(tx.IsMint() || tx.vjoinsplit.empty())) { BOOST_STATIC_ASSERT(crypto_sign_PUBLICKEYBYTES == 32); - + // We rely on libsodium to check that the signature is canonical. // https://github.com/jedisct1/libsodium/commit/62911edb7ff2275cccd74bf1c8aefcc4d76924e0 if (crypto_sign_verify_detached(&tx.joinSplitSig[0], @@ -1213,7 +1216,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, if (!tx.IsCoinBase()) { transactionsValidated.increment(); } - + if (!CheckTransactionWithoutProofVerification(tx, state)) { return false; } else { @@ -1228,10 +1231,32 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, } } +int32_t komodo_isnotaryvout(char *coinaddr) // from ac_private chains only +{ + static int32_t didinit; static char notaryaddrs[sizeof(Notaries_elected1)/sizeof(*Notaries_elected1) + 1][64]; + int32_t i; + if ( didinit == 0 ) + { + uint8_t pubkey33[33]; + for (i=0; i<=sizeof(Notaries_elected1)/sizeof(*Notaries_elected1); i++) + { + if ( i < sizeof(Notaries_elected1)/sizeof(*Notaries_elected1) ) + decode_hex(pubkey33,33,(char *)Notaries_elected1[i][1]); + else decode_hex(pubkey33,33,(char *)CRYPTO777_PUBSECPSTR); + pubkey2addr((char *)notaryaddrs[i],(uint8_t *)pubkey33); + } + didinit = 1; + } + for (i=0; i<=sizeof(Notaries_elected1)/sizeof(*Notaries_elected1); i++) + if ( strcmp(coinaddr,notaryaddrs[i]) == 0 ) + return(1); + return(0); +} + bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidationState &state) { // Basic checks that don't depend on any context - + /** * Previously: * 1. The consensus rule below was: @@ -1284,14 +1309,14 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio if (tx.vout.empty() && tx.vjoinsplit.empty() && tx.vShieldedOutput.empty()) return state.DoS(10, error("CheckTransaction(): vout empty"), REJECT_INVALID, "bad-txns-vout-empty"); - + // Size limits BOOST_STATIC_ASSERT(MAX_BLOCK_SIZE >= MAX_TX_SIZE_AFTER_SAPLING); // sanity BOOST_STATIC_ASSERT(MAX_TX_SIZE_AFTER_SAPLING > MAX_TX_SIZE_BEFORE_SAPLING); // sanity if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_TX_SIZE_AFTER_SAPLING) return state.DoS(100, error("CheckTransaction(): size limits failed"), REJECT_INVALID, "bad-txns-oversize"); - + // Check for negative or overflow output values CAmount nValueOut = 0; int32_t iscoinbase = tx.IsCoinBase(); @@ -1307,9 +1332,14 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio } if ( ASSETCHAINS_PRIVATE != 0 ) { - fprintf(stderr,"private chain nValue %.8f iscoinbase.%d\n",(double)txout.nValue/COIN,iscoinbase); - if ( (txout.nValue > 0 && iscoinbase == 0) || tx.GetValueOut() > 0 ) - return state.DoS(100, error("CheckTransaction(): this is a private chain, no public allowed"),REJECT_INVALID, "bad-txns-acprivacy-chain"); + //fprintf(stderr,"private chain nValue %.8f iscoinbase.%d\n",(double)txout.nValue/COIN,iscoinbase); + if (iscoinbase == 0 && txout.nValue > 0) + { + char destaddr[65]; + Getscriptaddress(destaddr,txout.scriptPubKey); + if ( komodo_isnotaryvout(destaddr) == 0 ) + return state.DoS(100, error("CheckTransaction(): this is a private chain, no public allowed"),REJECT_INVALID, "bad-txns-acprivacy-chain"); + } } if ( txout.scriptPubKey.size() > IGUANA_MAXSCRIPTSIZE ) return state.DoS(100, error("CheckTransaction(): txout.scriptPubKey.size() too big"),REJECT_INVALID, "bad-txns-vout-negative"); @@ -1353,34 +1383,51 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_old negative"), REJECT_INVALID, "bad-txns-vpub_old-negative"); } - + if (joinsplit.vpub_new < 0) { return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_new negative"), REJECT_INVALID, "bad-txns-vpub_new-negative"); } - + if (joinsplit.vpub_old > MAX_MONEY) { return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_old too high"), REJECT_INVALID, "bad-txns-vpub_old-toolarge"); } - + if (joinsplit.vpub_new > MAX_MONEY) { return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_new too high"), REJECT_INVALID, "bad-txns-vpub_new-toolarge"); } - + if (joinsplit.vpub_new != 0 && joinsplit.vpub_old != 0) { return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_new and joinsplit.vpub_old both nonzero"), REJECT_INVALID, "bad-txns-vpubs-both-nonzero"); } - + nValueOut += joinsplit.vpub_old; if (!MoneyRange(nValueOut)) { return state.DoS(100, error("CheckTransaction(): txout total out of range"), REJECT_INVALID, "bad-txns-txouttotal-toolarge"); } } - + if ( ASSETCHAINS_TXPOW != 0 && tx.vjoinsplit.size() == 0 ) + { + // genesis coinbase 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b + uint256 txid = tx.GetHash(); + if ( ((ASSETCHAINS_TXPOW & 2) != 0 && iscoinbase != 0) || ((ASSETCHAINS_TXPOW & 1) != 0 && iscoinbase == 0) ) + { + if ( ((uint8_t *)&txid)[0] != 0 || ((uint8_t *)&txid)[31] != 0 ) + { + uint256 genesistxid = uint256S("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"); + if ( txid != genesistxid ) + { + fprintf(stderr,"private chain iscoinbase.%d invalid txpow.%d txid.%s\n",iscoinbase,ASSETCHAINS_TXPOW,txid.GetHex().c_str()); + return state.DoS(100, error("CheckTransaction(): this is a txpow chain, must have 0x00 ends"),REJECT_INVALID, "bad-txns-actxpow-chain"); + } + } + } + } + // Ensure input values do not exceed MAX_MONEY // We have not resolved the txin values at this stage, // but we do know what the joinsplits claim to add @@ -1390,7 +1437,7 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio for (std::vector::const_iterator it(tx.vjoinsplit.begin()); it != tx.vjoinsplit.end(); ++it) { nValueIn += it->vpub_new; - + if (!MoneyRange(it->vpub_new) || !MoneyRange(nValueIn)) { return state.DoS(100, error("CheckTransaction(): txin total out of range"), REJECT_INVALID, "bad-txns-txintotal-toolarge"); @@ -1418,7 +1465,7 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio REJECT_INVALID, "bad-txns-inputs-duplicate"); vInOutPoints.insert(txin.prevout); } - + // Check for duplicate joinsplit nullifiers in this transaction { set vJoinSplitNullifiers; @@ -1447,7 +1494,7 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio vSaplingNullifiers.insert(spend_desc.nullifier); } } - + if (tx.IsMint()) { // There should be no joinsplits in a coinbase transaction @@ -1474,7 +1521,7 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio return state.DoS(10, error("CheckTransaction(): prevout is null"), REJECT_INVALID, "bad-txns-prevout-null"); } - + return true; } @@ -1490,9 +1537,9 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF if (dPriorityDelta > 0 || nFeeDelta > 0) return 0; } - + CAmount nMinFee = ::minRelayTxFee.GetFee(nBytes); - + if (fAllowFree) { // There is a free transaction area in blocks created by most miners, @@ -1502,7 +1549,7 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF if (nBytes < (DEFAULT_BLOCK_PRIORITY_SIZE - 1000)) nMinFee = 0; } - + if (!MoneyRange(nMinFee)) nMinFee = MAX_MONEY; return nMinFee; @@ -1514,10 +1561,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa AssertLockHeld(cs_main); if (pfMissingInputs) *pfMissingInputs = false; - + int flag=0,nextBlockHeight = chainActive.Height() + 1; auto consensusBranchId = CurrentEpochBranchId(nextBlockHeight, Params().GetConsensus()); - + // Node operator can choose to reject tx by number of transparent inputs static_assert(std::numeric_limits::max() >= std::numeric_limits::max(), "size_t too small"); size_t limit = (size_t) GetArg("-mempooltxinputlimit", 0); @@ -1533,7 +1580,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } auto verifier = libzcash::ProofVerifier::Strict(); - if ( komodo_validate_interest(tx,chainActive.LastTip()->GetHeight()+1,chainActive.LastTip()->GetMedianTimePast() + 777,0) < 0 ) + if ( ASSETCHAINS_SYMBOL[0] == 0 && komodo_validate_interest(tx,chainActive.LastTip()->GetHeight()+1,chainActive.LastTip()->GetMedianTimePast() + 777,0) < 0 ) { //fprintf(stderr,"AcceptToMemoryPool komodo_validate_interest failure\n"); return error("AcceptToMemoryPool: komodo_validate_interest failed"); @@ -1571,13 +1618,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return state.DoS(0, false, REJECT_NONSTANDARD, "non-final"); } - // if this is a valid stake transaction, don't put it in the mempool - CStakeParams p; - if (ValidateStakeTransaction(tx, p, false)) - { - return state.DoS(0, false, REJECT_INVALID, "staking"); - } - // is it already in the memory pool? uint256 hash = tx.GetHash(); if (pool.exists(hash)) @@ -1585,7 +1625,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa //fprintf(stderr,"already in mempool\n"); return state.Invalid(false, REJECT_DUPLICATE, "already in mempool"); } - + // Check for conflicts with in-memory transactions { LOCK(pool.cs); // protect pool.mapNextTx @@ -1622,14 +1662,14 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa LOCK(pool.cs); CCoinsViewMemPool viewMemPool(pcoinsTip, pool); view.SetBackend(viewMemPool); - + // do we already have it? if (view.HaveCoins(hash)) { //fprintf(stderr,"view.HaveCoins(hash) error\n"); return state.Invalid(false, REJECT_DUPLICATE, "already have coins"); } - + if (tx.IsCoinImport()) { // Inverse of normal case; if input exists, it's been spent @@ -1651,7 +1691,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return state.DoS(0, error("AcceptToMemoryPool: tx inputs not found"),REJECT_INVALID, "bad-txns-inputs-missing"); } } - + // are the actual inputs available? if (!view.HaveInputs(tx)) { @@ -1665,7 +1705,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa //fprintf(stderr,"accept failure.2\n"); return state.Invalid(error("AcceptToMemoryPool: joinsplit requirements not met"),REJECT_DUPLICATE, "bad-txns-joinsplit-requirements-not-met"); } - // Bring the best block into scope view.GetBestBlock(); @@ -1675,11 +1714,11 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool view.SetBackend(dummy); } - + // Check for non-standard pay-to-script-hash in inputs if (Params().RequireStandard() && !AreInputsStandard(tx, view, consensusBranchId)) return error("AcceptToMemoryPool: reject nonstandard transaction input"); - + // Check that the transaction doesn't have an excessive number of // sigops, making it impossible to mine. Since the coinbase transaction // itself can contain sigops MAX_STANDARD_TX_SIGOPS is less than @@ -1692,11 +1731,11 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa fprintf(stderr,"accept failure.4\n"); return state.DoS(1, error("AcceptToMemoryPool: too many sigops %s, %d > %d", hash.ToString(), nSigOps, MAX_STANDARD_TX_SIGOPS),REJECT_NONSTANDARD, "bad-txns-too-many-sigops"); } - + CAmount nValueOut = tx.GetValueOut(); CAmount nFees = nValueIn-nValueOut; double dPriority = view.GetPriority(tx, chainActive.Height()); - + // Keep track of transactions that spend a coinbase, which we re-scan // during reorgs to ensure COINBASE_MATURITY is still met. bool fSpendsCoinbase = false; @@ -1709,15 +1748,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } } } - + // Grab the branch ID we expect this transaction to commit to. We don't // yet know if it does, but if the entry gets added to the mempool, then // it has passed ContextualCheckInputs and therefore this is correct. auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); - + CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), mempool.HasNoInputsOf(tx), fSpendsCoinbase, consensusBranchId); unsigned int nSize = entry.GetTxSize(); - + // Accept a tx if it contains joinsplits and has at least the default fee specified by z_sendmany. if (tx.vjoinsplit.size() > 0 && nFees >= ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE) { // In future we will we have more accurate and dynamic computation of fees for tx with joinsplits. @@ -1730,13 +1769,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return state.DoS(0, error("AcceptToMemoryPool: not enough fees %s, %d < %d",hash.ToString(), nFees, txMinFee),REJECT_INSUFFICIENTFEE, "insufficient fee"); } } - + // Require that free transactions have sufficient priority to be mined in the next block. if (GetBoolArg("-relaypriority", false) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) { fprintf(stderr,"accept failure.6\n"); return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority"); } - + // Continuously rate-limit free (really, very-low-fee) transactions // This mitigates 'penny-flooding' -- sending thousands of free transactions just to // be annoying or make others' transactions take longer to confirm. @@ -1746,9 +1785,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa static double dFreeCount; static int64_t nLastTime; int64_t nNow = GetTime(); - + LOCK(csFreeLimiter); - + // Use an exponentially decaying ~10-minute window: dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); nLastTime = nNow; @@ -1780,7 +1819,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa //fprintf(stderr,"accept failure.9\n"); return error("AcceptToMemoryPool: ConnectInputs failed %s", hash.ToString()); } - + // Check again against just the consensus-critical mandatory script // verification flags, in case of bugs in the standard flags that cause // transactions to pass as valid when they're actually invalid. For @@ -1823,9 +1862,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } } } - + SyncWithWallets(tx, NULL); - + return true; } @@ -1946,14 +1985,14 @@ bool myGetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlo bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow) { CBlockIndex *pindexSlow = NULL; - + LOCK(cs_main); - + if (mempool.lookup(hash, txOut)) { return true; } - + if (fTxIndex) { CDiskTxPos postx; if (pblocktree->ReadTxIndex(hash, postx)) { @@ -1974,7 +2013,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock return true; } } - + if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it int nHeight = -1; { @@ -1986,7 +2025,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock if (nHeight > 0) pindexSlow = chainActive[nHeight]; } - + if (pindexSlow) { CBlock block; if (ReadBlockFromDisk(block, pindexSlow,1)) { @@ -1999,7 +2038,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock } } } - + return false; } @@ -2029,18 +2068,18 @@ bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::M CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); if (fileout.IsNull()) return error("WriteBlockToDisk: OpenBlockFile failed"); - + // Write index header unsigned int nSize = GetSerializeSize(fileout, block); fileout << FLATDATA(messageStart) << nSize; - + // Write block long fileOutPos = ftell(fileout.Get()); if (fileOutPos < 0) return error("WriteBlockToDisk: ftell failed"); pos.nPos = (unsigned int)fileOutPos; fileout << block; - + return true; } @@ -2048,7 +2087,7 @@ bool ReadBlockFromDisk(int32_t height,CBlock& block, const CDiskBlockPos& pos,bo { uint8_t pubkey33[33]; block.SetNull(); - + // Open history file to read CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); if (filein.IsNull()) @@ -2056,7 +2095,7 @@ bool ReadBlockFromDisk(int32_t height,CBlock& block, const CDiskBlockPos& pos,bo //fprintf(stderr,"readblockfromdisk err A\n"); return error("ReadBlockFromDisk: OpenBlockFile failed for %s", pos.ToString()); } - + // Read block try { filein >> block; @@ -2074,7 +2113,7 @@ bool ReadBlockFromDisk(int32_t height,CBlock& block, const CDiskBlockPos& pos,bo int32_t i; for (i=0; i<33; i++) fprintf(stderr,"%02x",pubkey33[i]); fprintf(stderr," warning unexpected diff at ht.%d\n",height); - + return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString()); } } @@ -2107,9 +2146,11 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) { if ( nHeight == 1 ) return(100000000 * COIN); // ICO allocation - else if ( nHeight < KOMODO_ENDOFERA ) //komodo_moneysupply(nHeight) < MAX_MONEY ) + else if ( nHeight < KOMODO_ENDOFERA ) return(3 * COIN); - else return(0); + else if ( nHeight < 2*KOMODO_ENDOFERA ) + return(2 * COIN); + else return(COIN); } else { @@ -2128,14 +2169,14 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) nSubsidy *= (nHeight+1); return nSubsidy; } - + assert(nHeight > consensusParams.SubsidySlowStartShift()); int halvings = (nHeight - consensusParams.SubsidySlowStartShift()) / consensusParams.nSubsidyHalvingInterval;*/ // Force block reward to zero when right shift is undefined. //int halvings = nHeight / consensusParams.nSubsidyHalvingInterval; //if (halvings >= 64) // return 0; - + // Subsidy is cut in half every 840,000 blocks which will occur approximately every 4 years. //nSubsidy >>= halvings; //return nSubsidy; @@ -2236,7 +2277,7 @@ void CheckForkWarningConditions() // (we assume we don't get stuck on a fork before finishing our initial sync) if (IsInitialBlockDownload()) return; - + // If our best fork is no longer within 288 blocks (+/- 12 hours if no one mines it) // of our head, drop it if (pindexBestForkTip && chainActive.Height() - pindexBestForkTip->GetHeight() >= 288) @@ -2286,7 +2327,7 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) break; pfork = pfork->pprev; } - + // We define a condition where we should warn the user about as a fork of at least 7 blocks // with a tip within 72 blocks (+/- 3 hours if no one mines it) of ours // We use 7 blocks rather arbitrarily as it represents just under 10% of sustained network @@ -2301,7 +2342,7 @@ void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) pindexBestForkTip = pindexNewForkTip; pindexBestForkBase = pfork; } - + CheckForkWarningConditions(); } @@ -2310,11 +2351,11 @@ void Misbehaving(NodeId pnode, int howmuch) { if (howmuch == 0) return; - + CNodeState *state = State(pnode); if (state == NULL) return; - + state->nMisbehavior += howmuch; int banscore = GetArg("-banscore", 101); if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore) @@ -2372,7 +2413,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund BOOST_FOREACH(const CTxIn &txin, tx.vin) { CCoinsModifier coins = inputs.ModifyCoins(txin.prevout.hash); unsigned nPos = txin.prevout.n; - + if (nPos >= coins->vout.size() || coins->vout[nPos].IsNull()) assert(false); // mark an outpoint spent, and construct undo information @@ -2391,7 +2432,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund inputs.SetNullifiers(tx, true); inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight); // add outputs - + // Unorthodox state if (tx.IsCoinImport()) { // add a tombstone for the burnTx @@ -2428,11 +2469,11 @@ namespace Consensus { // for an attacker to attempt to split the network. if (!inputs.HaveInputs(tx)) return state.Invalid(error("CheckInputs(): %s inputs unavailable", tx.GetHash().ToString())); - + // are the JoinSplit's requirements met? if (!inputs.HaveJoinSplitRequirements(tx)) return state.Invalid(error("CheckInputs(): %s JoinSplit requirements not met", tx.GetHash().ToString())); - + CAmount nValueIn = 0; CAmount nFees = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) @@ -2455,9 +2496,9 @@ namespace Consensus { // Ensure that coinbases are matured, no DoS as retry may work later if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) { - return state.DoS(0, - error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight), - REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); + return state.Invalid( + error("CheckInputs(): tried to spend coinbase at depth %d/%d", nSpendHeight - coins->nHeight, (int32_t)COINBASE_MATURITY), + REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); } // Ensure that coinbases cannot be spent to transparent outputs @@ -2471,7 +2512,7 @@ namespace Consensus { REJECT_INVALID, "bad-txns-coinbase-spend-has-transparent-outputs"); } } - + // Check for negative or overflow input values nValueIn += coins->vout[prevout.n].nValue; #ifdef KOMODO_ENABLE_INTEREST @@ -2491,14 +2532,14 @@ namespace Consensus { if (!MoneyRange(coins->vout[prevout.n].nValue) || !MoneyRange(nValueIn)) return state.DoS(100, error("CheckInputs(): txin values out of range"), REJECT_INVALID, "bad-txns-inputvalues-outofrange"); - + } nValueIn += tx.GetShieldedValueIn(); if (!MoneyRange(nValueIn)) return state.DoS(100, error("CheckInputs(): shielded input to transparent value pool out of range"), REJECT_INVALID, "bad-txns-inputvalues-outofrange"); - + if (nValueIn < tx.GetValueOut()) { fprintf(stderr,"spentheight.%d valuein %s vs %s error\n",nSpendHeight,FormatMoney(nValueIn).c_str(), FormatMoney(tx.GetValueOut()).c_str()); @@ -2535,14 +2576,14 @@ bool ContextualCheckInputs( if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs), consensusParams)) { return false; } - + if (pvChecks) pvChecks->reserve(tx.vin.size()); - + // The first loop above does all the inexpensive checks. // Only if ALL inputs pass do we perform expensive ECDSA signature checks. // Helps prevent CPU exhaustion attacks. - + // Skip ECDSA signature verification when connecting blocks // before the last block chain checkpoint. This is safe because block merkle hashes are // still computed and checked, and any change will be caught at the next checkpoint. @@ -2551,7 +2592,7 @@ bool ContextualCheckInputs( const COutPoint &prevout = tx.vin[i].prevout; const CCoins* coins = inputs.AccessCoins(prevout.hash); assert(coins); - + // Verify signature CScriptCheck check(*coins, tx, i, flags, cacheStore, consensusBranchId, &txdata); if (pvChecks) { @@ -2599,7 +2640,7 @@ bool ContextualCheckInputs( fprintf(stderr,"ContextualCheckInputs failure.0\n"); return false; } - + if (!tx.IsCoinBase()) { // While checking, GetBestBlock() refers to the parent block. @@ -2613,60 +2654,60 @@ bool ContextualCheckInputs( // Assertion is okay because NonContextualCheckInputs ensures the inputs // are available. assert(coins); - + // If prev is coinbase, check that it's matured if (coins->IsCoinBase()) { if ( ASSETCHAINS_SYMBOL[0] == 0 ) COINBASE_MATURITY = _COINBASE_MATURITY; if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) { fprintf(stderr,"ContextualCheckInputs failure.1 i.%d of %d\n",i,(int32_t)tx.vin.size()); - + return state.Invalid( error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight),REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); } } } } - + return true; }*/ namespace { - + bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart) { // Open history file to append CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); if (fileout.IsNull()) return error("%s: OpenUndoFile failed", __func__); - + // Write index header unsigned int nSize = GetSerializeSize(fileout, blockundo); fileout << FLATDATA(messageStart) << nSize; - + // Write undo data long fileOutPos = ftell(fileout.Get()); if (fileOutPos < 0) return error("%s: ftell failed", __func__); pos.nPos = (unsigned int)fileOutPos; fileout << blockundo; - + // calculate & write checksum CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); hasher << hashBlock; hasher << blockundo; fileout << hasher.GetHash(); - + return true; } - + bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uint256& hashBlock) { // Open history file to read CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); if (filein.IsNull()) return error("%s: OpenBlockFile failed", __func__); - + // Read block uint256 hashChecksum; try { @@ -2682,10 +2723,10 @@ namespace { hasher << blockundo; if (hashChecksum != hasher.GetHash()) return error("%s: Checksum mismatch", __func__); - + return true; } - + /** Abort with a message */ bool AbortNode(const std::string& strMessage, const std::string& userMessage="") { @@ -2697,13 +2738,13 @@ namespace { StartShutdown(); return false; } - + bool AbortNode(CValidationState& state, const std::string& strMessage, const std::string& userMessage="") { AbortNode(strMessage, userMessage); return state.Error(strMessage); } - + } // anon namespace /** @@ -2716,7 +2757,7 @@ namespace { static bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const COutPoint& out) { bool fClean = true; - + CCoinsModifier coins = view.ModifyCoins(out.hash); if (undo.nHeight != 0) { // undo data contains height: this is the last output of the prevout tx being spent @@ -2735,7 +2776,7 @@ static bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const CO if (coins->vout.size() < out.n+1) coins->vout.resize(out.n+1); coins->vout[out.n] = undo.txout; - + return fClean; } @@ -2773,10 +2814,10 @@ void DisconnectNotarisations(const CBlock &block) bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) { assert(pindex->GetBlockHash() == view.GetBestBlock()); - + if (pfClean) *pfClean = false; - + bool fClean = true; komodo_disconnect(pindex,block); CBlockUndo blockUndo; @@ -2785,7 +2826,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex return error("DisconnectBlock(): no undo data available"); if (!UndoReadFromDisk(blockUndo, pos, pindex->pprev->GetBlockHash())) return error("DisconnectBlock(): failure reading undo data"); - + if (blockUndo.vtxundo.size() + 1 != block.vtx.size()) return error("DisconnectBlock(): block and undo data inconsistent"); std::vector > addressIndex; @@ -2823,23 +2864,23 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex } else if (out.scriptPubKey.IsPayToPublicKey()) { vector hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34); - + // undo receiving activity addressIndex.push_back(make_pair(CAddressIndexKey(1, Hash160(hashBytes), pindex->GetHeight(), i, hash, k, false), out.nValue)); // undo unspent index addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, Hash160(hashBytes), hash, k), CAddressUnspentValue())); - + } else if (out.scriptPubKey.IsPayToCryptoCondition()) { vector hashBytes(out.scriptPubKey.begin(), out.scriptPubKey.end()); - + // undo receiving activity addressIndex.push_back(make_pair(CAddressIndexKey(1, Hash160(hashBytes), pindex->GetHeight(), i, hash, k, false), out.nValue)); // undo unspent index addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, Hash160(hashBytes), hash, k), CAddressUnspentValue())); - + } else { continue; @@ -2863,11 +2904,11 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex outs->nVersion = outsBlock.nVersion; if (*outs != outsBlock) fClean = fClean && error("DisconnectBlock(): added transaction mismatch? database corrupted"); - + // remove outputs outs->Clear(); } - + // unspend nullifiers view.SetNullifiers(tx, false); @@ -2914,23 +2955,23 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex } else if (prevout.scriptPubKey.IsPayToPublicKey()) { vector hashBytes(prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34); - + // undo spending activity addressIndex.push_back(make_pair(CAddressIndexKey(1, Hash160(hashBytes), pindex->GetHeight(), i, hash, j, true), prevout.nValue * -1)); // restore unspent index addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, Hash160(hashBytes), input.prevout.hash, input.prevout.n), CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight))); - + } else if (prevout.scriptPubKey.IsPayToCryptoCondition()) { vector hashBytes(prevout.scriptPubKey.begin(), prevout.scriptPubKey.end()); - + // undo spending activity addressIndex.push_back(make_pair(CAddressIndexKey(1, Hash160(hashBytes), pindex->GetHeight(), i, hash, j, true), prevout.nValue * -1)); // restore unspent index addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, Hash160(hashBytes), input.prevout.hash, input.prevout.n), CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight))); - + } else { continue; @@ -2960,7 +3001,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex // move best block pointer to prevout block view.SetBestBlock(pindex->pprev->GetBlockHash()); - + if (pfClean) { *pfClean = fClean; return true; @@ -2981,9 +3022,9 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex void static FlushBlockFile(bool fFinalize = false) { LOCK(cs_LastBlockFile); - + CDiskBlockPos posOld(nLastBlockFile, 0); - + FILE *fileOld = OpenBlockFile(posOld); if (fileOld) { if (fFinalize) @@ -2991,7 +3032,7 @@ void static FlushBlockFile(bool fFinalize = false) FileCommit(fileOld); fclose(fileOld); } - + fileOld = OpenUndoFile(posOld); if (fileOld) { if (fFinalize) @@ -3019,20 +3060,20 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const int64_t nPowTargetSpacing) { if (bestHeader == NULL || initialDownloadCheck()) return; - + static int64_t lastAlertTime = 0; int64_t now = GetAdjustedTime(); if (lastAlertTime > now-60*60*24) return; // Alert at most once per day - + const int SPAN_HOURS=4; const int SPAN_SECONDS=SPAN_HOURS*60*60; int BLOCKS_EXPECTED = SPAN_SECONDS / nPowTargetSpacing; - + boost::math::poisson_distribution poisson(BLOCKS_EXPECTED); - + std::string strWarning; int64_t startTime = GetAdjustedTime()-SPAN_SECONDS; - + LOCK(cs); const CBlockIndex* i = bestHeader; int nBlocks = 0; @@ -3041,13 +3082,13 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const i = i->pprev; if (i == NULL) return; // Ran out of chain, we must not be fully synced } - + // How likely is it to find that many by chance? double p = boost::math::pdf(poisson, nBlocks); - + LogPrint("partitioncheck", "%s : Found %d blocks in the last %d hours\n", __func__, nBlocks, SPAN_HOURS); LogPrint("partitioncheck", "%s : likelihood: %g\n", __func__, p); - + // Aim for one false-positive about every fifty years of normal running: const int FIFTY_YEARS = 50*365*24*60*60; double alertThreshold = 1.0 / (FIFTY_YEARS / SPAN_SECONDS); @@ -3106,7 +3147,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin //fprintf(stderr,"checkblock failure in connectblock futureblock.%d\n",futureblock); return false; } - + // verify that the view's current state corresponds to the previous block uint256 hashPrevBlock = pindex->pprev == NULL ? uint256() : pindex->pprev->GetBlockHash(); if ( hashPrevBlock != view.GetBestBlock() ) @@ -3116,7 +3157,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin REJECT_INVALID, "hashPrevBlock-not-bestblock"); } assert(hashPrevBlock == view.GetBestBlock()); - + // Special case for the genesis block, skipping connection of its transactions // (its coinbase is unspendable) if (block.GetHash() == chainparams.GetConsensus().hashGenesisBlock) { @@ -3142,13 +3183,13 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"), REJECT_INVALID, "bad-txns-BIP30"); } - + unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; - + // DERSIG (BIP66) is also always enforced, but does not have a flag. - + CBlockUndo blockundo; - + if ( ASSETCHAINS_CC != 0 ) { if ( scriptcheckqueue.IsIdle() == 0 ) @@ -3158,7 +3199,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } } CCheckQueueControl control(fExpensiveChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); - + int64_t nTimeStart = GetTimeMicros(); CAmount nFees = 0; int nInputs = 0; @@ -3274,14 +3315,14 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, error("ConnectBlock(): too many sigops"), REJECT_INVALID, "bad-blk-sigops"); } - + txdata.emplace_back(tx); - + if (!tx.IsCoinBase()) { nFees += view.GetValueIn(chainActive.LastTip()->GetHeight(),&interest,tx,chainActive.LastTip()->nTime) - tx.GetValueOut(); sum += interest; - + std::vector vChecks; if (!ContextualCheckInputs(tx, state, view, fExpensiveChecks, flags, false, txdata[i], chainparams.GetConsensus(), consensusBranchId, nScriptCheckThreads ? &vChecks : NULL)) return false; @@ -3314,7 +3355,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } else if (out.scriptPubKey.IsPayToPublicKey()) { vector hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34); - + // record receiving activity addressIndex.push_back(make_pair(CAddressIndexKey(1, Hash160(hashBytes), pindex->GetHeight(), i, txhash, k, false), out.nValue)); @@ -3324,7 +3365,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } else if (out.scriptPubKey.IsPayToCryptoCondition()) { vector hashBytes(out.scriptPubKey.begin(), out.scriptPubKey.end()); - + // record receiving activity addressIndex.push_back(make_pair(CAddressIndexKey(1, Hash160(hashBytes), pindex->GetHeight(), i, txhash, k, false), out.nValue)); @@ -3386,7 +3427,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 && ASSETCHAINS_COMMISSION != 0 ) { uint64_t checktoshis; - if ( (checktoshis= komodo_commission((CBlock *)&block)) != 0 ) + 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; @@ -3413,10 +3454,10 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, false); int64_t nTime2 = GetTimeMicros(); nTimeVerify += nTime2 - nTimeStart; LogPrint("bench", " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs]\n", nInputs - 1, 0.001 * (nTime2 - nTimeStart), nInputs <= 1 ? 0 : 0.001 * (nTime2 - nTimeStart) / (nInputs-1), nTimeVerify * 0.000001); - + if (fJustCheck) return true; - + // Write undo information to disk if (pindex->GetUndoPos().IsNull() || !pindex->IsValid(BLOCK_VALID_SCRIPTS)) { @@ -3426,12 +3467,12 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return error("ConnectBlock(): FindUndoPos failed"); if (!UndoWriteToDisk(blockundo, pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart())) return AbortNode(state, "Failed to write undo data"); - + // update nUndoPos in block index pindex->nUndoPos = pos.nPos; pindex->nStatus |= BLOCK_HAVE_UNDO; } - + // Now that all consensus rules have been validated, set nCachedBranchId. // Move this if BLOCK_VALID_CONSENSUS is ever altered. static_assert(BLOCK_VALID_CONSENSUS == BLOCK_VALID_SCRIPTS, @@ -3442,7 +3483,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } else if (pindex->pprev) { pindex->nCachedBranchId = pindex->pprev->nCachedBranchId; } - + pindex->RaiseValidity(BLOCK_VALID_SCRIPTS); setDirtyBlockIndex.insert(pindex); } @@ -3466,7 +3507,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (!pblocktree->UpdateSpentIndex(spentIndex)) return AbortNode(state, "Failed to write transaction index"); - if (fTimestampIndex) { + if (fTimestampIndex) + { unsigned int logicalTS = pindex->nTime; unsigned int prevLogicalTS = 0; @@ -3489,18 +3531,18 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // add this block to the view's block chain view.SetBestBlock(pindex->GetBlockHash()); - + int64_t nTime3 = GetTimeMicros(); nTimeIndex += nTime3 - nTime2; LogPrint("bench", " - Index writing: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeIndex * 0.000001); - + // Watch for changes to the previous coinbase transaction. static uint256 hashPrevBestCoinBase; GetMainSignals().UpdatedTransaction(hashPrevBestCoinBase); hashPrevBestCoinBase = block.vtx[0].GetHash(); - + int64_t nTime4 = GetTimeMicros(); nTimeCallbacks += nTime4 - nTime3; LogPrint("bench", " - Callbacks: %.2fms [%.2fs]\n", 0.001 * (nTime4 - nTime3), nTimeCallbacks * 0.000001); - + //FlushStateToDisk(); komodo_connectblock(pindex,*(CBlock *)&block); return true; @@ -3630,7 +3672,7 @@ void PruneAndFlush() { void static UpdateTip(CBlockIndex *pindexNew) { const CChainParams& chainParams = Params(); chainActive.SetTip(pindexNew); - + // New best block nTimeBestReceived = GetTime(); mempool.AddTransactionsUpdated(1); @@ -3650,9 +3692,9 @@ void static UpdateTip(CBlockIndex *pindexNew) { (unsigned long)chainActive.LastTip()->nChainTx, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.LastTip()->GetBlockTime()), progress, pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); - + cvBlockChange.notify_all(); - + // Check the version of the last 100 blocks to see if we need to upgrade: static bool fWarned = false; if (!IsInitialBlockDownload() && !fWarned) @@ -3688,6 +3730,16 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { CBlock block; if (!ReadBlockFromDisk(block, pindexDelete,1)) return AbortNode(state, "Failed to read block"); + //if ( ASSETCHAINS_SYMBOL[0] != 0 || pindexDelete->GetHeight() > 1400000 ) + { + int32_t prevMoMheight; uint256 notarizedhash,txid; + komodo_notarized_height(&prevMoMheight,¬arizedhash,&txid); + if ( block.GetHash() == notarizedhash ) + { + fprintf(stderr,"DisconnectTip trying to disconnect notarized block at ht.%d\n",(int32_t)pindexDelete->GetHeight()); + return(false); + } + } // Apply the block atomically to the chain state. uint256 sproutAnchorBeforeDisconnect = pcoinsTip->GetBestAnchor(SPROUT); uint256 saplingAnchorBeforeDisconnect = pcoinsTip->GetBestAnchor(SAPLING); @@ -3724,16 +3776,6 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { { mempool.remove(tx, removed, true); } - - // if this is a staking tx, and we are on Verus Sapling with nothing at stake solution, - // save staking tx as a possible cheat - if (NetworkUpgradeActive(pindexDelete->GetHeight(), Params().GetConsensus(), Consensus::UPGRADE_SAPLING) && - ASSETCHAINS_LWMAPOS && (i == (block.vtx.size() - 1)) && - (block.IsVerusPOSBlock())) - { - CTxHolder txh = CTxHolder(block.vtx[i], pindexDelete->GetHeight()); - cheatList.Add(txh); - } } if (sproutAnchorBeforeDisconnect != sproutAnchorAfterDisconnect) { // The anchor may not change between block disconnects, @@ -3746,7 +3788,7 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { mempool.removeWithAnchor(saplingAnchorBeforeDisconnect, SAPLING); } } - + // Update chainActive and related variables. UpdateTip(pindexDelete->pprev); @@ -3787,7 +3829,7 @@ static int64_t nTimePostConnect = 0; * You probably want to call mempool.removeWithoutBranchId after this, with cs_main held. */ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *pblock) { - + assert(pindexNew->pprev == chainActive.Tip()); // Read block from disk. int64_t nTime1 = GetTimeMicros(); @@ -3856,7 +3898,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001); LogPrint("bench", "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001); if ( KOMODO_LONGESTCHAIN != 0 && pindexNew->GetHeight() >= KOMODO_LONGESTCHAIN ) - KOMODO_INSYNC = 1; + KOMODO_INSYNC = (int32_t)pindexNew->GetHeight(); else KOMODO_INSYNC = 0; //fprintf(stderr,"connect.%d insync.%d\n",(int32_t)pindexNew->GetHeight(),KOMODO_INSYNC); if ( ASSETCHAINS_SYMBOL[0] == 0 && KOMODO_INSYNC != 0 ) @@ -3871,7 +3913,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * static CBlockIndex* FindMostWorkChain() { do { CBlockIndex *pindexNew = NULL; - + // Find the best candidate header. { std::set::reverse_iterator it = setBlockIndexCandidates.rbegin(); @@ -3879,7 +3921,7 @@ static CBlockIndex* FindMostWorkChain() { return NULL; pindexNew = *it; } - + // Check whether all blocks on the path between the currently active chain and the candidate are valid. // Just going until the active chain is an optimization, as we know all blocks in it are valid already. CBlockIndex *pindexTest = pindexNew; @@ -3950,27 +3992,38 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo // our genesis block. In practice this (probably) won't happen because of checks elsewhere. auto reorgLength = pindexOldTip ? pindexOldTip->GetHeight() - (pindexFork ? pindexFork->GetHeight() : -1) : 0; static_assert(MAX_REORG_LENGTH > 0, "We must be able to reorg some distance"); - if (reorgLength > MAX_REORG_LENGTH) { - auto msg = strprintf(_( - "A block chain reorganization has been detected that would roll back %d blocks! " - "This is larger than the maximum of %d blocks, and so the node is shutting down for your safety." - ), reorgLength, MAX_REORG_LENGTH) + "\n\n" + - _("Reorganization details") + ":\n" + - "- " + strprintf(_("Current tip: %s, height %d, work %s\nstake %s"), - pindexOldTip->phashBlock->GetHex(), pindexOldTip->GetHeight(), pindexOldTip->chainPower.chainWork.GetHex(), - pindexOldTip->chainPower.chainStake.GetHex()) + "\n" + - "- " + strprintf(_("New tip: %s, height %d, work %s\nstake %s"), - pindexMostWork->phashBlock->GetHex(), pindexMostWork->GetHeight(), pindexMostWork->chainPower.chainWork.GetHex(), - pindexMostWork->chainPower.chainStake.GetHex()) + "\n" + - "- " + strprintf(_("Fork point: %s %s, height %d"), - ASSETCHAINS_SYMBOL,pindexFork->phashBlock->GetHex(), pindexFork->GetHeight()) + "\n\n" + - _("Please help, human!"); - LogPrintf("*** %s\n", msg); - uiInterface.ThreadSafeMessageBox(msg, "", CClientUIInterface::MSG_ERROR); - StartShutdown(); - return false; + if (reorgLength > MAX_REORG_LENGTH) + { + int32_t notarizedht,prevMoMheight; uint256 notarizedhash,txid; + notarizedht = komodo_notarized_height(&prevMoMheight,¬arizedhash,&txid); + if ( pindexFork->GetHeight() < notarizedht ) + { + fprintf(stderr,"pindexFork->GetHeight().%d is < notarizedht %d, so ignore it\n",(int32_t)pindexFork->GetHeight(),notarizedht); + pindexFork = pindexOldTip; + } + else + { + auto msg = strprintf(_( + "A block chain reorganization has been detected that would roll back %d blocks! " + "This is larger than the maximum of %d blocks, and so the node is shutting down for your safety." + ), reorgLength, MAX_REORG_LENGTH) + "\n\n" + + _("Reorganization details") + ":\n" + + "- " + strprintf(_("Current tip: %s, height %d, work %s\nstake %s"), + pindexOldTip->phashBlock->GetHex(), pindexOldTip->GetHeight(), pindexOldTip->chainPower.chainWork.GetHex(), + pindexOldTip->chainPower.chainStake.GetHex()) + "\n" + + "- " + strprintf(_("New tip: %s, height %d, work %s\nstake %s"), + pindexMostWork->phashBlock->GetHex(), pindexMostWork->GetHeight(), pindexMostWork->chainPower.chainWork.GetHex(), + pindexMostWork->chainPower.chainStake.GetHex()) + "\n" + + "- " + strprintf(_("Fork point: %s %s, height %d"), + ASSETCHAINS_SYMBOL,pindexFork->phashBlock->GetHex(), pindexFork->GetHeight()) + "\n\n" + + _("Please help, human!"); + LogPrintf("*** %s\n", msg); + uiInterface.ThreadSafeMessageBox(msg, "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return false; + } } - + // Disconnect active blocks which are no longer in the best chain. bool fBlocksDisconnected = false; @@ -4013,7 +4066,7 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo pindexIter = pindexIter->pprev; } nHeight = nTargetHeight; - + // Connect new blocks. BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) { if (!ConnectTip(state, pindexConnect, pindexConnect == pindexMostWork ? pblock : NULL)) { @@ -4039,14 +4092,14 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo } } } - + if (fBlocksDisconnected) { mempool.removeForReorg(pcoinsTip, chainActive.Tip()->GetHeight() + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); } mempool.removeWithoutBranchId( CurrentEpochBranchId(chainActive.Tip()->GetHeight() + 1, Params().GetConsensus())); mempool.check(pcoinsTip); - + // Callbacks/notifications for a new best chain. if (fInvalidFound) CheckForkWarningConditionsOnNewFork(vpindexToConnect.back()); @@ -4067,23 +4120,23 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { const CChainParams& chainParams = Params(); do { boost::this_thread::interruption_point(); - + bool fInitialDownload; { LOCK(cs_main); pindexMostWork = FindMostWorkChain(); - + // Whether we have anything to do at all. if (pindexMostWork == NULL || pindexMostWork == chainActive.Tip()) return true; - + if (!ActivateBestChainStep(state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL)) return false; pindexNewTip = chainActive.Tip(); fInitialDownload = IsInitialBlockDownload(); } // When we reach this point, we switched to a new tip (stored in pindexNewTip). - + // Notifications/callbacks that can run without cs_main if (!fInitialDownload) { uint256 hashNewTip = pindexNewTip->GetBlockHash(); @@ -4105,23 +4158,23 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { } //else fprintf(stderr,"initial download skips propagation\n"); } while(pindexMostWork != chainActive.Tip()); CheckBlockIndex(); - + // Write changes periodically to disk, after relay. if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) { return false; } - + return true; } bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) { AssertLockHeld(cs_main); - + // Mark the block itself as invalid. pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); setBlockIndexCandidates.erase(pindex); - + while (chainActive.Contains(pindex)) { CBlockIndex *pindexWalk = chainActive.Tip(); pindexWalk->nStatus |= BLOCK_FAILED_CHILD; @@ -4137,7 +4190,7 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) { } } //LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); - + // The resulting new best tip may not be in setBlockIndexCandidates anymore, so // add it again. BlockMap::iterator it = mapBlockIndex.begin(); @@ -4147,7 +4200,7 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) { } it++; } - + InvalidChainFound(pindex); mempool.removeForReorg(pcoinsTip, chainActive.Tip()->GetHeight() + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); mempool.removeWithoutBranchId( @@ -4176,7 +4229,7 @@ bool ReconsiderBlock(CValidationState& state, CBlockIndex *pindex) { } it++; } - + // Remove the invalidity flag from all ancestors too. while (pindex != NULL) { if (pindex->nStatus & BLOCK_FAILED_MASK) { @@ -4243,7 +4296,7 @@ CBlockIndex* AddToBlockIndex(const CBlockHeader& block) pindexNew->RaiseValidity(BLOCK_VALID_TREE); if (pindexBestHeader == NULL || pindexBestHeader->chainPower < pindexNew->chainPower) pindexBestHeader = pindexNew; - + setDirtyBlockIndex.insert(pindexNew); //fprintf(stderr,"added to block index %s %p\n",hash.ToString().c_str(),pindexNew); mi->second = pindexNew; @@ -4279,12 +4332,12 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl pindexNew->nStatus |= BLOCK_HAVE_DATA; pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS); setDirtyBlockIndex.insert(pindexNew); - + if (pindexNew->pprev == NULL || pindexNew->pprev->nChainTx) { // If pindexNew is the genesis block or all parents are BLOCK_VALID_TRANSACTIONS. deque queue; queue.push_back(pindexNew); - + // Recursively process any descendant blocks that now may be eligible to be connected. while (!queue.empty()) { CBlockIndex *pindex = queue.front(); @@ -4325,19 +4378,19 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl mapBlocksUnlinked.insert(std::make_pair(pindexNew->pprev, pindexNew)); } } - + return true; } bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false) { LOCK(cs_LastBlockFile); - + unsigned int nFile = fKnown ? pos.nFile : nLastBlockFile; if (vinfoBlockFile.size() <= nFile) { vinfoBlockFile.resize(nFile + 1); } - + if (!fKnown) { while (vinfoBlockFile[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { nFile++; @@ -4348,7 +4401,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd pos.nFile = nFile; pos.nPos = vinfoBlockFile[nFile].nSize; } - + if (nFile != nLastBlockFile) { if (!fKnown) { LogPrintf("Leaving block file %i: %s\n", nFile, vinfoBlockFile[nFile].ToString()); @@ -4356,13 +4409,13 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd FlushBlockFile(!fKnown); nLastBlockFile = nFile; } - + vinfoBlockFile[nFile].AddBlock(nHeight, nTime); if (fKnown) vinfoBlockFile[nFile].nSize = std::max(pos.nPos + nAddSize, vinfoBlockFile[nFile].nSize); else vinfoBlockFile[nFile].nSize += nAddSize; - + if (!fKnown) { unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; unsigned int nNewChunks = (vinfoBlockFile[nFile].nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; @@ -4381,7 +4434,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd return state.Error("out of disk space"); } } - + setDirtyFileInfo.insert(nFile); return true; } @@ -4389,14 +4442,14 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize) { pos.nFile = nFile; - + LOCK(cs_LastBlockFile); - + unsigned int nNewSize; pos.nPos = vinfoBlockFile[nFile].nUndoSize; nNewSize = vinfoBlockFile[nFile].nUndoSize += nAddSize; setDirtyFileInfo.insert(nFile); - + unsigned int nOldChunks = (pos.nPos + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; unsigned int nNewChunks = (nNewSize + UNDOFILE_CHUNK_SIZE - 1) / UNDOFILE_CHUNK_SIZE; if (nNewChunks > nOldChunks) { @@ -4413,7 +4466,7 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne else return state.Error("out of disk space"); } - + return true; } @@ -4458,7 +4511,7 @@ bool CheckBlockHeader(int32_t *futureblockp,int32_t height,CBlockIndex *pindex, // Check block version if (height > 0 && blockhdr.nVersion < MIN_BLOCK_VERSION) return state.DoS(100, error("CheckBlockHeader(): block version too low"),REJECT_INVALID, "version-too-low"); - + // Check Equihash solution is valid if ( fCheckPOW ) { @@ -4513,7 +4566,7 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C if (block.hashMerkleRoot != hashMerkleRoot2) return state.DoS(100, error("CheckBlock: hashMerkleRoot mismatch"), REJECT_INVALID, "bad-txnmrklroot", true); - + // Check for merkle tree malleability (CVE-2012-2459): repeating sequences // of transactions in a block without affecting the merkle root of a block, // while still invalidating it. @@ -4521,16 +4574,16 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C return state.DoS(100, error("CheckBlock: duplicate transaction"), REJECT_INVALID, "bad-txns-duplicate", true); } - + // All potential-corruption validation must be done before we do any // transaction validation, as otherwise we may mark the header as invalid // because we receive the wrong transactions for it. - + // Size limits if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return state.DoS(100, error("CheckBlock: size limits failed"), REJECT_INVALID, "bad-blk-length"); - + // First transaction must be coinbase, the rest must not be if (block.vtx.empty() || !block.vtx[0].IsCoinBase()) return state.DoS(100, error("CheckBlock: first tx is not coinbase"), @@ -4540,7 +4593,7 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C if (block.vtx[i].IsCoinBase()) return state.DoS(100, error("CheckBlock: more than one coinbase"), REJECT_INVALID, "bad-cb-multiple"); - + // Check transactions CTransaction sTx; CTransaction *ptx = NULL; @@ -4548,6 +4601,22 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C { int32_t i,j,rejects=0,lastrejects=0; //fprintf(stderr,"put block's tx into mempool\n"); + // Copy all non Z-txs in mempool to temporary mempool because there can be tx in local mempool that make the block invalid. + LOCK(mempool.cs); + list transactionsToRemove; + BOOST_FOREACH(const CTxMemPoolEntry& e, mempool.mapTx) { + const CTransaction &tx = e.GetTx(); + const uint256 &hash = tx.GetHash(); + if ( tx.vjoinsplit.size() == 0 ) { + transactionsToRemove.push_back(tx); + tmpmempool.addUnchecked(hash,e,!IsInitialBlockDownload()); + } + } + BOOST_FOREACH(const CTransaction& tx, transactionsToRemove) { + list removed; + mempool.remove(tx, removed, false); + } + // add all the txs in the block to the empty mempool. while ( 1 ) { for (i=0; i 0 ) + fprintf(stderr, "number of invalid txs: %d\n",invalidtxs ); + // empty the temp mempool for next time. + tmpmempool.clear(); + } return true; } @@ -4622,7 +4710,7 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta uint256 hash = block.GetHash(); if (hash == consensusParams.hashGenesisBlock) return true; - + assert(pindexPrev); int nHeight = pindexPrev->GetHeight()+1; @@ -4635,7 +4723,7 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta return state.DoS(100, error("%s: incorrect proof of work", __func__), REJECT_INVALID, "bad-diffbits"); } - + // Check timestamp against prev if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) { @@ -4692,7 +4780,7 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta if (block.nVersion < 4) return state.Invalid(error("%s : rejected nVersion<4 block", __func__), REJECT_OBSOLETE, "bad-version"); - + return true; } @@ -4711,13 +4799,6 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn return false; // Failure reason has been set in validation state object } - // if this is a stake transaction with a stake opreturn, reject it if not staking a block. don't check coinbase or actual stake tx - CStakeParams p; - if (sapling && i > 0 && i < (block.vtx.size() - 1) && ValidateStakeTransaction(tx, p, false)) - { - return state.DoS(10, error("%s: attempt to submit block with staking transaction that is not staking", __func__), REJECT_INVALID, "bad-txns-staking"); - } - int nLockTimeFlags = 0; int64_t nLockTimeCutoff = (nLockTimeFlags & LOCKTIME_MEDIAN_TIME_PAST) ? pindexPrev->GetMedianTimePast() @@ -4726,7 +4807,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn return state.DoS(10, error("%s: contains a non-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal"); } } - + // Enforce BIP 34 rule that the coinbase starts with serialized block height. // In Zcash this has been enforced since launch, except that the genesis // block didn't include the height in the coinbase (see Zcash protocol spec @@ -4760,7 +4841,15 @@ bool AcceptBlockHeader(int32_t *futureblockp,const CBlockHeader& block, CValidat if (ppindex) *ppindex = pindex; if ( pindex != 0 && pindex->nStatus & BLOCK_FAILED_MASK ) - return state.Invalid(error("%s: block is marked invalid", __func__), 0, "duplicate"); + { + if ( ASSETCHAINS_CC == 0 ) + return state.Invalid(error("%s: block is marked invalid", __func__), 0, "duplicate"); + else + { + fprintf(stderr,"reconsider block %s\n",hash.GetHex().c_str()); + pindex->nStatus &= ~BLOCK_FAILED_MASK; + } + } /*if ( pindex != 0 && hash == komodo_requestedhash ) { fprintf(stderr,"AddToBlockIndex A komodo_requestedhash %s\n",komodo_requestedhash.ToString().c_str()); @@ -4831,7 +4920,7 @@ bool AcceptBlock(int32_t *futureblockp,CBlock& block, CValidationState& state, C { const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); - + CBlockIndex *&pindex = *ppindex; if (!AcceptBlockHeader(futureblockp, block, state, &pindex)) { @@ -4869,7 +4958,7 @@ bool AcceptBlock(int32_t *futureblockp,CBlock& block, CValidationState& state, C if (!fHasMoreWork) return true; // Don't process less-work chains if (fTooFarAhead) return true; // Block height is too high } - + // See method docstring for why this is always disabled auto verifier = libzcash::ProofVerifier::Disabled(); if ((!CheckBlock(futureblockp,pindex->GetHeight(),pindex,block, state, verifier,0)) || !ContextualCheckBlock(block, state, pindex->pprev)) @@ -4902,7 +4991,7 @@ bool AcceptBlock(int32_t *futureblockp,CBlock& block, CValidationState& state, C } catch (const std::runtime_error& e) { return AbortNode(state, std::string("System error: ") + e.what()); } - + if (fCheckForPruning) FlushStateToDisk(state, FLUSH_STATE_NONE); // we just allocated more disk space for block files if ( *futureblockp == 0 ) @@ -5044,16 +5133,11 @@ bool ProcessNewBlock(bool from_miner,int32_t height,CValidationState &state, CNo return error("%s: AcceptBlock FAILED", __func__); //else fprintf(stderr,"added block %s %p\n",pindex->GetBlockHash().ToString().c_str(),pindex->pprev); } - + if (futureblock == 0 && !ActivateBestChain(state, pblock)) return error("%s: ActivateBestChain failed", __func__); //fprintf(stderr,"finished ProcessBlock %d\n",(int32_t)chainActive.LastTip()->GetHeight()); - // when we succeed here, we prune all cheat candidates in the cheat list to 250 blocks ago, as they should be used or not - // useful by then - if ((height - 250) > 1) - cheatList.Prune(height - 200); - return true; } @@ -5061,7 +5145,7 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex { AssertLockHeld(cs_main); assert(pindexPrev == chainActive.Tip()); - + CCoinsViewCache viewNew(pcoinsTip); CBlockIndex indexDummy(block); indexDummy.pprev = pindexPrev; @@ -5122,7 +5206,7 @@ void PruneOneBlockFile(const int fileNumber) pindex->nDataPos = 0; pindex->nUndoPos = 0; setDirtyBlockIndex.insert(pindex); - + // Prune from mapBlocksUnlinked -- any block we prune would have // to be downloaded again in order to consider its chain, at which // point it would be considered as a candidate for @@ -5137,7 +5221,7 @@ void PruneOneBlockFile(const int fileNumber) } } } - + vinfoBlockFile[fileNumber].SetNull(); setDirtyFileInfo.insert(fileNumber); } @@ -5171,21 +5255,21 @@ void FindFilesToPrune(std::set& setFilesToPrune) uint64_t nBuffer = BLOCKFILE_CHUNK_SIZE + UNDOFILE_CHUNK_SIZE; uint64_t nBytesToPrune; int count=0; - + if (nCurrentUsage + nBuffer >= nPruneTarget) { for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) { nBytesToPrune = vinfoBlockFile[fileNumber].nSize + vinfoBlockFile[fileNumber].nUndoSize; - + if (vinfoBlockFile[fileNumber].nSize == 0) continue; - + if (nCurrentUsage + nBuffer < nPruneTarget) // are we below our target? break; - + // don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip but keep scanning if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) continue; - + PruneOneBlockFile(fileNumber); // Queue up the files for removal setFilesToPrune.insert(fileNumber); @@ -5193,7 +5277,7 @@ void FindFilesToPrune(std::set& setFilesToPrune) count++; } } - + LogPrint("prune", "Prune: target=%dMiB actual=%dMiB diff=%dMiB max_prune_height=%d removed %d blk/rev pairs\n", nPruneTarget/1024/1024, nCurrentUsage/1024/1024, ((int64_t)nPruneTarget - (int64_t)nCurrentUsage)/1024/1024, @@ -5203,11 +5287,11 @@ void FindFilesToPrune(std::set& setFilesToPrune) bool CheckDiskSpace(uint64_t nAdditionalBytes) { uint64_t nFreeBytesAvailable = boost::filesystem::space(GetDataDir()).available; - + // Check for nMinDiskSpace bytes (currently 50MB) if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) return AbortNode("Disk space is low!", _("Error: Disk space is low!")); - + return true; } @@ -5257,12 +5341,12 @@ CBlockIndex * InsertBlockIndex(uint256 hash) { if (hash.IsNull()) return NULL; - + // Return existing BlockMap::iterator mi = mapBlockIndex.find(hash); if (mi != mapBlockIndex.end() && mi->second != NULL) return (*mi).second; - + // Create new CBlockIndex* pindexNew = new CBlockIndex(); if (!pindexNew) @@ -5370,7 +5454,7 @@ bool static LoadBlockIndexDB() break; } } - + // Check presence of blk files LogPrintf("Checking all blk files are present...\n"); set setBlkDataFiles; @@ -5390,17 +5474,17 @@ bool static LoadBlockIndexDB() return false; } } - + // Check whether we have ever pruned block & undo files pblocktree->ReadFlag("prunedblockfiles", fHavePruned); if (fHavePruned) LogPrintf("LoadBlockIndexDB(): Block files have previously been pruned\n"); - + // Check whether we need to continue reindexing bool fReindexing = false; pblocktree->ReadReindexing(fReindexing); fReindex |= fReindexing; - + // Check whether we have a transaction index pblocktree->ReadFlag("txindex", fTxIndex); LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled"); @@ -5430,7 +5514,7 @@ bool static LoadBlockIndexDB() } //komodo_pindex_init(pindex,(int32_t)pindex->GetHeight()); } - + // Load pointer to end of best chain BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); if (it == mapBlockIndex.end()) @@ -5452,14 +5536,14 @@ bool static LoadBlockIndexDB() // runs, which makes it return 0, so we guess 50% for now progress = (longestchain > 0 ) ? (double) chainActive.Height() / longestchain : 0.5; } - + LogPrintf("%s: hashBestChain=%s height=%d date=%s progress=%f\n", __func__, chainActive.LastTip()->GetBlockHash().ToString(), chainActive.Height(), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.LastTip()->GetBlockTime()), progress); - + EnforceNodeDeprecation(chainActive.Height(), true); - + return true; } @@ -5478,7 +5562,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth LOCK(cs_main); if (chainActive.Tip() == NULL || chainActive.Tip()->pprev == NULL) return true; - + // Verify blocks in the best chain if (nCheckDepth <= 0) nCheckDepth = 1000000000; // suffices until the year 19000 @@ -5559,7 +5643,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth bool RewindBlockIndex(const CChainParams& params, bool& clearWitnessCaches) { LOCK(cs_main); - + // RewindBlockIndex is called after LoadBlockIndex, so at this point every block // index will have nCachedBranchId set based on the values previously persisted // to disk. By definition, a set nCachedBranchId means that the block was @@ -5577,7 +5661,7 @@ bool RewindBlockIndex(const CChainParams& params, bool& clearWitnessCaches) pindex->nCachedBranchId && *pindex->nCachedBranchId == CurrentEpochBranchId(pindex->GetHeight(), consensus); }; - + int nHeight = 1; while (nHeight <= chainActive.Height()) { if (!sufficientlyValidated(chainActive[nHeight])) { @@ -5585,7 +5669,7 @@ bool RewindBlockIndex(const CChainParams& params, bool& clearWitnessCaches) } nHeight++; } - + // nHeight is now the height of the first insufficiently-validated block, or tipheight + 1 auto rewindLength = chainActive.Height() - nHeight; if (rewindLength > 0 && rewindLength > MAX_REORG_LENGTH) @@ -5607,7 +5691,7 @@ bool RewindBlockIndex(const CChainParams& params, bool& clearWitnessCaches) StartShutdown(); return false; } - + CValidationState state; CBlockIndex* pindex = chainActive.Tip(); while (chainActive.Height() >= nHeight) { @@ -5626,7 +5710,7 @@ bool RewindBlockIndex(const CChainParams& params, bool& clearWitnessCaches) if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC)) return false; } - + // Reduce validity flag and have-data flags. // Collect blocks to be removed (blocks in mapBlockIndex must be at least BLOCK_VALID_TREE). @@ -5635,7 +5719,7 @@ bool RewindBlockIndex(const CChainParams& params, bool& clearWitnessCaches) std::vector vBlocks; for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) { CBlockIndex* pindexIter = it->second; - + // Note: If we encounter an insufficiently validated block that // is on chainActive, it must be because we are a pruning node, and // this block or some successor doesn't HAVE_DATA, so we were unable to @@ -5674,7 +5758,7 @@ bool RewindBlockIndex(const CChainParams& params, bool& clearWitnessCaches) //fprintf(stderr,"Reset invalid block marker if it was pointing to this block\n"); pindexBestInvalid = NULL; } - + // Update indices setBlockIndexCandidates.erase(pindexIter); auto ret = mapBlocksUnlinked.equal_range(pindexIter->pprev); @@ -5689,35 +5773,15 @@ bool RewindBlockIndex(const CChainParams& params, bool& clearWitnessCaches) setBlockIndexCandidates.insert(pindexIter); } } - - /* - // Set pindexBestHeader to the current chain tip - // (since we are about to delete the block it is pointing to) - pindexBestHeader = chainActive.Tip(); - - // Erase block indices on-disk - if (!pblocktree->EraseBatchSync(vBlocks)) { - return AbortNode(state, "Failed to erase from block index database"); - } - - // Erase block indices in-memory - for (auto pindex : vBlocks) { - auto ret = mapBlockIndex.find(*pindex->phashBlock); - if (ret != mapBlockIndex.end()) { - mapBlockIndex.erase(ret); - delete pindex; - } - } - */ PruneBlockIndexCandidates(); - + CheckBlockIndex(); - + if (!FlushStateToDisk(state, FLUSH_STATE_ALWAYS)) { return false; } - + return true; } @@ -5744,7 +5808,7 @@ void UnloadBlockIndex() setDirtyFileInfo.clear(); mapNodeState.clear(); recentRejects.reset(NULL); - + BOOST_FOREACH(BlockMap::value_type& entry, mapBlockIndex) { delete entry.second; } @@ -5769,7 +5833,7 @@ bool LoadBlockIndex() bool InitBlockIndex() { const CChainParams& chainparams = Params(); LOCK(cs_main); - + // Initialize global variables that cannot be constructed at startup. recentRejects.reset(new CRollingBloomFilter(120000, 0.000001)); // Check whether we're already initialized @@ -5787,12 +5851,12 @@ bool InitBlockIndex() { // Use the provided setting for -timestampindex in the new database fTimestampIndex = GetBoolArg("-timestampindex", DEFAULT_TIMESTAMPINDEX); pblocktree->WriteFlag("timestampindex", fTimestampIndex); - + fSpentIndex = GetBoolArg("-spentindex", DEFAULT_SPENTINDEX); pblocktree->WriteFlag("spentindex", fSpentIndex); fprintf(stderr,"fAddressIndex.%d/%d fSpentIndex.%d/%d\n",fAddressIndex,DEFAULT_ADDRESSINDEX,fSpentIndex,DEFAULT_SPENTINDEX); LogPrintf("Initializing databases...\n"); - + // Only add the genesis block if not reindexing (in which case we reuse the one already on disk) if (!fReindex) { try { @@ -5818,7 +5882,7 @@ bool InitBlockIndex() { return error("LoadBlockIndex(): failed to initialize block database: %s", e.what()); } } - + return true; } @@ -5830,7 +5894,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) // Map of disk positions for blocks with unknown parent (only used for reindex) static std::multimap mapBlocksUnknownParent; int64_t nStart = GetTimeMillis(); - + int nLoaded = 0; try { // This takes over fileIn and calls fclose() on it in the CBufferedFile destructor @@ -5839,7 +5903,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) uint64_t nRewind = blkdat.GetPos(); while (!blkdat.eof()) { boost::this_thread::interruption_point(); - + blkdat.SetPos(nRewind); nRewind++; // start one byte further next time, in case of failure blkdat.SetLimit(); // remove former limit @@ -5870,7 +5934,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) CBlock block; blkdat >> block; nRewind = blkdat.GetPos(); - + // detect out of order blocks, and store them for later uint256 hash = block.GetHash(); if (hash != chainparams.GetConsensus().hashGenesisBlock && mapBlockIndex.find(block.hashPrevBlock) == mapBlockIndex.end()) { @@ -5880,7 +5944,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) mapBlocksUnknownParent.insert(std::make_pair(block.hashPrevBlock, *dbp)); continue; } - + // process in case the block isn't known yet if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) { CValidationState state; @@ -5891,7 +5955,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) } else if (hash != chainparams.GetConsensus().hashGenesisBlock && mapBlockIndex[hash]->GetHeight() % 1000 == 0) { LogPrintf("Block Import: already had block %s at height %d\n", hash.ToString(), mapBlockIndex[hash]->GetHeight()); } - + // Recursively process earlier encountered successors of this block deque queue; queue.push_back(hash); @@ -5934,9 +5998,9 @@ void static CheckBlockIndex() if (!fCheckBlockIndex) { return; } - + LOCK(cs_main); - + // During a reindex, we read the genesis block and call CheckBlockIndex before ActivateBestChain, // so we have the genesis block in mapBlockIndex but no active chain. (A few of the tests when // iterating the block tree require that chainActive has been initialized.) @@ -5944,7 +6008,7 @@ void static CheckBlockIndex() assert(mapBlockIndex.size() <= 1); return; } - + // Build forward-pointing map of the entire block tree. std::multimap forward; for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) { @@ -5953,12 +6017,12 @@ void static CheckBlockIndex() } if ( Params().NetworkIDString() != "regtest" ) assert(forward.size() == mapBlockIndex.size()); - + std::pair::iterator,std::multimap::iterator> rangeGenesis = forward.equal_range(NULL); CBlockIndex *pindex = rangeGenesis.first->second; rangeGenesis.first++; assert(rangeGenesis.first == rangeGenesis.second); // There is only one index entry with parent NULL. - + // Iterate over the entire block tree, using depth-first search. // Along the way, remember whether there are blocks on the path from genesis // block being explored which are the first to have certain properties. @@ -5980,7 +6044,7 @@ void static CheckBlockIndex() if (pindex->pprev != NULL && pindexFirstNotTransactionsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_TRANSACTIONS) pindexFirstNotTransactionsValid = pindex; if (pindex->pprev != NULL && pindexFirstNotChainValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_CHAIN) pindexFirstNotChainValid = pindex; if (pindex->pprev != NULL && pindexFirstNotScriptsValid == NULL && (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) pindexFirstNotScriptsValid = pindex; - + // Begin: actual consistency checks. if (pindex->pprev == NULL) { // Genesis block checks. @@ -6066,7 +6130,7 @@ void static CheckBlockIndex() } // assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // Perhaps too slow // End: actual consistency checks. - + // Try descending into the first subnode. std::pair::iterator,std::multimap::iterator> range = forward.equal_range(pindex); if (range.first != range.second) { @@ -6109,7 +6173,7 @@ void static CheckBlockIndex() } } } - + // Check that we actually traversed the entire map. assert(nNodes == forward.size()); } @@ -6124,20 +6188,20 @@ std::string GetWarnings(const std::string& strFor) int nPriority = 0; string strStatusBar; string strRPC; - + if (!CLIENT_VERSION_IS_RELEASE) strStatusBar = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"); - + if (GetBoolArg("-testsafemode", false)) strStatusBar = strRPC = "testsafemode enabled"; - + // Misc warnings like out of disk space and clock is wrong if (strMiscWarning != "") { nPriority = 1000; strStatusBar = strMiscWarning; } - + if (fLargeWorkForkFound) { nPriority = 2000; @@ -6148,7 +6212,7 @@ std::string GetWarnings(const std::string& strFor) nPriority = 2000; strStatusBar = strRPC = _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."); } - + // Alerts { LOCK(cs_mapAlerts); @@ -6165,7 +6229,7 @@ std::string GetWarnings(const std::string& strFor) } } } - + if (strFor == "statusbar") return strStatusBar; else if (strFor == "rpc") @@ -6203,7 +6267,7 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) hashRecentRejectsChainTip = chainActive.Tip()->GetBlockHash(); recentRejects->reset(); } - + return recentRejects->contains(inv.hash) || mempool.exists(inv.hash) || mapOrphanTransactions.count(inv.hash) || @@ -6219,21 +6283,21 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) void static ProcessGetData(CNode* pfrom) { std::deque::iterator it = pfrom->vRecvGetData.begin(); - + vector vNotFound; - + LOCK(cs_main); - + while (it != pfrom->vRecvGetData.end()) { // Don't bother if send buffer is too full to respond anyway if (pfrom->nSendSize >= SendBufferSize()) break; - + const CInv &inv = *it; { boost::this_thread::interruption_point(); it++; - + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) { bool send = false; @@ -6337,17 +6401,17 @@ void static ProcessGetData(CNode* pfrom) vNotFound.push_back(inv); } } - + // Track requests for our stuff. GetMainSignals().Inventory(inv.hash); - + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) break; } } - + pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it); - + if (!vNotFound.empty()) { // Let the peer know that we didn't find what it asked for, so it doesn't // have to wait around forever. Currently only SPV clients actually care @@ -6382,7 +6446,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 1); return false; } - + int64_t nTime; CAddress addrMe; CAddress addrFrom; @@ -6427,7 +6491,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message else pfrom->fRelayTxes = true; - + // Disconnect if we connected to ourself if (nNonce == nLocalHostNonce && nNonce > 1) { @@ -6443,20 +6507,20 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { SeenLocal(addrMe); } - + // Be shy and don't send version until we hear if (pfrom->fInbound) pfrom->PushVersion(); - + pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); - + // Potentially mark this peer as a preferred download peer. UpdatePreferredDownload(pfrom, State(pfrom->GetId())); - + // Change version pfrom->PushMessage("verack"); pfrom->ssSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); - + if (!pfrom->fInbound) { // Advertise our address @@ -6473,7 +6537,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushAddress(addr); } } - + // Get recent addresses if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || addrman.size() < 1000) { @@ -6488,51 +6552,51 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, addrman.Good(addrFrom); } } - + // Relay alerts { LOCK(cs_mapAlerts); BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) item.second.RelayTo(pfrom); } - + pfrom->fSuccessfullyConnected = true; - + string remoteAddr; if (fLogIPs) remoteAddr = ", peeraddr=" + pfrom->addr.ToString(); - + LogPrintf("receive version message: %s: version %d, blocks=%d, us=%s, peer=%d%s\n", pfrom->cleanSubVer, pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString(), pfrom->id, remoteAddr); - + int64_t nTimeOffset = nTime - GetTime(); pfrom->nTimeOffset = nTimeOffset; AddTimeData(pfrom->addr, nTimeOffset); } - - + + else if (pfrom->nVersion == 0) { // Must have a version message before anything else Misbehaving(pfrom->GetId(), 1); return false; } - - + + else if (strCommand == "verack") { pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); - + // Mark this node as currently connected, so we update its timestamp later. if (pfrom->fNetworkNode) { LOCK(cs_main); State(pfrom->GetId())->fCurrentlyConnected = true; } } - - + + // Disconnect existing peer connection when: // 1. The version message has been received // 2. Peer version is below the minimum version for the current epoch @@ -6547,13 +6611,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fDisconnect = true; return false; } - - + + else if (strCommand == "addr") { vector vAddr; vRecv >> vAddr; - + // Don't want addr from older versions unless seeding if (pfrom->nVersion < CADDR_TIME_VERSION && addrman.size() > 1000) return true; @@ -6562,7 +6626,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 20); return error("message addr size() = %u", vAddr.size()); } - + // Store the new addresses vector vAddrOk; int64_t nNow = GetAdjustedTime(); @@ -6570,7 +6634,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, BOOST_FOREACH(CAddress& addr, vAddr) { boost::this_thread::interruption_point(); - + if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) addr.nTime = nNow - 5 * 24 * 60 * 60; pfrom->AddAddressKnown(addr); @@ -6614,8 +6678,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pfrom->fOneShot) pfrom->fDisconnect = true; } - - + + else if (strCommand == "inv") { vector vInv; @@ -6625,24 +6689,24 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 20); return error("message inv size() = %u", vInv.size()); } - + LOCK(cs_main); - + std::vector vToFetch; - + for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { const CInv &inv = vInv[nInv]; - + boost::this_thread::interruption_point(); pfrom->AddInventoryKnown(inv); - + bool fAlreadyHave = AlreadyHave(inv); LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id); - + if (!fAlreadyHave && !fImporting && !fReindex && inv.type != MSG_BLOCK) pfrom->AskFor(inv); - + if (inv.type == MSG_BLOCK) { UpdateBlockAvailability(pfrom->GetId(), inv.hash); if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) { @@ -6666,21 +6730,21 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrint("net", "getheaders (%d) %s to peer=%d\n", pindexBestHeader->GetHeight(), inv.hash.ToString(), pfrom->id); } } - + // Track requests for our stuff GetMainSignals().Inventory(inv.hash); - + if (pfrom->nSendSize > (SendBufferSize() * 2)) { Misbehaving(pfrom->GetId(), 50); return error("send buffer size() = %u", pfrom->nSendSize); } } - + if (!vToFetch.empty()) pfrom->PushMessage("getdata", vToFetch); } - - + + else if (strCommand == "getdata") { vector vInv; @@ -6690,29 +6754,29 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 20); return error("message getdata size() = %u", vInv.size()); } - + if (fDebug || (vInv.size() != 1)) LogPrint("net", "received getdata (%u invsz) peer=%d\n", vInv.size(), pfrom->id); - + if ((fDebug && vInv.size() > 0) || (vInv.size() == 1)) LogPrint("net", "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->id); - + pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end()); ProcessGetData(pfrom); } - - + + else if (strCommand == "getblocks") { CBlockLocator locator; uint256 hashStop; vRecv >> locator >> hashStop; - + LOCK(cs_main); - + // Find the last block the caller has in the main chain CBlockIndex* pindex = FindForkInGlobalIndex(chainActive, locator); - + // Send the rest of the chain if (pindex) pindex = chainActive.Next(pindex); @@ -6736,19 +6800,19 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } } - - + + else if (strCommand == "getheaders") { CBlockLocator locator; uint256 hashStop; vRecv >> locator >> hashStop; - + LOCK(cs_main); - + if (IsInitialBlockDownload()) return true; - + CBlockIndex* pindex = NULL; if (locator.IsNull()) { @@ -6791,23 +6855,23 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, fprintf(stderr,"you can ignore redundant getheaders from peer.%d %d prev.%d\n",(int32_t)pfrom->id,(int32_t)(pindex ? pindex->GetHeight() : -1),pfrom->lasthdrsreq); }*/ } - - + + else if (strCommand == "tx") { vector vWorkQueue; vector vEraseQueue; CTransaction tx; vRecv >> tx; - + CInv inv(MSG_TX, tx.GetHash()); pfrom->AddInventoryKnown(inv); - + LOCK(cs_main); - + bool fMissingInputs = false; CValidationState state; - + pfrom->setAskFor.erase(inv.hash); mapAlreadyAskedFor.erase(inv); @@ -6816,12 +6880,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, mempool.check(pcoinsTip); RelayTransaction(tx); vWorkQueue.push_back(inv.hash); - + LogPrint("mempool", "AcceptToMemoryPool: peer=%d %s: accepted %s (poolsz %u)\n", pfrom->id, pfrom->cleanSubVer, tx.GetHash().ToString(), mempool.mapTx.size()); - + // Recursively process any orphan transactions that depended on this one set setMisbehaving; for (unsigned int i = 0; i < vWorkQueue.size(); i++) @@ -6841,8 +6905,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get // anyone relaying LegitTxX banned) CValidationState stateDummy; - - + + if (setMisbehaving.count(fromPeer)) continue; if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) @@ -6872,7 +6936,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, mempool.check(pcoinsTip); } } - + BOOST_FOREACH(uint256 hash, vEraseQueue) EraseOrphanTx(hash); } @@ -6893,7 +6957,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } else { assert(recentRejects); recentRejects->insert(tx.GetHash()); - + if (pfrom->fWhitelisted) { // Always relay transactions received from whitelisted peers, even // if they were already in the mempool or rejected from it due @@ -6929,7 +6993,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if (strCommand == "headers" && !fImporting && !fReindex) // Ignore headers received while importing { std::vector headers; - + // Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks. unsigned int nCount = ReadCompactSize(vRecv); if (nCount > MAX_HEADERS_RESULTS) { @@ -6941,14 +7005,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vRecv >> headers[n]; ReadCompactSize(vRecv); // ignore tx count; assume it is 0. } - + LOCK(cs_main); - + if (nCount == 0) { // Nothing interesting. Stop asking this peers for more headers. return true; } - + CBlockIndex *pindexLast = NULL; BOOST_FOREACH(const CBlockHeader& header, headers) { //printf("size.%i, solution size.%i\n", (int)sizeof(header), (int)header.nSolution.size()); @@ -6970,10 +7034,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } } - + if (pindexLast) UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); - + if (nCount == MAX_HEADERS_RESULTS && pindexLast) { // Headers message had its maximum size; the peer may have more headers. // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue @@ -6985,20 +7049,20 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256()); } } - + CheckBlockIndex(); } - + else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing { CBlock block; vRecv >> block; - + CInv inv(MSG_BLOCK, block.GetHash()); LogPrint("net", "received block %s peer=%d\n", inv.hash.ToString(), pfrom->id); - + pfrom->AddInventoryKnown(inv); - + CValidationState state; // Process all blocks from whitelisted peers, even if not requested, // unless we're still syncing with the network. @@ -7015,10 +7079,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), nDoS); } } - + } - - + + // This asymmetric behavior for inbound and outbound connections was introduced // to prevent a fingerprinting attack: an attacker can send specific fake addresses // to users' AddrMan and later request them by sending getaddr messages. @@ -7033,18 +7097,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } pfrom->fSentAddr = true; - + pfrom->vAddrToSend.clear(); vector vAddr = addrman.GetAddr(); BOOST_FOREACH(const CAddress &addr, vAddr) pfrom->PushAddress(addr); } - - + + else if (strCommand == "mempool") { LOCK2(cs_main, pfrom->cs_filter); - + std::vector vtxid; mempool.queryHashes(vtxid); vector vInv; @@ -7065,8 +7129,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (vInv.size() > 0) pfrom->PushMessage("inv", vInv); } - - + + else if (strCommand == "ping") { if (pfrom->nVersion > BIP0031_VERSION) @@ -7087,8 +7151,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushMessage("pong", nonce); } } - - + + else if (strCommand == "pong") { int64_t pingUsecEnd = nTimeReceived; @@ -7096,10 +7160,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, size_t nAvail = vRecv.in_avail(); bool bPingFinished = false; std::string sProblem; - + if (nAvail >= sizeof(nonce)) { vRecv >> nonce; - + // Only process pong message if there is an outstanding ping (old ping without nonce should never pong) if (pfrom->nPingNonceSent != 0) { if (nonce == pfrom->nPingNonceSent) { @@ -7131,7 +7195,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, bPingFinished = true; sProblem = "Short payload"; } - + if (!(sProblem.empty())) { LogPrint("net", "pong peer=%d %s: %s, %x expected, %x received, %u bytes\n", pfrom->id, @@ -7145,13 +7209,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->nPingNonceSent = 0; } } - - + + else if (fAlerts && strCommand == "alert") { CAlert alert; vRecv >> alert; - + uint256 alertHash = alert.GetHash(); if (pfrom->setKnown.count(alertHash) == 0) { @@ -7195,7 +7259,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { CBloomFilter filter; vRecv >> filter; - + if (!filter.IsWithinSizeConstraints()) // There is no excuse for sending a too-large filter Misbehaving(pfrom->GetId(), 100); @@ -7208,13 +7272,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } pfrom->fRelayTxes = true; } - - + + else if (strCommand == "filteradd") { vector vData; vRecv >> vData; - + // Nodes must NEVER send a data item > 520 bytes (the max size for a script data object, // and thus, the maximum size any matched object can have) in a filteradd message if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) @@ -7228,8 +7292,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 100); } } - - + + else if (strCommand == "filterclear") { LOCK(pfrom->cs_filter); @@ -7239,18 +7303,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } pfrom->fRelayTxes = true; } - - + + else if (strCommand == "reject") { if (fDebug) { try { string strMsg; unsigned char ccode; string strReason; vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, MAX_REJECT_MESSAGE_LENGTH); - + ostringstream ss; ss << strMsg << " code " << itostr(ccode) << ": " << strReason; - + if (strMsg == "block" || strMsg == "tx") { uint256 hash; @@ -7268,14 +7332,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // We do not care about the NOTFOUND message, but logging an Unknown Command // message would be undesirable as we transmit it ourselves. } - + else { // Ignore unknown commands for extensibility LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id); } - - - + + + return true; } @@ -7284,7 +7348,7 @@ bool ProcessMessages(CNode* pfrom) { //if (fDebug) // LogPrintf("%s(%u messages)\n", __func__, pfrom->vRecvMsg.size()); - + // // Message format // (4) message start @@ -7294,41 +7358,41 @@ bool ProcessMessages(CNode* pfrom) // (x) data // bool fOk = true; - + if (!pfrom->vRecvGetData.empty()) ProcessGetData(pfrom); - + // this maintains the order of responses if (!pfrom->vRecvGetData.empty()) return fOk; - + std::deque::iterator it = pfrom->vRecvMsg.begin(); while (!pfrom->fDisconnect && it != pfrom->vRecvMsg.end()) { // Don't bother if send buffer is too full to respond anyway if (pfrom->nSendSize >= SendBufferSize()) break; - + // get next message CNetMessage& msg = *it; - + //if (fDebug) // LogPrintf("%s(message %u msgsz, %u bytes, complete:%s)\n", __func__, // msg.hdr.nMessageSize, msg.vRecv.size(), // msg.complete() ? "Y" : "N"); - + // end, if an incomplete message is found if (!msg.complete()) break; - + // at this point, any failure means we can delete the current message it++; - + // Scan for message start if (memcmp(msg.hdr.pchMessageStart, Params().MessageStart(), MESSAGE_START_SIZE) != 0) { LogPrintf("PROCESSMESSAGE: INVALID MESSAGESTART %s peer=%d\n", SanitizeString(msg.hdr.GetCommand()), pfrom->id); fOk = false; break; } - + // Read header CMessageHeader& hdr = msg.hdr; if (!hdr.IsValid(Params().MessageStart())) @@ -7337,10 +7401,10 @@ bool ProcessMessages(CNode* pfrom) continue; } string strCommand = hdr.GetCommand(); - + // Message size unsigned int nMessageSize = hdr.nMessageSize; - + // Checksum CDataStream& vRecv = msg.vRecv; uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); @@ -7351,7 +7415,7 @@ bool ProcessMessages(CNode* pfrom) SanitizeString(strCommand), nMessageSize, nChecksum, hdr.nChecksum); continue; } - + // Process message bool fRet = false; try @@ -7385,17 +7449,17 @@ bool ProcessMessages(CNode* pfrom) } catch (...) { PrintExceptionContinue(NULL, "ProcessMessages()"); } - + if (!fRet) LogPrintf("%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->id); - + break; } - + // In case the connection got shut down, its receive buffer was wiped if (!pfrom->fDisconnect) pfrom->vRecvMsg.erase(pfrom->vRecvMsg.begin(), it); - + return fOk; } @@ -7407,7 +7471,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // Don't send anything until we get its version message if (pto->nVersion == 0) return true; - + // // Message: ping // @@ -7436,11 +7500,11 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->PushMessage("ping"); } } - + TRY_LOCK(cs_main, lockMain); // Acquire cs_main for IsInitialBlockDownload() and CNodeState() if (!lockMain) return true; - + // Address refresh broadcast static int64_t nLastRebroadcast; if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60)) @@ -7451,14 +7515,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // Periodically clear addrKnown to allow refresh broadcasts if (nLastRebroadcast) pnode->addrKnown.reset(); - + // Rebroadcast our address AdvertizeLocal(pnode); } if (!vNodes.empty()) nLastRebroadcast = GetTime(); } - + // // Message: addr // @@ -7484,7 +7548,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) if (!vAddr.empty()) pto->PushMessage("addr", vAddr); } - + CNodeState &state = *State(pto->GetId()); if (state.fShouldBan) { if (pto->fWhitelisted) @@ -7500,11 +7564,11 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } state.fShouldBan = false; } - + BOOST_FOREACH(const CBlockReject& reject, state.rejects) pto->PushMessage("reject", (string)"block", reject.chRejectCode, reject.strRejectReason, reject.hashBlock); state.rejects.clear(); - + // Start block sync if (pindexBestHeader == NULL) pindexBestHeader = chainActive.Tip(); @@ -7519,7 +7583,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->PushMessage("getheaders", chainActive.GetLocator(pindexStart), uint256()); } } - + // Resend wallet transactions that haven't gotten in a block yet // Except during reindex, importing and IBD, when old wallet // transactions become unconfirmed and spams other nodes. @@ -7527,7 +7591,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) { GetMainSignals().Broadcast(nTimeBestReceived); } - + // // Message: inventory // @@ -7541,7 +7605,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) { if (pto->setInventoryKnown.count(inv)) continue; - + // trickle out tx inv to protect privacy if (inv.type == MSG_TX && !fSendTrickle) { @@ -7552,14 +7616,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle) uint256 hashRand = ArithToUint256(UintToArith256(inv.hash) ^ UintToArith256(hashSalt)); hashRand = Hash(BEGIN(hashRand), END(hashRand)); bool fTrickleWait = ((UintToArith256(hashRand) & 3) != 0); - + if (fTrickleWait) { vInvWait.push_back(inv); continue; } } - + // returns true if wasn't already contained in the set if (pto->setInventoryKnown.insert(inv).second) { @@ -7575,7 +7639,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } if (!vInv.empty()) pto->PushMessage("inv", vInv); - + // Detect whether we're stalling int64_t nNow = GetTimeMicros(); if (!pto->fDisconnect && state.nStallingSince && state.nStallingSince < nNow - 1000000 * BLOCK_STALLING_TIMEOUT) { @@ -7607,7 +7671,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) pto->fDisconnect = true; } } - + // // Message: getdata (blocks) // @@ -7644,7 +7708,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) komodo_requestedcount = 0; } }*/ - + // // Message: getdata (non-blocks) // @@ -7669,7 +7733,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } if (!vGetData.empty()) pto->PushMessage("getdata", vGetData); - + } return true; } @@ -7690,7 +7754,7 @@ public: for (; it1 != mapBlockIndex.end(); it1++) delete (*it1).second; mapBlockIndex.clear(); - + // orphan transactions mapOrphanTransactions.clear(); mapOrphanTransactionsByPrev.clear(); @@ -7707,7 +7771,7 @@ extern "C" const char* getDataDir() CMutableTransaction CreateNewContextualCMutableTransaction(const Consensus::Params& consensusParams, int nHeight) { CMutableTransaction mtx; - + bool isOverwintered = NetworkUpgradeActive(nHeight, consensusParams, Consensus::UPGRADE_OVERWINTER); if (isOverwintered) { mtx.fOverwintered = true; diff --git a/src/main.h b/src/main.h index 2bfb782a0..ca5e94330 100644 --- a/src/main.h +++ b/src/main.h @@ -28,7 +28,6 @@ #include "tinyformat.h" #include "txmempool.h" #include "uint256.h" -#include "cheatcatcher.h" #include #include @@ -184,11 +183,11 @@ void RegisterNodeSignals(CNodeSignals& nodeSignals); /** Unregister a network node */ void UnregisterNodeSignals(CNodeSignals& nodeSignals); -/** +/** * Process an incoming block. This only returns after the best known valid * block is made active. Note that it does not, however, guarantee that the * specific block passed to it has been checked for validity! - * + * * @param[out] state This may be set to an Error state if any error occurred processing it, including during validation/connection/etc of otherwise unrelated blocks during reorganisation; or it may be set to an Invalid state if pblock is itself invalid (but this is not guaranteed even when the block is checked). If you want to *possibly* get feedback on whether pblock is valid, you must also install a CValidationInterface (see validationinterface.h) - this will have its BlockChecked method called whenever *any* block completes validation. * @param[in] pfrom The node which we are receiving the block from; it is added to mapBlockSource and may be penalised if the block is invalid. * @param[in] pblock The block we want to process. @@ -647,7 +646,7 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF /** * Check transaction inputs, and make sure any * pay-to-script-hash transactions are evaluating IsStandard scripts - * + * * Why bother? To avoid denial-of-service attacks; an attacker * can submit a standard HASH... OP_EQUAL transaction, * which will get accepted into blocks. The redemption @@ -656,14 +655,14 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF * DUP CHECKSIG DROP ... repeated 100 times... OP_1 */ -/** +/** * Check for standard transaction types * @param[in] mapInputs Map of previous transactions that have outputs we're spending * @return True if all inputs (scriptSigs) use only standard transaction forms */ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, uint32_t consensusBranchId); -/** +/** * Count ECDSA signature operations the old-fashioned (pre-0.6) way * @return number of sigops this transaction's outputs will produce when spent * @see CTransaction::FetchInputs @@ -672,7 +671,7 @@ unsigned int GetLegacySigOpCount(const CTransaction& tx); /** * Count ECDSA signature operations in pay-to-script-hash inputs. - * + * * @param[in] mapInputs Map of previous transactions that have outputs we're spending * @return maximum number of sigops required to validate this transaction's inputs * @see CTransaction::FetchInputs @@ -740,9 +739,9 @@ bool IsExpiredTx(const CTransaction &tx, int nBlockHeight); */ bool CheckFinalTx(const CTransaction &tx, int flags = -1); -/** +/** * Closure representing one script verification - * Note that this stores references to the spending transaction + * Note that this stores references to the spending transaction */ class CScriptCheck { diff --git a/src/miner.cpp b/src/miner.cpp index e88ce4ac4..10f95e210 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -10,7 +10,6 @@ #include "amount.h" #include "chainparams.h" -#include "cc/StakeGuard.h" #include "importcoin.h" #include "consensus/consensus.h" #include "consensus/upgrades.h" @@ -124,7 +123,7 @@ extern uint64_t ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS], ASSETCHAINS_TIMELOCKGT extern const char *ASSETCHAINS_ALGORITHMS[]; extern int32_t VERUS_MIN_STAKEAGE, ASSETCHAINS_ALGO, ASSETCHAINS_EQUIHASH, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA, ASSETCHAINS_LWMAPOS, ASSETCHAINS_NONCESHIFT[], ASSETCHAINS_HASHESPERROUND[]; extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; -extern std::string NOTARY_PUBKEY,ASSETCHAINS_OVERRIDE_PUBKEY; +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]; @@ -135,7 +134,7 @@ int32_t komodo_pax_opreturn(int32_t height,uint8_t *opret,int32_t maxsize); int32_t komodo_baseid(char *origbase); int32_t komodo_validate_interest(const CTransaction &tx,int32_t txheight,uint32_t nTime,int32_t dispflag); int64_t komodo_block_unlocktime(uint32_t nHeight); -uint64_t komodo_commission(const CBlock *block); +uint64_t komodo_commission(const CBlock *block,int32_t height); 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); int32_t verus_staked(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &nBits, arith_uint256 &hashResult, uint8_t *utxosig, CPubKey &pk); int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33); @@ -237,94 +236,6 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount, vector vecPriority; vecPriority.reserve(mempool.mapTx.size() + 1); - // check if we should add cheat transaction - CBlockIndex *ppast; - CTransaction cb; - int cheatHeight = nHeight - COINBASE_MATURITY < 1 ? 1 : nHeight - COINBASE_MATURITY; - if (cheatCatcher && - sapling && chainActive.Height() > 100 && - (ppast = chainActive[cheatHeight]) && - ppast->IsVerusPOSBlock() && - cheatList.IsHeightOrGreaterInList(cheatHeight)) - { - // get the block and see if there is a cheat candidate for the stake tx - CBlock b; - if (!(fHavePruned && !(ppast->nStatus & BLOCK_HAVE_DATA) && ppast->nTx > 0) && ReadBlockFromDisk(b, ppast, 1)) - { - CTransaction &stakeTx = b.vtx[b.vtx.size() - 1]; - - if (cheatList.IsCheatInList(stakeTx, &cheatTx)) - { - // make and sign the cheat transaction to spend the coinbase to our address - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(consensusParams, nHeight); - - uint32_t voutNum; - // get the first vout with value - for (voutNum = 0; voutNum < b.vtx[0].vout.size(); voutNum++) - { - if (b.vtx[0].vout[voutNum].nValue > 0) - break; - } - - // send to the same pub key as the destination of this block reward - if (MakeCheatEvidence(mtx, b.vtx[0], voutNum, cheatTx)) - { - extern CWallet *pwalletMain; - LOCK(pwalletMain->cs_wallet); - TransactionBuilder tb = TransactionBuilder(consensusParams, nHeight); - cb = b.vtx[0]; - cbHash = cb.GetHash(); - - bool hasInput = false; - for (uint32_t i = 0; i < cb.vout.size(); i++) - { - // add the spends with the cheat - if (cb.vout[i].nValue > 0) - { - tb.AddTransparentInput(COutPoint(cbHash,i), cb.vout[0].scriptPubKey, cb.vout[0].nValue); - hasInput = true; - } - } - - if (hasInput) - { - // this is a send from a t-address to a sapling address, which we don't have an ovk for. - // Instead, generate a common one from the HD seed. This ensures the data is - // recoverable, at least for us, while keeping it logically separate from the ZIP 32 - // Sapling key hierarchy, which the user might not be using. - uint256 ovk; - HDSeed seed; - if (pwalletMain->GetHDSeed(seed)) { - ovk = ovkForShieldingFromTaddr(seed); - - // send everything to Sapling address - tb.SendChangeTo(cheatCatcher.value(), ovk); - - tb.AddOpRet(mtx.vout[mtx.vout.size() - 1].scriptPubKey); - - cheatSpend = tb.Build(); - } - } - } - } - } - } - - if (cheatSpend) - { - cheatTx = cheatSpend.value(); - std::list removed; - mempool.removeConflicts(cheatTx, removed); - printf("Found cheating stake! Adding cheat spend for %.8f at block #%d, coinbase tx\n%s\n", - (double)cb.GetValueOut() / (double)COIN, nHeight, cheatSpend.value().vin[0].prevout.hash.GetHex().c_str()); - - // add to mem pool and relay - if (myAddtomempool(cheatTx)) - { - RelayTransaction(cheatTx); - } - } - // now add transactions from the mem pool for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi) @@ -573,9 +484,7 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount, { CAmount txfees; - // after Sapling, stake transactions have a fee, but it is recovered in the reward - // this ensures that a rebroadcast goes through quickly to begin staking again - txfees = sapling ? DEFAULT_STAKE_TXFEE : 0; + txfees = 0; pblock->vtx.push_back(txStaked); pblocktemplate->vTxFees.push_back(txfees); @@ -596,27 +505,6 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount, txNew.vout[0].scriptPubKey = scriptPubKeyIn; txNew.vout[0].nValue = GetBlockSubsidy(nHeight,consensusParams) + nFees; - // once we get to Sapling, enable CC StakeGuard for stake transactions - if (isStake && sapling) - { - // if there is a specific destination, use it - CTransaction stakeTx = pblock->vtx[pblock->vtx.size() - 1]; - CStakeParams p; - if (ValidateStakeTransaction(stakeTx, p, false)) - { - if (!p.pk.IsValid() || !MakeGuardedOutput(txNew.vout[0].nValue, p.pk, stakeTx, txNew.vout[0])) - { - fprintf(stderr,"CreateNewBlock: failed to make GuardedOutput on staking coinbase\n"); - return 0; - } - } - else - { - fprintf(stderr,"CreateNewBlock: invalid stake transaction\n"); - return 0; - } - } - txNew.nExpiryHeight = 0; txNew.nLockTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); @@ -647,17 +535,22 @@ CBlockTemplate* CreateNewBlock(const CScript& _scriptPubKeyIn, int32_t gpucount, 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_COMMISSION != 0 && (commission= komodo_commission((CBlock*)&pblocktemplate->block)) != 0 ) + 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 ) { int32_t i; uint8_t *ptr; txNew.vout.resize(2); txNew.vout[1].nValue = commission; - txNew.vout[1].scriptPubKey.resize(35); - ptr = (uint8_t *)&txNew.vout[1].scriptPubKey[0]; - ptr[0] = 33; - for (i=0; i<33; i++) - ptr[i+1] = ASSETCHAINS_OVERRIDE_PUBKEY33[i]; - ptr[34] = OP_CHECKSIG; + if ( ASSETCHAINS_SCRIPTPUB.size() > 1 ) + txNew.vout[1].scriptPubKey = CScript() << ParseHex(ASSETCHAINS_SCRIPTPUB); + else + { + txNew.vout[1].scriptPubKey.resize(35); + ptr = (uint8_t *)&txNew.vout[1].scriptPubKey[0]; + ptr[0] = 33; + for (i=0; i<33; i++) + ptr[i+1] = ASSETCHAINS_OVERRIDE_PUBKEY33[i]; + ptr[34] = OP_CHECKSIG; + } //printf("autocreate commision vout\n"); } @@ -1455,7 +1348,7 @@ void static BitcoinMiner() unsigned int n = chainparams.EquihashN(); unsigned int k = chainparams.EquihashK(); - uint8_t *script; uint64_t total,checktoshis; int32_t i,j,gpucount=KOMODO_MAXGPUCOUNT,notaryid = -1; + uint8_t *script; uint64_t total; int32_t i,j,gpucount=KOMODO_MAXGPUCOUNT,notaryid = -1; while ( (ASSETCHAIN_INIT == 0 || KOMODO_INITDONE == 0) ) { sleep(1); @@ -1648,7 +1541,7 @@ void static BitcoinMiner() { fprintf(stderr,"Mining when blockchain might not be in sync longest.%d vs %d\n",KOMODO_LONGESTCHAIN,Mining_height); if ( KOMODO_LONGESTCHAIN != 0 && Mining_height >= KOMODO_LONGESTCHAIN ) - KOMODO_INSYNC = 1; + KOMODO_INSYNC = Mining_height; sleep(3); } // Hash state @@ -1904,35 +1797,6 @@ void static BitcoinMiner() void GenerateBitcoins(bool fGenerate, int nThreads) #endif { - // if we are supposed to catch stake cheaters, there must be a valid sapling parameter, we need it at - // initialization, and this is the first time we can get it. store the Sapling address here - extern boost::optional cheatCatcher; - extern std::string VERUS_CHEATCATCHER; - libzcash::PaymentAddress addr = DecodePaymentAddress(VERUS_CHEATCATCHER); - if (VERUS_CHEATCATCHER.size() > 0 && IsValidPaymentAddress(addr)) - { - try - { - cheatCatcher = boost::get(addr); - } - catch (...) - { - } - } - if (VERUS_CHEATCATCHER.size() > 0) - { - if (cheatCatcher == boost::none) - { - LogPrintf("ERROR: -cheatcatcher parameter is invalid Sapling payment address\n"); - fprintf(stderr, "-cheatcatcher parameter is invalid Sapling payment address\n"); - } - else - { - LogPrintf("Cheat Catcher active on %s\n", VERUS_CHEATCATCHER.c_str()); - fprintf(stderr, "Cheat Catcher active on %s\n", VERUS_CHEATCATCHER.c_str()); - } - } - static boost::thread_group* minerThreads = NULL; if (nThreads < 0) diff --git a/src/pow.cpp b/src/pow.cpp index 450d9eb1e..f4b18c2ae 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -472,30 +472,7 @@ CChainPower GetBlockProof(const CBlockIndex& block) CBlockHeader header = block.GetBlockHeader(); - // if POS block, add stake - if (!NetworkUpgradeActive(block.GetHeight(), Params().GetConsensus(), Consensus::UPGRADE_SAPLING) || !header.IsVerusPOSBlock()) - { - return CChainPower(0, bnStakeTarget, (~bnWorkTarget / (bnWorkTarget + 1)) + 1); - } - else - { - bnStakeTarget.SetCompact(header.GetVerusPOSTarget(), &fNegative, &fOverflow); - if (fNegative || fOverflow || bnStakeTarget == 0) - return CChainPower(0); - // as the nonce has a fixed definition for a POS block, add the random amount of "work" from the nonce, so there will - // statistically always be a deterministic winner in POS - arith_uint256 aNonce; - - // random amount of additional stake added is capped to 1/2 the current stake target - aNonce = UintToArith256(block.nNonce) | (bnStakeTarget << (uint64_t)1); - - // We need to compute 2**256 / (bnTarget+1), but we can't represent 2**256 - // as it's too large for a arith_uint256. However, as 2**256 is at least as large - // as bnTarget+1, it is equal to ((2**256 - bnTarget - 1) / (bnTarget+1)) + 1, - // or ~bnTarget / (nTarget+1) + 1. - return CChainPower(0, ((~bnStakeTarget / (bnStakeTarget + 1)) + 1) + ((~aNonce / (aNonce + 1)) + 1), - (~bnWorkTarget / (bnWorkTarget + 1)) + 1); - } + return CChainPower(0, bnStakeTarget, (~bnWorkTarget / (bnWorkTarget + 1)) + 1); } int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& from, const CBlockIndex& tip, const Consensus::Params& params) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 280e7a173..a69a4ebb3 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -33,6 +33,8 @@ using namespace std; extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); int32_t komodo_longestchain(); +int32_t komodo_dpowconfs(int32_t height,int32_t numconfs); +extern int32_t KOMODO_LONGESTCHAIN; double GetDifficultyINTERNAL(const CBlockIndex* blockindex, bool networkDifficulty) { @@ -118,7 +120,8 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex) // Only report confirmations if the block is on the main chain if (chainActive.Contains(blockindex)) confirmations = chainActive.Height() - blockindex->GetHeight() + 1; - result.push_back(Pair("confirmations", confirmations)); + result.push_back(Pair("confirmations", komodo_dpowconfs(blockindex->GetHeight(),confirmations))); + result.push_back(Pair("rawconfirmations", confirmations)); result.push_back(Pair("height", blockindex->GetHeight())); result.push_back(Pair("version", blockindex->nVersion)); result.push_back(Pair("merkleroot", blockindex->hashMerkleRoot.GetHex())); @@ -150,7 +153,8 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) } else { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block is an orphan"); } - result.push_back(Pair("confirmations", confirmations)); + result.push_back(Pair("confirmations", komodo_dpowconfs(blockindex->GetHeight(),confirmations))); + result.push_back(Pair("rawconfirmations", confirmations)); result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); result.push_back(Pair("height", blockindex->GetHeight())); result.push_back(Pair("version", block.nVersion)); @@ -267,7 +271,8 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx // Only report confirmations if the block is on the main chain if (chainActive.Contains(blockindex)) confirmations = chainActive.Height() - blockindex->GetHeight() + 1; - result.push_back(Pair("confirmations", confirmations)); + result.push_back(Pair("confirmations", komodo_dpowconfs(blockindex->GetHeight(),confirmations))); + result.push_back(Pair("rawconfirmations", confirmations)); result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); result.push_back(Pair("height", blockindex->GetHeight())); result.push_back(Pair("version", block.nVersion)); @@ -379,6 +384,18 @@ bool myIsutxo_spentinmempool(uint256 txid,int32_t vout) return(false); } +bool mytxid_inmempool(uint256 txid) +{ + BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx) + { + const CTransaction &tx = e.GetTx(); + const uint256 &hash = tx.GetHash(); + if ( txid == hash ) + return(true); + } + return(false); +} + UniValue mempoolToJSON(bool fVerbose = false) { if (fVerbose) @@ -851,7 +868,7 @@ UniValue kvsearch(const UniValue& params, bool fHelp) "}\n" "\nExamples:\n" + HelpExampleCli("kvsearch", "examplekey") - + HelpExampleRpc("kvsearch", "examplekey") + + HelpExampleRpc("kvsearch", "\"examplekey\"") ); LOCK(cs_main); if ( (keylen= (int32_t)strlen(params[0].get_str().c_str())) > 0 ) @@ -1176,7 +1193,11 @@ UniValue gettxout(const UniValue& params, bool fHelp) ret.push_back(Pair("bestblock", pindex->GetBlockHash().GetHex())); if ((unsigned int)coins.nHeight == MEMPOOL_HEIGHT) ret.push_back(Pair("confirmations", 0)); - else ret.push_back(Pair("confirmations", pindex->GetHeight() - coins.nHeight + 1)); + else + { + ret.push_back(Pair("confirmations", komodo_dpowconfs(coins.nHeight,pindex->GetHeight() - coins.nHeight + 1))); + ret.push_back(Pair("rawconfirmations", pindex->GetHeight() - coins.nHeight + 1)); + } ret.push_back(Pair("value", ValueFromAmount(coins.vout[n].nValue))); uint64_t interest; int32_t txheight; uint32_t locktime; if ( (interest= komodo_accrued_interest(&txheight,&locktime,hash,n,coins.nHeight,coins.vout[n].nValue,(int32_t)pindex->GetHeight())) != 0 ) @@ -1333,7 +1354,7 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) if ( ASSETCHAINS_SYMBOL[0] == 0 ) { progress = Checkpoints::GuessVerificationProgress(Params().Checkpoints(), chainActive.LastTip()); } else { - int32_t longestchain = komodo_longestchain(); + int32_t longestchain = KOMODO_LONGESTCHAIN;//komodo_longestchain(); progress = (longestchain > 0 ) ? (double) chainActive.Height() / longestchain : 1.0; } UniValue obj(UniValue::VOBJ); @@ -1404,6 +1425,8 @@ struct CompareBlocksByHeight } }; +#include + UniValue getchaintips(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) @@ -1442,17 +1465,33 @@ UniValue getchaintips(const UniValue& params, bool fHelp) /* Build up a list of chain tips. We start with the list of all known blocks, and successively remove blocks that appear as pprev of another block. */ + /*static pthread_mutex_t mutex; static int32_t didinit; + if ( didinit == 0 ) + { + pthread_mutex_init(&mutex,NULL); + didinit = 1; + } + pthread_mutex_lock(&mutex);*/ std::set setTips; + int32_t n = 0; BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex) + { + n++; setTips.insert(item.second); + } + fprintf(stderr,"iterations getchaintips %d\n",n); + n = 0; BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex) { const CBlockIndex* pprev=0; + n++; if ( item.second != 0 ) pprev = item.second->pprev; if (pprev) setTips.erase(pprev); } + fprintf(stderr,"iterations getchaintips %d\n",n); + //pthread_mutex_unlock(&mutex); // Always report the currently active tip. setTips.insert(chainActive.LastTip()); diff --git a/src/rpc/crosschain.cpp b/src/rpc/crosschain.cpp index 7e113f330..dd604124c 100644 --- a/src/rpc/crosschain.cpp +++ b/src/rpc/crosschain.cpp @@ -50,7 +50,9 @@ UniValue assetchainproof(const UniValue& params, bool fHelp) UniValue crosschainproof(const UniValue& params, bool fHelp) { - + UniValue ret(UniValue::VOBJ); + //fprintf(stderr,"crosschainproof needs to be implemented\n"); + return(ret); } diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index b1bb404d2..27935047c 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -996,7 +996,7 @@ UniValue getblocksubsidy(const UniValue& params, bool fHelp) "}\n" "\nExamples:\n" + HelpExampleCli("getblocksubsidy", "1000") - + HelpExampleRpc("getblockubsidy", "1000") + + HelpExampleRpc("getblocksubsidy", "1000") ); LOCK(cs_main); diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 9588cb67e..9e07c0b1b 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -63,6 +63,7 @@ extern uint32_t ASSETCHAINS_MAGIC; extern uint64_t ASSETCHAINS_COMMISSION,ASSETCHAINS_STAKED,ASSETCHAINS_SUPPLY,ASSETCHAINS_LASTERA; extern int32_t ASSETCHAINS_LWMAPOS; extern uint64_t ASSETCHAINS_ENDSUBSIDY[],ASSETCHAINS_REWARD[],ASSETCHAINS_HALVING[],ASSETCHAINS_DECAY[]; +extern std::string NOTARY_PUBKEY; extern uint8_t NOTARY_PUBKEY33[]; UniValue getinfo(const UniValue& params, bool fHelp) { @@ -125,9 +126,15 @@ UniValue getinfo(const UniValue& params, bool fHelp) #ifdef ENABLE_WALLET if (pwalletMain) { obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); - obj.push_back(Pair("balance", ValueFromAmount(KOMODO_WALLETBALANCE))); //pwalletMain->GetBalance() if ( ASSETCHAINS_SYMBOL[0] == 0 ) - obj.push_back(Pair("interest", ValueFromAmount(KOMODO_INTERESTSUM))); //komodo_interestsum() + { + obj.push_back(Pair("interest", ValueFromAmount(KOMODO_INTERESTSUM))); + obj.push_back(Pair("balance", ValueFromAmount(KOMODO_WALLETBALANCE))); //pwalletMain->GetBalance() + } + else + { + obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); // + } } #endif //fprintf(stderr,"after wallet %u\n",(uint32_t)time(NULL)); @@ -162,16 +169,18 @@ UniValue getinfo(const UniValue& params, bool fHelp) obj.push_back(Pair("pubkey", pubkeystr)); if ( KOMODO_LASTMINED != 0 ) obj.push_back(Pair("lastmined", KOMODO_LASTMINED)); + } else if ( NOTARY_PUBKEY33[0] != 0 ) { + obj.push_back(Pair("pubkey", NOTARY_PUBKEY)); } } if ( ASSETCHAINS_CC != 0 ) obj.push_back(Pair("CCid", (int)ASSETCHAINS_CC)); obj.push_back(Pair("name", ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL)); + obj.push_back(Pair("p2pport", ASSETCHAINS_P2PPORT)); + obj.push_back(Pair("rpcport", ASSETCHAINS_RPCPORT)); if ( ASSETCHAINS_SYMBOL[0] != 0 ) { //obj.push_back(Pair("name", ASSETCHAINS_SYMBOL)); - obj.push_back(Pair("p2pport", ASSETCHAINS_P2PPORT)); - obj.push_back(Pair("rpcport", ASSETCHAINS_RPCPORT)); obj.push_back(Pair("magic", (int)ASSETCHAINS_MAGIC)); obj.push_back(Pair("premine", ASSETCHAINS_SUPPLY)); diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index bc28bc5ea..d922daad4 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -168,42 +168,51 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp) int32_t KOMODO_LONGESTCHAIN; int32_t komodo_longestchain() { + static int32_t depth; int32_t ht,n=0,num=0,maxheight=0,height = 0; - vector vstats; + if ( depth < 0 ) + depth = 0; + if ( depth == 0 ) { - LOCK(cs_main); - CopyNodeStats(vstats); + depth++; + vector vstats; + { + //LOCK(cs_main); + CopyNodeStats(vstats); + } + BOOST_FOREACH(const CNodeStats& stats, vstats) + { + //fprintf(stderr,"komodo_longestchain iter.%d\n",n); + CNodeStateStats statestats; + bool fStateStats = GetNodeStateStats(stats.nodeid,statestats); + if ( statestats.nSyncHeight < 0 ) + continue; + ht = 0; + if ( stats.nStartingHeight > ht ) + ht = stats.nStartingHeight; + if ( statestats.nSyncHeight > ht ) + ht = statestats.nSyncHeight; + if ( statestats.nCommonHeight > ht ) + ht = statestats.nCommonHeight; + if ( maxheight == 0 || ht > maxheight*1.01 ) + maxheight = ht, num = 1; + else if ( ht > maxheight*0.99 ) + num++; + if ( ht > height ) + height = ht; + } + depth--; + if ( num > (n >> 1) ) + { + extern char ASSETCHAINS_SYMBOL[]; + if ( 0 && height != KOMODO_LONGESTCHAIN ) + fprintf(stderr,"set %s KOMODO_LONGESTCHAIN <- %d\n",ASSETCHAINS_SYMBOL,height); + KOMODO_LONGESTCHAIN = height; + return(height); + } + KOMODO_LONGESTCHAIN = 0; } - BOOST_FOREACH(const CNodeStats& stats, vstats) - { - //fprintf(stderr,"komodo_longestchain iter.%d\n",n); - CNodeStateStats statestats; - bool fStateStats = GetNodeStateStats(stats.nodeid,statestats); - ht = 0; - if ( stats.nStartingHeight > ht ) - ht = stats.nStartingHeight; - if ( statestats.nSyncHeight > ht ) - ht = statestats.nSyncHeight; - if ( statestats.nCommonHeight > ht ) - ht = statestats.nCommonHeight; - if ( maxheight == 0 || ht > maxheight*1.01 ) - maxheight = ht, num = 1; - else if ( ht > maxheight*0.99 ) - num++; - n++; - if ( ht > height ) - height = ht; - } - if ( num > (n >> 1) ) - { - extern char ASSETCHAINS_SYMBOL[]; - if ( 0 && height != KOMODO_LONGESTCHAIN ) - fprintf(stderr,"set %s KOMODO_LONGESTCHAIN <- %d\n",ASSETCHAINS_SYMBOL,height); - KOMODO_LONGESTCHAIN = height; - return(height); - } - KOMODO_LONGESTCHAIN = 0; - return(0); + return(KOMODO_LONGESTCHAIN); } UniValue addnode(const UniValue& params, bool fHelp) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index eecfab83c..e6d356f90 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -35,6 +35,7 @@ using namespace std; extern char ASSETCHAINS_SYMBOL[]; +int32_t komodo_dpowconfs(int32_t height,int32_t numconfs); void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex) { @@ -274,7 +275,8 @@ void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, UniValue& if (nConfirmations > 0) { entry.push_back(Pair("height", nHeight)); - entry.push_back(Pair("confirmations", nConfirmations)); + entry.push_back(Pair("confirmations", komodo_dpowconfs(nHeight,nConfirmations))); + entry.push_back(Pair("rawconfirmations", nConfirmations)); entry.push_back(Pair("time", nBlockTime)); entry.push_back(Pair("blocktime", nBlockTime)); } else { @@ -357,7 +359,9 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) if (mi != mapBlockIndex.end() && (*mi).second) { CBlockIndex* pindex = (*mi).second; if (chainActive.Contains(pindex)) { - entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->GetHeight())); + entry.push_back(Pair("height", pindex->GetHeight())); + entry.push_back(Pair("rawconfirmations", 1 + chainActive.Height() - pindex->GetHeight())); + entry.push_back(Pair("confirmations", komodo_dpowconfs(pindex->GetHeight(),1 + chainActive.Height() - pindex->GetHeight()))); entry.push_back(Pair("time", pindex->GetBlockTime())); entry.push_back(Pair("blocktime", pindex->GetBlockTime())); } @@ -543,7 +547,7 @@ int32_t gettxout_scriptPubKey(uint8_t *scriptPubKey,int32_t maxsize,uint256 txid uint256 hashBlock; if ( GetTransaction(txid,tx,hashBlock,false) == 0 ) return(-1); - else if ( n <= tx.vout.size() ) // vout.size() seems off by 1 + else if ( n < tx.vout.size() ) { ptr = (uint8_t *)&tx.vout[n].scriptPubKey[0]; m = tx.vout[n].scriptPubKey.size(); @@ -1155,35 +1159,65 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) // Use CTransaction for the constant parts of the // transaction to avoid rehashing. + CMutableTransaction mergedTxsave = mergedTx; + int32_t txpow,numiters = 0; const CTransaction txConst(mergedTx); - // Sign what we can: - for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { - CTxIn& txin = mergedTx.vin[i]; - const CCoins* coins = view.AccessCoins(txin.prevout.hash); - if (coins == NULL || !coins->IsAvailable(txin.prevout.n)) { - TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); - continue; + if ( (txpow = ASSETCHAINS_TXPOW) != 0 ) + { + if ( txConst.IsCoinBase() != 0 ) + { + if ( (txpow & 2) == 0 ) + txpow == 0; } - const CScript& prevPubKey = CCoinsViewCache::GetSpendFor(coins, txin); - const CAmount& amount = coins->vout[txin.prevout.n].nValue; - - SignatureData sigdata; - // Only sign SIGHASH_SINGLE if there's a corresponding output: - if (!fHashSingle || (i < mergedTx.vout.size())) - ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata, consensusBranchId); - - // ... and merge in other signatures: - BOOST_FOREACH(const CMutableTransaction& txv, txVariants) { - sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i), consensusBranchId); - } - - UpdateTransaction(mergedTx, i, sigdata); - - ScriptError serror = SCRIPT_ERR_OK; - if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), consensusBranchId, &serror)) { - TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); + else + { + if ( (txpow & 1) == 0 ) + txpow == 0; } } + while ( 1 ) + { + if ( txpow != 0 ) + mergedTx = mergedTxsave; + // Sign what we can: + for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { + CTxIn& txin = mergedTx.vin[i]; + const CCoins* coins = view.AccessCoins(txin.prevout.hash); + if (coins == NULL || !coins->IsAvailable(txin.prevout.n)) { + TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); + continue; + } + const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey; + const CAmount& amount = coins->vout[txin.prevout.n].nValue; + + SignatureData sigdata; + // Only sign SIGHASH_SINGLE if there's a corresponding output: + if (!fHashSingle || (i < mergedTx.vout.size())) + ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata, consensusBranchId); + + // ... and merge in other signatures: + BOOST_FOREACH(const CMutableTransaction& txv, txVariants) { + sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i), consensusBranchId); + } + + UpdateTransaction(mergedTx, i, sigdata); + + ScriptError serror = SCRIPT_ERR_OK; + if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), consensusBranchId, &serror)) { + TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); + } + } + if ( txpow != 0 ) + { + uint256 txid = mergedTx.GetHash(); + if ( ((uint8_t *)&txid)[0] == 0 && ((uint8_t *)&txid)[31] == 0 ) + break; + //fprintf(stderr,"%d: tmp txid.%s\n",numiters,txid.GetHex().c_str()); + } else break; + numiters++; + } + if ( numiters > 0 ) + fprintf(stderr,"ASSETCHAINS_TXPOW.%d txpow.%d numiters.%d for signature\n",ASSETCHAINS_TXPOW,txpow,numiters); bool fComplete = vErrors.empty(); UniValue result(UniValue::VOBJ); diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index c8f500ca7..70a124b8c 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -249,7 +249,7 @@ void GenerateBitcoins(bool b, CWallet *pw); UniValue stop(const UniValue& params, bool fHelp) { - char buf[64]; + char buf[66+128]; // Accept the deprecated and ignored 'detach' boolean argument if (fHelp || params.size() > 1) throw runtime_error( @@ -264,7 +264,7 @@ UniValue stop(const UniValue& params, bool fHelp) // Shutdown will take long enough that the response should get back StartShutdown(); - sprintf(buf,"%s Komodo server stopping",ASSETCHAINS_SYMBOL); + sprintf(buf,"%s server stopping",ASSETCHAINS_SYMBOL[0] != 0 ? ASSETCHAINS_SYMBOL : "Komodo"); return buf; } @@ -361,7 +361,7 @@ static const CRPCCommand vRPCCommands[] = #ifdef ENABLE_WALLET { "rawtransactions", "fundrawtransaction", &fundrawtransaction, false }, #endif -/* + // auction { "auction", "auctionaddress", &auctionaddress, true }, @@ -388,17 +388,16 @@ static const CRPCCommand vRPCCommands[] = { "faucet", "faucetfund", &faucetfund, true }, { "faucet", "faucetget", &faucetget, true }, { "faucet", "faucetaddress", &faucetaddress, true }, - - // MofN - { "MofN", "mofnaddress", &mofnaddress, true }, - + + // Heir + { "heir", "heiraddress", &heiraddress, true }, + // Channels { "channels", "channelsaddress", &channelsaddress, true }, { "channels", "channelsinfo", &channelsinfo, true }, { "channels", "channelsopen", &channelsopen, true }, { "channels", "channelspayment", &channelspayment, true }, - { "channels", "channelscollect", &channelscollect, true }, - { "channels", "channelsstop", &channelsstop, true }, + { "channels", "channelsclose", &channelsclose, true }, { "channels", "channelsrefund", &channelsrefund, true }, // Oracles @@ -410,19 +409,26 @@ static const CRPCCommand vRPCCommands[] = { "oracles", "oraclessubscribe", &oraclessubscribe, true }, { "oracles", "oraclesdata", &oraclesdata, true }, { "oracles", "oraclessamples", &oraclessamples, true }, - + // Prices - { "prices", "pricesaddress", &pricesaddress, true }, - + { "prices", "pricesaddress", &pricesaddress, true }, + { "prices", "priceslist", &priceslist, true }, + { "prices", "pricesinfo", &pricesinfo, true }, + { "prices", "pricescreate", &pricescreate, true }, + { "prices", "pricesaddfunding", &pricesaddfunding, true }, + { "prices", "pricesbet", &pricesbet, true }, + { "prices", "pricesstatus", &pricesstatus, true }, + { "prices", "pricesfinish", &pricesfinish, true }, + // Pegs { "pegs", "pegsaddress", &pegsaddress, true }, - + // Triggers { "triggers", "triggersaddress", &triggersaddress, true }, - + // Payments { "payments", "paymentsaddress", &paymentsaddress, true }, - + // Gateways { "gateways", "gatewaysaddress", &gatewaysaddress, true }, { "gateways", "gatewayslist", &gatewayslist, true }, @@ -432,7 +438,9 @@ static const CRPCCommand vRPCCommands[] = { "gateways", "gatewaysclaim", &gatewaysclaim, true }, { "gateways", "gatewayswithdraw", &gatewayswithdraw, true }, { "gateways", "gatewayspending", &gatewayspending, true }, + { "gateways", "gatewaysmultisig", &gatewaysmultisig, true }, { "gateways", "gatewaysmarkdone", &gatewaysmarkdone, true }, + { "gateways", "gatewayspartialsign", &gatewayspartialsign, true }, // dice { "dice", "dicelist", &dicelist, true }, @@ -460,7 +468,7 @@ static const CRPCCommand vRPCCommands[] = { "tokens", "tokencancelask", &tokencancelask, true }, { "tokens", "tokenfillask", &tokenfillask, true }, //{ "tokens", "tokenfillswap", &tokenfillswap, true }, -*/ + /* Address index */ { "addressindex", "getaddressmempool", &getaddressmempool, true }, { "addressindex", "getaddressutxos", &getaddressutxos, false }, @@ -524,6 +532,7 @@ static const CRPCCommand vRPCCommands[] = { "wallet", "sendmany", &sendmany, false }, { "wallet", "sendtoaddress", &sendtoaddress, false }, { "wallet", "setaccount", &setaccount, true }, + { "wallet", "setpubkey", &setpubkey, true }, { "wallet", "settxfee", &settxfee, true }, { "wallet", "signmessage", &signmessage, true }, { "wallet", "walletlock", &walletlock, true }, diff --git a/src/rpc/server.h b/src/rpc/server.h index bd204801c..65a36706e 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -236,7 +236,8 @@ extern UniValue tokenfillbid(const UniValue& params, bool fHelp); extern UniValue tokenask(const UniValue& params, bool fHelp); extern UniValue tokencancelask(const UniValue& params, bool fHelp); extern UniValue tokenfillask(const UniValue& params, bool fHelp); -extern UniValue mofnaddress(const UniValue& params, bool fHelp); +extern UniValue tokenconvert(const UniValue& params, bool fHelp); +extern UniValue heiraddress(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); @@ -247,6 +248,13 @@ extern UniValue oraclessubscribe(const UniValue& params, bool fHelp); extern UniValue oraclesdata(const UniValue& params, bool fHelp); extern UniValue oraclessamples(const UniValue& params, bool fHelp); extern UniValue pricesaddress(const UniValue& params, bool fHelp); +extern UniValue priceslist(const UniValue& params, bool fHelp); +extern UniValue pricesinfo(const UniValue& params, bool fHelp); +extern UniValue pricescreate(const UniValue& params, bool fHelp); +extern UniValue pricesaddfunding(const UniValue& params, bool fHelp); +extern UniValue pricesbet(const UniValue& params, bool fHelp); +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 triggersaddress(const UniValue& params, bool fHelp); extern UniValue paymentsaddress(const UniValue& params, bool fHelp); @@ -259,12 +267,12 @@ extern UniValue gatewaysclaim(const UniValue& params, bool fHelp); extern UniValue gatewayswithdraw(const UniValue& params, bool fHelp); extern UniValue gatewayspending(const UniValue& params, bool fHelp); extern UniValue gatewaysmarkdone(const UniValue& params, bool fHelp); +extern UniValue gatewaysmultisig(const UniValue& params, bool fHelp); +extern UniValue gatewayspartialsign(const UniValue& params, bool fHelp); extern UniValue channelsinfo(const UniValue& params, bool fHelp); -extern UniValue channelsbind(const UniValue& params, bool fHelp); extern UniValue channelsopen(const UniValue& params, bool fHelp); extern UniValue channelspayment(const UniValue& params, bool fHelp); -extern UniValue channelscollect(const UniValue& params, bool fHelp); -extern UniValue channelsstop(const UniValue& params, bool fHelp); +extern UniValue channelsclose(const UniValue& params, bool fHelp); extern UniValue channelsrefund(const UniValue& params, bool fHelp); //extern UniValue tokenswapask(const UniValue& params, bool fHelp); @@ -330,6 +338,7 @@ extern UniValue walletlock(const UniValue& params, bool fHelp); extern UniValue encryptwallet(const UniValue& params, bool fHelp); extern UniValue validateaddress(const UniValue& params, bool fHelp); extern UniValue getinfo(const UniValue& params, bool fHelp); +extern UniValue setpubkey(const UniValue& params, bool fHelp); extern UniValue getwalletinfo(const UniValue& params, bool fHelp); extern UniValue getblockchaininfo(const UniValue& params, bool fHelp); extern UniValue getnetworkinfo(const UniValue& params, bool fHelp); diff --git a/src/script/sign.cpp b/src/script/sign.cpp index ec7d89dac..3bc49ef76 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -18,7 +18,8 @@ using namespace std; -typedef std::vector valtype; +typedef vector valtype; +extern uint8_t ASSETCHAINS_TXPOW; TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} @@ -48,10 +49,20 @@ bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, } else { - if (!key.Sign(hash, vchSig)) - return false; + if ( ASSETCHAINS_TXPOW == 0 ) + { + if (!key.Sign(hash, vchSig)) + return false; + } + else + { + if (!key.Sign(hash, vchSig, rand())) + return false; + } } + vchSig.push_back((unsigned char)nHashType); + return true; } @@ -97,11 +108,6 @@ CC *CCcond1(uint8_t evalcode,CPubKey pk) return CCNewThreshold(2, {condCC, Sig}); } -// TODO: these are redundant and should be cleaned up to one file -std::string _StakeGuardAddr = "RCG8KwJNDVwpUBcdoa6AoHqHVJsA1uMYMR"; -std::string _StakeGuardPubKey = "03166b7813a4855a88e9ef7340a692ef3c2decedfdc2c7563ec79537e89667d935"; -std::string _StakeGuardWIF = "Uw7vRYHGKjyi1FaJ8Lv1USSuj7ntUti8fAhSDiCdbzuV6yDagaTn"; - std::vector &GetCryptoConditions() { static bool initialized = false; @@ -110,14 +116,7 @@ std::vector &GetCryptoConditions() if (!initialized) { - C.evalcode = EVAL_STAKEGUARD; - strcpy(C.unspendableCCaddr,_StakeGuardAddr.c_str()); - strcpy(C.normaladdr,_StakeGuardAddr.c_str()); - strcpy(C.CChexstr,_StakeGuardPubKey.c_str()); - memcpy(C.CCpriv, DecodeSecret(_StakeGuardWIF).begin(),32); - vCC.push_back(C); - - initialized = true; + // this should initialize any desired auto-signed crypto-conditions } return vCC; } diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 67aa0f28f..3b57afd9d 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -371,7 +371,7 @@ bool ExtractDestination(const CScript& _scriptPubKey, CTxDestination& addressRet CPubKey pubKey(vSolutions[0]); if (!pubKey.IsValid()) { - fprintf(stderr,"TX_PUBKEY invalid pubkey\n"); + //fprintf(stderr,"TX_PUBKEY invalid pubkey\n"); return false; } diff --git a/src/wallet-utility.cpp b/src/wallet-utility.cpp index 8f3fe03ab..7af2ca5bf 100644 --- a/src/wallet-utility.cpp +++ b/src/wallet-utility.cpp @@ -23,8 +23,6 @@ int32_t ASSETCHAINS_LWMAPOS = 0; int32_t VERUS_BLOCK_POSUNITS = 1000; int32_t ASSETCHAINS_OVERWINTER = 227520; int32_t ASSETCHAINS_SAPLING = 227520; -boost::optional cheatCatcher; -std::string VERUS_CHEATCATCHER = ""; unsigned int MAX_BLOCK_SIGOPS = 20000; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e11e0c520..233553afb 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -54,6 +54,8 @@ const std::string ADDR_TYPE_SAPLING = "sapling"; extern UniValue TxJoinSplitToJSON(const CTransaction& tx); extern uint8_t ASSETCHAINS_PRIVATE; 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 int64_t nWalletUnlockTime; static CCriticalSection cs_nWalletUnlockTime; @@ -96,16 +98,18 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) { //int32_t i,n,txheight; uint32_t locktime; uint64_t interest = 0; int confirms = wtx.GetDepthInMainChain(); - entry.push_back(Pair("confirmations", confirms)); + entry.push_back(Pair("rawconfirmations", confirms)); if (wtx.IsCoinBase()) entry.push_back(Pair("generated", true)); if (confirms > 0) { + entry.push_back(Pair("confirmations", komodo_dpowconfs((int32_t)mapBlockIndex[wtx.hashBlock]->GetHeight(),confirms))); entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex())); entry.push_back(Pair("blockindex", wtx.nIndex)); entry.push_back(Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime())); entry.push_back(Pair("expiryheight", (int64_t)wtx.nExpiryHeight)); - } + } else entry.push_back(Pair("confirmations", confirms)); + uint256 hash = wtx.GetHash(); entry.push_back(Pair("txid", hash.GetHex())); UniValue conflicts(UniValue::VARR); @@ -438,6 +442,8 @@ static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtr throw JSONRPCError(RPC_WALLET_ERROR, "Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); } +int32_t komodo_isnotaryvout(char *coinaddr); + UniValue sendtoaddress(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) @@ -468,8 +474,12 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp) ); if ( ASSETCHAINS_PRIVATE != 0 && AmountFromValue(params[1]) > 0 ) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid " + strprintf("%s",komodo_chainname()) + " address"); - + { + if ( komodo_isnotaryvout((char *)params[0].get_str().c_str()) == 0 ) + { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid " + strprintf("%s",komodo_chainname()) + " address"); + } + } LOCK2(cs_main, pwalletMain->cs_wallet); CTxDestination dest = DecodeDestination(params[0].get_str()); @@ -552,7 +562,7 @@ UniValue kvupdate(const UniValue& params, bool fHelp) "}\n" "\nExamples:\n" + HelpExampleCli("kvupdate", "examplekey \"examplevalue\" 2 examplepassphrase") - + HelpExampleRpc("kvupdate", "examplekey \"examplevalue\" 2 examplepassphrase") + + HelpExampleRpc("kvupdate", "\"examplekey\",\"examplevalue\",\"2\",\"examplepassphrase\"") ); if (!EnsureWalletIsAvailable(fHelp)) return 0; @@ -1268,13 +1278,13 @@ UniValue sendmany(const UniValue& params, bool fHelp) " the number of addresses.\n" "\nExamples:\n" "\nSend two amounts to two different addresses:\n" - + HelpExampleCli("sendmany", "\"\" \"{\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\":0.01,\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\":0.02}\"") + + + HelpExampleCli("sendmany", "\"\" \"{\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPVMY\\\":0.01,\\\"RRyyejME7LRTuvdziWsXkAbSW1fdiohGwK\\\":0.02}\"") + "\nSend two amounts to two different addresses setting the confirmation and comment:\n" - + HelpExampleCli("sendmany", "\"\" \"{\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\":0.01,\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\":0.02}\" 6 \"testing\"") + + + HelpExampleCli("sendmany", "\"\" \"{\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPVMY\\\":0.01,\\\"RRyyejME7LRTuvdziWsXkAbSW1fdiohGwK\\\":0.02}\" 6 \"testing\"") + "\nSend two amounts to two different addresses, subtract fee from amount:\n" - + HelpExampleCli("sendmany", "\"\" \"{\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\":0.01,\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\":0.02}\" 1 \"\" \"[\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\",\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\"]\"") + + + HelpExampleCli("sendmany", "\"\" \"{\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPVMY\\\":0.01,\\\"RRyyejME7LRTuvdziWsXkAbSW1fdiohGwK\\\":0.02}\" 1 \"\" \"[\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPVMY\\\",\\\"RRyyejME7LRTuvdziWsXkAbSW1fdiohGwK\\\"]\"") + "\nAs a json rpc call\n" - + HelpExampleRpc("sendmany", "\"\", \"{\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\":0.01,\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\":0.02}\", 6, \"testing\"") + + HelpExampleRpc("sendmany", "\"\", {\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPVMY\":0.01,\"RRyyejME7LRTuvdziWsXkAbSW1fdiohGwK\":0.02}, 6, \"testing\"") ); if ( ASSETCHAINS_PRIVATE != 0 ) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "cant use transparent addresses in private chain"); @@ -1613,8 +1623,6 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) } } -bool ValidateStakeTransaction(const CTransaction &stakeTx, CStakeParams &stakeParams, bool validateSig = true); - void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter) { CAmount nFee; @@ -1622,21 +1630,9 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe list listReceived; list listSent; - CStakeParams p; - bool bIsStake = false; - bool bIsCoinbase = false; - bool bIsMint = false; - if (ValidateStakeTransaction(wtx, p, false)) - { - bIsStake = true; - } - else - { - bIsCoinbase = wtx.IsCoinBase(); - bIsMint = bIsCoinbase && wtx.vout.size() > 0 && wtx.vout[0].scriptPubKey.IsPayToCryptoCondition(); - } + bool bIsCoinbase = wtx.IsCoinBase(); - wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, bIsStake ? ISMINE_ALLANDCHANGE : filter); + wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, filter); bool fAllAccounts = (strAccount == string("*")); bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY); @@ -1651,7 +1647,7 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe entry.push_back(Pair("involvesWatchonly", true)); entry.push_back(Pair("account", strSentAccount)); MaybePushAddress(entry, s.destination); - entry.push_back(Pair("category", bIsStake ? "stake" : "send")); + entry.push_back(Pair("category", "send")); entry.push_back(Pair("amount", ValueFromAmount(-s.amount))); entry.push_back(Pair("vout", s.vout)); entry.push_back(Pair("fee", ValueFromAmount(-nFee))); @@ -1694,11 +1690,11 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe entry.push_back(Pair("blockstomaturity", btm)); } else - entry.push_back(Pair("category", bIsMint ? "mint" : "generate")); + entry.push_back(Pair("category", "generate")); } else { - entry.push_back(Pair("category", bIsStake ? "stake" : "receive")); + entry.push_back(Pair("category", "receive")); } entry.push_back(Pair("amount", ValueFromAmount(r.amount))); @@ -2730,7 +2726,9 @@ UniValue listunspent(const UniValue& params, bool fHelp) if (destinations.size() && (!fValidAddress || !destinations.count(address))) continue; - UniValue entry(UniValue::VOBJ); + CAmount nValue = out.tx->vout[out.i].nValue; + const CScript& pk = out.tx->vout[out.i].scriptPubKey; + UniValue entry(UniValue::VOBJ); int32_t txheight = 0; entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); entry.push_back(Pair("vout", out.i)); entry.push_back(Pair("generated", out.tx->IsCoinBase())); @@ -2748,13 +2746,12 @@ UniValue listunspent(const UniValue& params, bool fHelp) entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); } } - CAmount nValue = out.tx->vout[out.i].nValue; - entry.push_back(Pair("amount", ValueFromAmount(out.tx->vout[out.i].nValue))); + entry.push_back(Pair("amount", ValueFromAmount(nValue))); if ( out.tx->nLockTime != 0 ) { BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); CBlockIndex *tipindex,*pindex = it->second; - uint64_t interest; uint32_t locktime; int32_t txheight; + uint64_t interest; uint32_t locktime; if ( pindex != 0 && (tipindex= chainActive.LastTip()) != 0 ) { interest = komodo_accrued_interest(&txheight,&locktime,out.tx->GetHash(),out.i,0,nValue,(int32_t)tipindex->GetHeight()); @@ -2763,19 +2760,21 @@ UniValue listunspent(const UniValue& params, bool fHelp) } //fprintf(stderr,"nValue %.8f pindex.%p tipindex.%p locktime.%u txheight.%d pindexht.%d\n",(double)nValue/COIN,pindex,chainActive.LastTip(),locktime,txheight,pindex->GetHeight()); } + else if ( chainActive.LastTip() != 0 ) + txheight = (chainActive.LastTip()->GetHeight() - out.nDepth - 1); entry.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); - entry.push_back(Pair("confirmations", out.nDepth)); + entry.push_back(Pair("rawconfirmations",out.nDepth)); + entry.push_back(Pair("confirmations",komodo_dpowconfs(txheight,out.nDepth))); entry.push_back(Pair("spendable", out.fSpendable)); results.push_back(entry); } - return results; } uint64_t komodo_interestsum() { #ifdef ENABLE_WALLET - if ( GetBoolArg("-disablewallet", false) == 0 ) + if ( ASSETCHAINS_SYMBOL[0] == 0 && GetBoolArg("-disablewallet", false) == 0 ) { uint64_t interest,sum = 0; int32_t txheight; uint32_t locktime; vector vecOutputs; @@ -4114,7 +4113,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ")+address ); } } - else if ( ASSETCHAINS_PRIVATE != 0 ) + else if ( ASSETCHAINS_PRIVATE != 0 && komodo_isnotaryvout((char *)address.c_str()) == 0 ) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "cant use transparent addresses in private chain"); if (setAddress.count(address)) @@ -4950,13 +4949,16 @@ int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex); extern std::string NOTARY_PUBKEY; uint32_t komodo_stake(int32_t validateflag,arith_uint256 bnTarget,int32_t nHeight,uint256 hash,int32_t n,uint32_t blocktime,uint32_t prevtime,char *destaddr); int8_t komodo_stakehash(uint256 *hashp,char *address,uint8_t *hashbuf,uint256 txid,int32_t vout); -int32_t komodo_segids(uint8_t *hashbuf,int32_t height,int32_t n); +void komodo_segids(uint8_t *hashbuf,int32_t height,int32_t n); int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33) { set setAddress; uint8_t *script,utxosig[128]; uint256 utxotxid; uint64_t utxovalue; int32_t i,siglen=0,nMinDepth = 1,nMaxDepth = 9999999; vector vecOutputs; uint32_t utxovout,eligible,earliest = 0; CScript best_scriptPubKey; bool fNegative,fOverflow; bool signSuccess; SignatureData sigdata; uint64_t txfee; uint8_t *ptr; auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); + if (!EnsureWalletIsAvailable(0)) + return 0; + const CKeyStore& keystore = *pwalletMain; assert(pwalletMain != NULL); LOCK2(cs_main, pwalletMain->cs_wallet); @@ -5117,6 +5119,9 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt { static struct komodo_staking *array; static int32_t numkp,maxkp; static uint32_t lasttime; set setAddress; struct komodo_staking *kp; int32_t winners,segid,minage,nHeight,counter=0,i,m,siglen=0,nMinDepth = 1,nMaxDepth = 99999999; vector 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; + if (!EnsureWalletIsAvailable(0)) + return 0; + bnTarget.SetCompact(nBits, &fNegative, &fOverflow); mindiff.SetCompact(KOMODO_MINDIFF_NBITS,&fNegative,&fOverflow); ratio = (mindiff / bnTarget); @@ -5294,6 +5299,7 @@ int32_t ensure_CCrequirements() #include "../cc/CCchannels.h" #include "../cc/CCOracles.h" #include "../cc/CCGateways.h" +#include "../cc/CCPrices.h" UniValue CCaddress(struct CCcontract_info *cp,char *name,std::vector &pubkey) { @@ -5330,9 +5336,73 @@ UniValue CCaddress(struct CCcontract_info *cp,char *name,std::vectorcs_wallet : NULL); +#else + LOCK(cs_main); +#endif + + char Raddress[18]; + uint8_t pubkey33[33]; + extern uint8_t NOTARY_PUBKEY33[]; + extern std::string NOTARY_PUBKEY; + if ( NOTARY_PUBKEY33[0] == 0 ) { + if (strlen(params[0].get_str().c_str()) == 66) { + decode_hex(pubkey33,33,(char *)params[0].get_str().c_str()); + pubkey2addr((char *)Raddress,(uint8_t *)pubkey33); + if (strcmp("RRmWExvapDM9YbLT9X9xAyzDgxomYf63ng",Raddress) == 0) { + result.push_back(Pair("error", "pubkey entered is invalid.")); + } else { + CBitcoinAddress address(Raddress); + bool isValid = address.IsValid(); + if (isValid) + { + CTxDestination dest = address.Get(); + string currentAddress = address.ToString(); + result.push_back(Pair("address", currentAddress)); +#ifdef ENABLE_WALLET + isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : ISMINE_NO; + result.push_back(Pair("ismine", (mine & ISMINE_SPENDABLE) ? true : false)); +#endif + } + NOTARY_PUBKEY = params[0].get_str(); + decode_hex(NOTARY_PUBKEY33,33,(char *)NOTARY_PUBKEY.c_str()); + } + } else { + result.push_back(Pair("error", "pubkey is wrong length, must be 66 char hex string.")); + } + } else { + result.push_back(Pair("error", "Can only set pubkey once, to change it you need to restart your daemon.")); + } + result.push_back(Pair("pubkey", NOTARY_PUBKEY)); + return result; +} + UniValue channelsaddress(const UniValue& params, bool fHelp) { - UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; std::vector destpubkey; CPubKey pk,pk2; char destaddr[64]; + UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; std::vector destpubkey; CPubKey pk,pk2; char destaddr[64]; cp = CCinit(&C,EVAL_CHANNELS); if ( fHelp || params.size() != 1 ) throw runtime_error("channelsaddress destpubkey\n"); @@ -5345,6 +5415,15 @@ UniValue channelsaddress(const UniValue& params, bool fHelp) result.push_back(Pair("otherpubkey", params[0].get_str())); GetCCaddress1of2(cp,destaddr,pk,pk2); result.push_back(Pair("channeladdress",destaddr)); + if ( 0 ) + { + int32_t i; + for (i=0; i<100; i++) + { + GetCCaddress1of2(cp,destaddr,pk,pk2); + fprintf(stderr,"i.%d %s\n",i,destaddr); + } + } return(result); } @@ -5363,15 +5442,25 @@ UniValue oraclesaddress(const UniValue& params, bool fHelp) UniValue pricesaddress(const UniValue& params, bool fHelp) { - struct CCcontract_info *cp,C; std::vector pubkey; + UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C,*assetscp,C2; std::vector pubkey; CPubKey mypk,planpk,pricespk; char myaddr[64],houseaddr[64],exposureaddr[64]; cp = CCinit(&C,EVAL_PRICES); + assetscp = CCinit(&C2,EVAL_PRICES); if ( fHelp || params.size() > 1 ) throw runtime_error("pricesaddress [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 *)"Prices",pubkey)); + result = CCaddress(cp,(char *)"Prices",pubkey); + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + GetCCaddress(assetscp,myaddr,mypk); + GetCCaddress1of2(assetscp,houseaddr,pricespk,planpk); + GetCCaddress1of2(assetscp,exposureaddr,pricespk,pricespk); + result.push_back(Pair("myaddr",myaddr)); // for holding my asssets + result.push_back(Pair("houseaddr",houseaddr)); // globally accessible house assets + result.push_back(Pair("exposureaddr",exposureaddr)); // tracking of exposure + return(result); } UniValue pegsaddress(const UniValue& params, bool fHelp) @@ -5426,17 +5515,17 @@ UniValue gatewaysaddress(const UniValue& params, bool fHelp) return(CCaddress(cp,(char *)"Gateways",pubkey)); } -UniValue mofnaddress(const UniValue& params, bool fHelp) +UniValue heiraddress(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; std::vector pubkey; - cp = CCinit(&C,EVAL_MOFN); + cp = CCinit(&C,EVAL_HEIR); if ( fHelp || params.size() > 1 ) - throw runtime_error("mofnaddress [pubkey]\n"); + 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 *)"MofN",pubkey)); + return(CCaddress(cp,(char *)"Heir",pubkey)); } UniValue lottoaddress(const UniValue& params, bool fHelp) @@ -5534,11 +5623,15 @@ UniValue tokenaddress(const UniValue& params, bool fHelp) UniValue channelsinfo(const UniValue& params, bool fHelp) { - if ( fHelp || params.size() != 0 ) - throw runtime_error("channelsinfo\n"); + uint256 opentxid; + if ( fHelp || params.size() > 1 ) + throw runtime_error("channelsinfo [opentxid]\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(ChannelsInfo()); + opentxid=zeroid; + if (params.size() > 0 && !params[0].isNull() && !params[0].get_str().empty()) + opentxid = Parseuint256((char *)params[0].get_str().c_str()); + return(ChannelsInfo(opentxid)); } UniValue channelsopen(const UniValue& params, bool fHelp) @@ -5549,10 +5642,26 @@ UniValue channelsopen(const UniValue& params, bool fHelp) 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"); - LOCK(cs_main); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); destpub = ParseHex(params[0].get_str().c_str()); + if (destpub.size()!= 33) + { + ERR_RESULT("invalid destination pubkey"); + return result; + } numpayments = atoi(params[1].get_str().c_str()); + if (numpayments <1) + { + ERR_RESULT("invalid number of payments, must be greater than 0"); + return result; + } payment = atol(params[2].get_str().c_str()); + if (payment <1) + { + ERR_RESULT("invalid payment amount, must be greater than 0"); + return result; + } hex = ChannelOpen(0,pubkey2pk(destpub),numpayments,payment); if ( hex.size() > 0 ) { @@ -5562,40 +5671,28 @@ UniValue channelsopen(const UniValue& params, bool fHelp) return(result); } -UniValue channelsstop(const UniValue& params, bool fHelp) -{ - UniValue result(UniValue::VOBJ); std::vector destpub; struct CCcontract_info *cp,C; std::string hex; uint256 origtxid; - cp = CCinit(&C,EVAL_CHANNELS); - if ( fHelp || params.size() != 2 ) - throw runtime_error("channelsstop destpubkey origtxid\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); - destpub = ParseHex(params[0].get_str().c_str()); - origtxid = Parseuint256((char *)params[1].get_str().c_str()); - hex = ChannelStop(0,pubkey2pk(destpub),origtxid); - if ( hex.size() > 0 ) - { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } else ERR_RESULT("couldnt create channelsstop transaction"); - return(result); -} - UniValue channelspayment(const UniValue& params, bool fHelp) { - UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; std::string hex; uint256 origtxid,prevtxid; int32_t n; int64_t amount; + 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() != 4 ) - throw runtime_error("channelspayment prevtxid origtxid n amount\n"); + if ( fHelp || params.size() != 2 ) + 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"); - LOCK(cs_main); - prevtxid = Parseuint256((char *)params[0].get_str().c_str()); - origtxid = Parseuint256((char *)params[1].get_str().c_str()); - n = atoi((char *)params[2].get_str().c_str()); - amount = atoi((char *)params[3].get_str().c_str()); - hex = ChannelPayment(0,prevtxid,origtxid,n,amount); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + opentxid = Parseuint256((char *)params[0].get_str().c_str()); + amount = atoi((char *)params[1].get_str().c_str()); + if (amount <1) + { + ERR_RESULT("invalid payment amount, must be greater than 0"); + return result; + } + if (params.size() > 2 && !params[2].isNull() && !params[2].get_str().empty()) + { + secret = Parseuint256((char *)params[2].get_str().c_str()); + } + hex = ChannelPayment(0,opentxid,amount,secret); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); @@ -5604,40 +5701,39 @@ UniValue channelspayment(const UniValue& params, bool fHelp) return(result); } -UniValue channelscollect(const UniValue& params, bool fHelp) +UniValue channelsclose(const UniValue& params, bool fHelp) { - UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; std::string hex; uint256 origtxid,paytxid; int32_t n; int64_t amount; + UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; std::string hex; uint256 opentxid; cp = CCinit(&C,EVAL_CHANNELS); - if ( fHelp || params.size() != 4 ) - throw runtime_error("channelscollect paytxid origtxid n amount\n"); + if ( fHelp || params.size() != 1 ) + throw runtime_error("channelsclose opentxid\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); - paytxid = Parseuint256((char *)params[0].get_str().c_str()); - origtxid = Parseuint256((char *)params[1].get_str().c_str()); - n = atoi((char *)params[2].get_str().c_str()); - amount = atoi((char *)params[3].get_str().c_str()); - hex = ChannelCollect(0,paytxid,origtxid,n,amount); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + opentxid = Parseuint256((char *)params[0].get_str().c_str()); + hex = ChannelClose(0,opentxid); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); result.push_back(Pair("hex", hex)); - } else ERR_RESULT("couldnt create channelscollect transaction"); + } else ERR_RESULT("couldnt create channelsclose transaction"); return(result); } UniValue channelsrefund(const UniValue& params, bool fHelp) { - UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; std::string hex; uint256 origtxid,stoptxid; + UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; std::string hex; uint256 opentxid,closetxid; cp = CCinit(&C,EVAL_CHANNELS); if ( fHelp || params.size() != 2 ) - throw runtime_error("channelsrefund stoptxid origtxid\n"); + throw runtime_error("channelsrefund opentxid closetxid\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); - stoptxid = Parseuint256((char *)params[0].get_str().c_str()); - origtxid = Parseuint256((char *)params[1].get_str().c_str()); - hex = ChannelRefund(0,stoptxid,origtxid); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + opentxid = Parseuint256((char *)params[0].get_str().c_str()); + closetxid = Parseuint256((char *)params[1].get_str().c_str()); + hex = ChannelRefund(0,opentxid,closetxid); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); @@ -5856,6 +5952,8 @@ UniValue gatewaysbind(const UniValue& params, bool fHelp) throw runtime_error("gatewaysbind tokenid oracletxid coin tokensupply M N pubkey(s)\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); tokenid = Parseuint256((char *)params[0].get_str().c_str()); oracletxid = Parseuint256((char *)params[1].get_str().c_str()); coin = params[2].get_str(); @@ -5887,6 +5985,8 @@ UniValue gatewaysdeposit(const UniValue& params, bool fHelp) throw runtime_error("gatewaysdeposit bindtxid height coin cointxid claimvout deposithex proof destpub amount\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); bindtxid = Parseuint256((char *)params[0].get_str().c_str()); height = atoi((char *)params[1].get_str().c_str()); coin = params[2].get_str(); @@ -5914,6 +6014,8 @@ UniValue gatewaysclaim(const UniValue& params, bool fHelp) throw runtime_error("gatewaysclaim bindtxid coin deposittxid destpub amount\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); bindtxid = Parseuint256((char *)params[0].get_str().c_str()); coin = params[1].get_str(); deposittxid = Parseuint256((char *)params[2].get_str().c_str()); @@ -5935,11 +6037,13 @@ UniValue gatewayswithdraw(const UniValue& params, bool fHelp) throw runtime_error("gatewayswithdraw bindtxid coin withdrawpub amount\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); bindtxid = Parseuint256((char *)params[0].get_str().c_str()); - coin = params[1].get_str(); + coin = params[1].get_str(); withdrawpub = ParseHex(params[2].get_str()); amount = atof((char *)params[3].get_str().c_str()) * COIN; - hex = GatewaysWithdraw(0,bindtxid,coin,withdrawpub,amount); + hex = GatewaysWithdraw(0,bindtxid,coin,pubkey2pk(withdrawpub),amount); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); @@ -5950,13 +6054,17 @@ UniValue gatewayswithdraw(const UniValue& params, bool fHelp) UniValue gatewaysmarkdone(const UniValue& params, bool fHelp) { - UniValue result(UniValue::VOBJ); uint256 withdrawtxid; std::string hex; - if ( fHelp || params.size() != 1 ) - throw runtime_error("gatewaysmarkdone withdrawtxid\n"); + UniValue result(UniValue::VOBJ); uint256 withdrawtxid,cointxid; std::string hex,coin; + if ( fHelp || params.size() != 3 ) + throw runtime_error("gatewaysmarkdone withdrawtxid coin cointxid\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); withdrawtxid = Parseuint256((char *)params[0].get_str().c_str()); - hex = GatewaysMarkdone(0,withdrawtxid); + coin = params[1].get_str(); + cointxid = Parseuint256((char *)params[2].get_str().c_str()); + hex = GatewaysMarkdone(0,withdrawtxid,coin,cointxid); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); @@ -5977,6 +6085,40 @@ UniValue gatewayspending(const UniValue& params, bool fHelp) return(GatewaysPendingWithdraws(bindtxid,coin)); } +UniValue gatewaysmultisig(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); std::string hex; char *txidaddr; + if ( fHelp || params.size() != 1 ) + throw runtime_error("gatewaysmultisig txidaddr\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); + txidaddr = (char *)params[0].get_str().c_str(); + return(GatewaysMultisig(txidaddr)); +} + +UniValue gatewayspartialsign(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); std::string coin,parthex,hex; uint256 txid; + if ( fHelp || params.size() != 3 ) + throw runtime_error("gatewayspartialsign txidaddr refcoin hex\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); + txid = Parseuint256((char *)params[0].get_str().c_str()); + coin = params[1].get_str(); + parthex = params[2].get_str(); + hex = GatewaysPartialSign(0,txid,coin,parthex); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex",hex)); + } else ERR_RESULT("couldnt gatewaysmultisig"); + return(result); +} + UniValue oracleslist(const UniValue& params, bool fHelp) { if ( fHelp || params.size() > 0 ) @@ -6004,8 +6146,11 @@ UniValue oraclesregister(const UniValue& params, bool fHelp) throw runtime_error("oraclesregister oracletxid datafee\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); txid = Parseuint256((char *)params[0].get_str().c_str()); - datafee = atol((char *)params[1].get_str().c_str()); + if ( (datafee= atol((char *)params[1].get_str().c_str())) == 0 ) + datafee = atof((char *)params[1].get_str().c_str()) * COIN; hex = OracleRegister(0,txid,datafee); if ( hex.size() > 0 ) { @@ -6022,6 +6167,8 @@ UniValue oraclessubscribe(const UniValue& params, bool fHelp) throw runtime_error("oraclessubscribe oracletxid publisher amount\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); txid = Parseuint256((char *)params[0].get_str().c_str()); pubkey = ParseHex(params[1].get_str().c_str()); amount = atof((char *)params[2].get_str().c_str()) * COIN; @@ -6036,7 +6183,7 @@ UniValue oraclessubscribe(const UniValue& params, bool fHelp) UniValue oraclessamples(const UniValue& params, bool fHelp) { - UniValue result(UniValue::VOBJ); uint256 txid,batontxid; int32_t num; + UniValue result(UniValue::VOBJ); uint256 txid,batontxid; int32_t num; if ( fHelp || params.size() != 3 ) throw runtime_error("oraclessamples oracletxid batonutxo num\n"); if ( ensure_CCrequirements() < 0 ) @@ -6054,6 +6201,8 @@ UniValue oraclesdata(const UniValue& params, bool fHelp) throw runtime_error("oraclesdata oracletxid hexstr\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); txid = Parseuint256((char *)params[0].get_str().c_str()); data = ParseHex(params[1].get_str().c_str()); hex = OracleData(0,txid,data); @@ -6092,6 +6241,24 @@ UniValue oraclescreate(const UniValue& params, bool fHelp) ERR_RESULT("oracles format must be <= 4096 characters"); return(result); } + // list of oracle valid formats from oracles.cpp -> oracle_format + const UniValue valid_formats[13] = {"s","S","d","D","c","C","t","T","i","I","l","L","h"}; + const UniValue header_type = "Ihh"; + // checking if oracle data type is valid + bool is_valid_format = false; + for ( int i = 0; i < 13; ++i ) { + if ( valid_formats[i].get_str() == format ) { + is_valid_format = true; + } + } + // additional check for special Ihh data type + if ( format == header_type.get_str() ) { + is_valid_format = true; + } + if ( !is_valid_format ) { + ERR_RESULT("oracles format not valid"); + return(result); + } hex = OracleCreate(0,name,description,format); if ( hex.size() > 0 ) { @@ -6190,6 +6357,156 @@ UniValue faucetget(const UniValue& params, bool fHelp) return(result); } +UniValue priceslist(const UniValue& params, bool fHelp) +{ + if ( fHelp || params.size() > 0 ) + throw runtime_error("priceslist\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(PricesList()); +} + +UniValue pricesinfo(const UniValue& params, bool fHelp) +{ + uint256 fundingtxid; + if ( fHelp || params.size() != 1 ) + throw runtime_error("pricesinfo 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(PricesInfo(fundingtxid)); +} + +UniValue pricescreate(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); uint64_t mode; int64_t funding; int32_t i,n,margin,maxleverage; std::string hex; uint256 oracletxid,longtoken,shorttoken,bettoken; std::vector pubkeys; std::vectorpubkey; + if ( fHelp || params.size() < 8 ) + throw runtime_error("pricescreate bettoken oracletxid margin mode longtoken shorttoken maxleverage funding N [pubkeys]\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); + bettoken = Parseuint256((char *)params[0].get_str().c_str()); + oracletxid = Parseuint256((char *)params[1].get_str().c_str()); + margin = atof(params[2].get_str().c_str()) * 1000; + mode = atol(params[3].get_str().c_str()); + longtoken = Parseuint256((char *)params[4].get_str().c_str()); + shorttoken = Parseuint256((char *)params[5].get_str().c_str()); + maxleverage = atol(params[6].get_str().c_str()); + funding = atof(params[7].get_str().c_str()) * COIN; + n = atoi(params[8].get_str().c_str()); + if ( n > 0 ) + { + for (i=0; i 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } + else + { + ERR_RESULT("couldnt create prices funding transaction"); + } + return(result); +} + +UniValue pricesaddfunding(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); std::string hex; uint256 fundingtxid,bettoken; int64_t amount; + if ( fHelp || params.size() != 3 ) + throw runtime_error("pricesaddfunding fundingtxid bettoken amount\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); + fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); + bettoken = Parseuint256((char *)params[1].get_str().c_str()); + amount = atof(params[2].get_str().c_str()) * COIN; + hex = PricesAddFunding(0,bettoken,fundingtxid,amount); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } + else + { + ERR_RESULT("couldnt create pricesaddfunding transaction"); + } + return(result); +} + +UniValue pricesbet(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); std::string hex; uint256 fundingtxid,bettoken; int64_t amount; int32_t leverage; + if ( fHelp || params.size() != 4 ) + throw runtime_error("pricesbet fundingtxid bettoken amount leverage\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); + fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); + bettoken = Parseuint256((char *)params[1].get_str().c_str()); + amount = atof(params[2].get_str().c_str()) * COIN; + leverage = atoi(params[3].get_str().c_str()); + hex = PricesBet(0,bettoken,fundingtxid,amount,leverage); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } + else + { + ERR_RESULT("couldnt create pricesbet transaction"); + } + return(result); +} + +UniValue pricesstatus(const UniValue& params, bool fHelp) +{ + uint256 fundingtxid,bettxid,bettoken; + if ( fHelp || params.size() != 3 ) + throw runtime_error("pricesstatus fundingtxid bettoken bettxid\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()); + bettoken = Parseuint256((char *)params[1].get_str().c_str()); + bettxid = Parseuint256((char *)params[2].get_str().c_str()); + return(PricesStatus(0,bettoken,fundingtxid,bettxid)); +} + +UniValue pricesfinish(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); uint256 fundingtxid,bettxid,bettoken; std::string hex; + if ( fHelp || params.size() != 3 ) + throw runtime_error("pricesfinish fundingtxid bettoken bettxid\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); + fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); + bettoken = Parseuint256((char *)params[1].get_str().c_str()); + bettxid = Parseuint256((char *)params[2].get_str().c_str()); + hex = PricesFinish(0,bettoken,fundingtxid,bettxid); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } + else + { + ERR_RESULT("couldnt create pricesfinish transaction"); + } + return(result); +} + UniValue dicefund(const UniValue& params, bool fHelp) { UniValue result(UniValue::VOBJ); int64_t funds,minbet,maxbet,maxodds,timeoutblocks; std::string hex; char *name; @@ -6253,7 +6570,7 @@ UniValue diceaddfunds(const UniValue& params, bool fHelp) UniValue dicebet(const UniValue& params, bool fHelp) { - UniValue result(UniValue::VOBJ); std::string hex; uint256 fundingtxid; int64_t amount,odds; char *name; + UniValue result(UniValue::VOBJ); std::string hex,error; uint256 fundingtxid; int64_t amount,odds; char *name; if ( fHelp || params.size() != 4 ) throw runtime_error("dicebet name fundingtxid amount odds\n"); if ( ensure_CCrequirements() < 0 ) @@ -6271,11 +6588,12 @@ UniValue dicebet(const UniValue& params, bool fHelp) } if (amount > 0 && odds > 0) { hex = DiceBet(0,name,fundingtxid,amount,odds); + RETURN_IF_ERROR(CCerror); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); result.push_back(Pair("hex", hex)); - } else ERR_RESULT("couldnt create dice bet transaction. make sure your address has funds"); + } } else { ERR_RESULT("amount and odds must be positive"); } @@ -6284,7 +6602,7 @@ UniValue dicebet(const UniValue& params, bool fHelp) UniValue dicefinish(const UniValue& params, bool fHelp) { - UniValue result(UniValue::VOBJ); char *name; uint256 fundingtxid,bettxid; std::string hex; int32_t r; + UniValue result(UniValue::VOBJ); uint8_t funcid; char *name; uint256 entropyused,fundingtxid,bettxid; std::string hex; int32_t r,entropyvout; if ( fHelp || params.size() != 3 ) throw runtime_error("dicefinish name fundingtxid bettxid\n"); if ( ensure_CCrequirements() < 0 ) @@ -6298,7 +6616,7 @@ UniValue dicefinish(const UniValue& params, bool fHelp) } fundingtxid = Parseuint256((char *)params[1].get_str().c_str()); bettxid = Parseuint256((char *)params[2].get_str().c_str()); - hex = DiceBetFinish(&r,0,name,fundingtxid,bettxid,1); + hex = DiceBetFinish(funcid,entropyused,entropyvout,&r,0,name,fundingtxid,bettxid,1,zeroid,-1); if ( CCerror != "" ) { ERR_RESULT(CCerror); @@ -6306,13 +6624,20 @@ UniValue dicefinish(const UniValue& params, bool fHelp) { result.push_back(Pair("result", "success")); result.push_back(Pair("hex", hex)); + if ( funcid != 0 ) + { + char funcidstr[2]; + funcidstr[0] = funcid; + funcidstr[1] = 0; + result.push_back(Pair("funcid", funcidstr)); + } } else ERR_RESULT( "couldnt create dicefinish transaction"); return(result); } UniValue dicestatus(const UniValue& params, bool fHelp) { - UniValue result(UniValue::VOBJ); char *name; uint256 fundingtxid,bettxid; std::string status; double winnings; + UniValue result(UniValue::VOBJ); char *name; uint256 fundingtxid,bettxid; std::string status,error; double winnings; if ( fHelp || (params.size() != 2 && params.size() != 3) ) throw runtime_error("dicestatus name fundingtxid bettxid\n"); if ( ensure_CCrequirements() < 0 ) @@ -6329,10 +6654,8 @@ UniValue dicestatus(const UniValue& params, bool fHelp) if ( params.size() == 3 ) bettxid = Parseuint256((char *)params[2].get_str().c_str()); winnings = DiceStatus(0,name,fundingtxid,bettxid); - if (CCerror != "") { - ERR_RESULT(CCerror); - return result; - } + RETURN_IF_ERROR(CCerror); + result.push_back(Pair("result", "success")); if ( winnings >= 0. ) { @@ -6361,7 +6684,6 @@ UniValue dicestatus(const UniValue& params, bool fHelp) UniValue dicelist(const UniValue& params, bool fHelp) { - uint256 tokenid; if ( fHelp || params.size() > 0 ) throw runtime_error("dicelist\n"); if ( ensure_CCrequirements() < 0 ) @@ -6510,6 +6832,42 @@ UniValue tokentransfer(const UniValue& params, bool fHelp) return(result); } +UniValue tokenconvert(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); std::string hex; int32_t evalcode; int64_t amount; uint256 tokenid; + if ( fHelp || params.size() != 4 ) + throw runtime_error("tokenconvert evalcode tokenid pubkey amount\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); + evalcode = atoi(params[0].get_str().c_str()); + tokenid = Parseuint256((char *)params[1].get_str().c_str()); + std::vector pubkey(ParseHex(params[2].get_str().c_str())); + amount = atol(params[3].get_str().c_str()); + if ( tokenid == zeroid ) + { + ERR_RESULT("invalid tokenid"); + return(result); + } + if ( amount <= 0 ) + { + ERR_RESULT("amount must be positive"); + return(result); + } + hex = AssetConvert(0,tokenid,pubkey,amount,evalcode); + if (amount > 0) { + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt convert tokens"); + } else { + ERR_RESULT("amount must be positive"); + } + return(result); +} + UniValue tokenbid(const UniValue& params, bool fHelp) { UniValue result(UniValue::VOBJ); int64_t bidamount,numtokens; std::string hex; double price; uint256 tokenid; @@ -6759,6 +7117,9 @@ UniValue getbalance64(const UniValue& params, bool fHelp) { set setAddress; vector vecOutputs; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR),b(UniValue::VARR); CTxDestination address; + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + const CKeyStore& keystore = *pwalletMain; CAmount nValues[64],nValues2[64],nValue,total,total2; int32_t i,segid; if (!EnsureWalletIsAvailable(fHelp)) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 473c0c8b4..db994b369 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -23,7 +23,6 @@ #include "crypter.h" #include "coins.h" #include "zcash/zip32.h" -#include "cc/StakeGuard.h" #include @@ -1346,8 +1345,7 @@ bool CWallet::VerusSelectStakeOutput(CBlock *pBlock, arith_uint256 &hashResult, if (txout.fSpendable && (UintToArith256(txout.tx->GetVerusPOSHash(&(pBlock->nNonce), txout.i, nHeight, pastHash)) <= target) && (txout.nDepth >= VERUS_MIN_STAKEAGE)) { if ((!pwinner || UintToArith256(curNonce) > UintToArith256(pBlock->nNonce)) && - (Solver(txout.tx->vout[txout.i].scriptPubKey, whichType, vSolutions) && (whichType == TX_PUBKEY || whichType == TX_PUBKEYHASH)) && - !cheatList.IsUTXOInList(COutPoint(txout.tx->GetHash(), txout.i), nHeight <= 100 ? 1 : nHeight-100)) + (Solver(txout.tx->vout[txout.i].scriptPubKey, whichType, vSolutions) && (whichType == TX_PUBKEY || whichType == TX_PUBKEYHASH))) { //printf("Found PoS block\nnNonce: %s\n", pBlock->nNonce.GetHex().c_str()); pwinner = &txout; @@ -1376,10 +1374,8 @@ int32_t CWallet::VerusStakeTransaction(CBlock *pBlock, CMutableTransaction &txNe CBlockIndex *tipindex = chainActive.LastTip(); uint32_t stakeHeight = tipindex->GetHeight() + 1; - bool extendedStake = stakeHeight >= Params().GetConsensus().vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight; - if (!extendedStake) - pk = CPubKey(); + pk = CPubKey(); bnTarget = lwmaGetNextPOSRequired(tipindex, Params().GetConsensus()); @@ -1398,7 +1394,7 @@ int32_t CWallet::VerusStakeTransaction(CBlock *pBlock, CMutableTransaction &txNe const CKeyStore& keystore = *pwalletMain; txNew.vin.resize(1); txNew.vout.resize(1); - txfee = extendedStake ? DEFAULT_STAKE_TXFEE : 0; + txfee = 0; txNew.vin[0].prevout.hash = stakeSource.GetHash(); txNew.vin[0].prevout.n = voutNum; @@ -1411,62 +1407,10 @@ int32_t CWallet::VerusStakeTransaction(CBlock *pBlock, CMutableTransaction &txNe else if (whichType == TX_PUBKEYHASH) { txNew.vout[0].scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(vSolutions[0]) << OP_EQUALVERIFY << OP_CHECKSIG; - if (extendedStake && !pk.IsValid()) - { - // we need a pubkey, so try to get one from the key ID, if not there, fail - if (!keystore.GetPubKey(CKeyID(uint160(vSolutions[0])), pk)) - return 0; - } } else return 0; - // if we are staking with the extended format, add the opreturn data required - if (extendedStake) - { - // set expiry to time out after 100 blocks, so we can remove the transaction if it orphans - txNew.nExpiryHeight = stakeHeight + 100; - - uint256 srcBlock = uint256(); - CBlockIndex *pSrcIndex; - - txNew.vout.push_back(CTxOut()); - CTxOut &txOut1 = txNew.vout[1]; - txOut1.nValue = 0; - if (!GetTransaction(stakeSource.GetHash(), stakeSource, srcBlock)) - return 0; - - BlockMap::const_iterator it = mapBlockIndex.find(srcBlock); - if (it == mapBlockIndex.end() || (pSrcIndex = it->second) == 0) - return 0; - - // !! DISABLE THIS FOR RELEASE: THIS MAKES A CHEAT TRANSACTION FOR EVERY STAKE FOR TESTING - //CMutableTransaction cheat; - //cheat = CMutableTransaction(txNew); - //printf("TESTING ONLY: THIS SHOULD NOT BE ENABLED FOR RELEASE - MAKING CHEAT TRANSACTION FOR TESTING\n"); - //cheat.vout[1].scriptPubKey << OP_RETURN - // << CStakeParams(pSrcIndex->GetHeight(), tipindex->GetHeight() + 1, pSrcIndex->GetBlockHash(), pk).AsVector(); - // !! DOWN TO HERE - - txOut1.scriptPubKey << OP_RETURN - << CStakeParams(pSrcIndex->GetHeight(), tipindex->GetHeight() + 1, tipindex->GetBlockHash(), pk).AsVector(); - - // !! DISABLE THIS FOR RELEASE: REMOVE THIS TOO - //nValue = cheat.vout[0].nValue = stakeSource.vout[voutNum].nValue - txfee; - //cheat.nLockTime = 0; - //CTransaction cheatConst(cheat); - //SignatureData cheatSig; - //if (!ProduceSignature(TransactionSignatureCreator(&keystore, &cheatConst, 0, nValue, SIGHASH_ALL), stakeSource.vout[voutNum].scriptPubKey, cheatSig, consensusBranchId)) - // fprintf(stderr,"failed to create cheat test signature\n"); - //else - //{ - // uint8_t *ptr; - // UpdateTransaction(cheat,0,cheatSig); - // cheatList.Add(CTxHolder(CTransaction(cheat), tipindex->GetHeight() + 1)); - //} - // !! DOWN TO HERE - } - nValue = txNew.vout[0].nValue = stakeSource.vout[voutNum].nValue - txfee; txNew.nLockTime = 0; @@ -3076,7 +3020,8 @@ std::vector CWallet::ResendWalletTransactionsBefore(int64_t nTime) // Don't rebroadcast if newer than nTime: if (wtx.nTimeReceived > nTime) continue; - if ( (wtx.nLockTime >= LOCKTIME_THRESHOLD && wtx.nLockTime < now-KOMODO_MAXMEMPOOLTIME) || wtx.hashBlock.IsNull() ) + + if ( (wtx.nLockTime >= LOCKTIME_THRESHOLD && wtx.nLockTime < now-KOMODO_MAXMEMPOOLTIME) ) { //LogPrintf("skip Relaying wtx %s nLockTime %u vs now.%u\n", wtx.GetHash().ToString(),(uint32_t)wtx.nLockTime,now); //vwtxh.push_back(wtx.GetHash()); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 307b380ce..c5cfedda8 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1473,5 +1473,6 @@ public: SpendingKeyAddResult operator()(const libzcash::InvalidEncoding& no) const; }; +#define RETURN_IF_ERROR(CCerror) if ( CCerror != "" ) { ERR_RESULT(CCerror); return(result); } #endif // BITCOIN_WALLET_WALLET_H