1
.gitignore
vendored
1
.gitignore
vendored
@@ -120,3 +120,4 @@ src/komodo-cli
|
|||||||
src/komodod
|
src/komodod
|
||||||
src/komodo-tx
|
src/komodo-tx
|
||||||
src/komodo-test
|
src/komodo-test
|
||||||
|
src/wallet-utility
|
||||||
|
|||||||
61
migratecoin.md
Normal file
61
migratecoin.md
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# MigrateCoin protocol
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## ExportCoins tx:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
vin:
|
||||||
|
|
||||||
|
[ any ]
|
||||||
|
|
||||||
|
vout:
|
||||||
|
|
||||||
|
- amount: {burnAmount}
|
||||||
|
|
||||||
|
script: OP_RETURN "send to ledger {id} {voutsHash}"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* ExportCoin is a standard tx which burns coins in an OP_RETURN
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## ImportCoins tx:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
vin:
|
||||||
|
|
||||||
|
- txid: 0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
|
||||||
|
idx: 0
|
||||||
|
|
||||||
|
script: CC_EVAL(EVAL_IMPORTCOINS, {momoProof},{exportCoin}) OP_CHECKCRYPTOCONDITION_UNILATERAL
|
||||||
|
|
||||||
|
vout:
|
||||||
|
|
||||||
|
- [ vouts matching voutsHash in exportCoin ]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* ImportCoin transaction has no signature
|
||||||
|
|
||||||
|
* ImportCoin is non malleable
|
||||||
|
|
||||||
|
* ImportCoin satisfies tx.IsCoinBase()
|
||||||
|
|
||||||
|
* ImportCoin uses a new opcode which allows a one sided check (no scriptPubKey)
|
||||||
|
|
||||||
|
* ImportCoin must contain CC opcode EVAL_IMPORTCOINS
|
||||||
|
|
||||||
|
* ImportCoin fees are equal to the difference between burnAmount in exportCoins and the sum of outputs.
|
||||||
@@ -256,11 +256,11 @@ libbitcoin_server_a_SOURCES = \
|
|||||||
asyncrpcqueue.cpp \
|
asyncrpcqueue.cpp \
|
||||||
bloom.cpp \
|
bloom.cpp \
|
||||||
cc/eval.cpp \
|
cc/eval.cpp \
|
||||||
cc/importpayout.cpp \
|
cc/import.cpp \
|
||||||
cc/disputepayout.cpp \
|
|
||||||
cc/betprotocol.cpp \
|
cc/betprotocol.cpp \
|
||||||
chain.cpp \
|
chain.cpp \
|
||||||
checkpoints.cpp \
|
checkpoints.cpp \
|
||||||
|
crosschain.cpp \
|
||||||
deprecation.cpp \
|
deprecation.cpp \
|
||||||
httprpc.cpp \
|
httprpc.cpp \
|
||||||
httpserver.cpp \
|
httpserver.cpp \
|
||||||
@@ -272,12 +272,14 @@ libbitcoin_server_a_SOURCES = \
|
|||||||
miner.cpp \
|
miner.cpp \
|
||||||
net.cpp \
|
net.cpp \
|
||||||
noui.cpp \
|
noui.cpp \
|
||||||
|
notarisationdb.cpp \
|
||||||
paymentdisclosure.cpp \
|
paymentdisclosure.cpp \
|
||||||
paymentdisclosuredb.cpp \
|
paymentdisclosuredb.cpp \
|
||||||
policy/fees.cpp \
|
policy/fees.cpp \
|
||||||
pow.cpp \
|
pow.cpp \
|
||||||
rest.cpp \
|
rest.cpp \
|
||||||
rpcblockchain.cpp \
|
rpcblockchain.cpp \
|
||||||
|
rpccrosschain.cpp \
|
||||||
rpcmining.cpp \
|
rpcmining.cpp \
|
||||||
rpcmisc.cpp \
|
rpcmisc.cpp \
|
||||||
rpcnet.cpp \
|
rpcnet.cpp \
|
||||||
@@ -385,6 +387,7 @@ libbitcoin_common_a_SOURCES = \
|
|||||||
core_read.cpp \
|
core_read.cpp \
|
||||||
core_write.cpp \
|
core_write.cpp \
|
||||||
hash.cpp \
|
hash.cpp \
|
||||||
|
importcoin.cpp \
|
||||||
key.cpp \
|
key.cpp \
|
||||||
keystore.cpp \
|
keystore.cpp \
|
||||||
netbase.cpp \
|
netbase.cpp \
|
||||||
@@ -605,7 +608,7 @@ endif
|
|||||||
|
|
||||||
libzcashconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS)
|
libzcashconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS)
|
||||||
libzcashconsensus_la_LIBADD = $(LIBSECP256K1)
|
libzcashconsensus_la_LIBADD = $(LIBSECP256K1)
|
||||||
libzcashconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL
|
libzcashconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -I$(srcdir)/cryptoconditions/include -DBUILD_BITCOIN_INTERNAL
|
||||||
libzcashconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
libzcashconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -5,9 +5,13 @@ bin_PROGRAMS += komodo-test
|
|||||||
# tool for generating our public parameters
|
# tool for generating our public parameters
|
||||||
komodo_test_SOURCES = \
|
komodo_test_SOURCES = \
|
||||||
test-komodo/main.cpp \
|
test-komodo/main.cpp \
|
||||||
|
test-komodo/testutils.cpp \
|
||||||
test-komodo/test_cryptoconditions.cpp \
|
test-komodo/test_cryptoconditions.cpp \
|
||||||
|
test-komodo/test_coinimport.cpp \
|
||||||
test-komodo/test_eval_bet.cpp \
|
test-komodo/test_eval_bet.cpp \
|
||||||
test-komodo/test_eval_notarisation.cpp
|
test-komodo/test_eval_notarisation.cpp \
|
||||||
|
test-komodo/test_crosschain.cpp \
|
||||||
|
test-komodo/test_parse_notarisation.cpp
|
||||||
|
|
||||||
komodo_test_CPPFLAGS = $(komodod_CPPFLAGS)
|
komodo_test_CPPFLAGS = $(komodod_CPPFLAGS)
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
source pubkey.txt
|
source pubkey.txt
|
||||||
|
# You can now add delay line to pubkey.txt file
|
||||||
|
|
||||||
args=("$@")
|
args=("$@")
|
||||||
seed_ip=`getent hosts zero.kolo.supernet.org | awk '{ print $1 }'`
|
seed_ip=`getent hosts zero.kolo.supernet.org | awk '{ print $1 }'`
|
||||||
komodo_binary='./komodod'
|
komodo_binary='./komodod'
|
||||||
delay=20
|
|
||||||
|
if [ -z "$delay" ]; then delay=20; fi
|
||||||
|
|
||||||
function komodo_asset ()
|
function komodo_asset ()
|
||||||
{
|
{
|
||||||
@@ -21,7 +25,14 @@ function komodo_asset ()
|
|||||||
supply=" "
|
supply=" "
|
||||||
fi
|
fi
|
||||||
|
|
||||||
$komodo_binary -ac_name=$1 $gen $supply $args -pubkey=$pubkey -addnode=$seed_ip &
|
if [ -n "$3" ]
|
||||||
|
then
|
||||||
|
reward=" -ac_reward=$3"
|
||||||
|
else
|
||||||
|
reward=" "
|
||||||
|
fi
|
||||||
|
|
||||||
|
$komodo_binary -ac_name=$1 $gen $supply $reward $args -pubkey=$pubkey -addnode=$seed_ip &
|
||||||
sleep $delay
|
sleep $delay
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,3 +69,4 @@ komodo_asset DSEC 7000000
|
|||||||
komodo_asset GLXT 100000000
|
komodo_asset GLXT 100000000
|
||||||
komodo_asset EQL 500000000
|
komodo_asset EQL 500000000
|
||||||
komodo_asset ZILLA 11000000
|
komodo_asset ZILLA 11000000
|
||||||
|
komodo_asset RFOX 1000000000 100000000
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ echo $pubkey
|
|||||||
./komodod -pubkey=$pubkey -ac_name=GLXT -ac_supply=100000000 -addnode=13.230.224.15 &
|
./komodod -pubkey=$pubkey -ac_name=GLXT -ac_supply=100000000 -addnode=13.230.224.15 &
|
||||||
./komodod -pubkey=$pubkey -ac_name=EQL -ac_supply=500000000 -addnode=46.101.124.153 &
|
./komodod -pubkey=$pubkey -ac_name=EQL -ac_supply=500000000 -addnode=46.101.124.153 &
|
||||||
./komodod -pubkey=$pubkey -ac_name=ZILLA -ac_supply=11000000 -addnode=54.39.23.248 &
|
./komodod -pubkey=$pubkey -ac_name=ZILLA -ac_supply=11000000 -addnode=54.39.23.248 &
|
||||||
~/VerusCoin/src/komodod -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=RFOX -ac_supply=1000000000 -ac_reward=100000000 -addnode=78.47.196.146 &
|
||||||
|
~/veruscoin/src/komodod -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 &
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
#include <cryptoconditions.h>
|
#include <cryptoconditions.h>
|
||||||
|
|
||||||
|
#include "hash.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "chain.h"
|
||||||
#include "streams.h"
|
#include "streams.h"
|
||||||
#include "script/cc.h"
|
#include "script/cc.h"
|
||||||
#include "cc/eval.h"
|
|
||||||
#include "cc/betprotocol.h"
|
#include "cc/betprotocol.h"
|
||||||
|
#include "cc/eval.h"
|
||||||
|
#include "cc/utils.h"
|
||||||
#include "primitives/transaction.h"
|
#include "primitives/transaction.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -137,3 +141,146 @@ bool GetOpReturnHash(CScript script, uint256 &hash)
|
|||||||
hash = uint256(vHash);
|
hash = uint256(vHash);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Crypto-Condition EVAL method that verifies a payout against a transaction
|
||||||
|
* notarised on another chain.
|
||||||
|
*
|
||||||
|
* IN: params - condition params
|
||||||
|
* IN: importTx - Payout transaction on value chain (KMD)
|
||||||
|
* IN: nIn - index of input of stake
|
||||||
|
*
|
||||||
|
* importTx: Spends stakeTx with payouts from asset chain
|
||||||
|
*
|
||||||
|
* in 0: Spends Stake TX and contains ImportPayout CC
|
||||||
|
* out 0: OP_RETURN MomProof, disputeTx
|
||||||
|
* out 1-: arbitrary payouts
|
||||||
|
*
|
||||||
|
* disputeTx: Spends sessionTx.0 (opener on asset chain)
|
||||||
|
*
|
||||||
|
* in 0: spends sessionTx.0
|
||||||
|
* in 1-: anything
|
||||||
|
* out 0: OP_RETURN hash of payouts
|
||||||
|
* out 1-: anything
|
||||||
|
*/
|
||||||
|
bool Eval::ImportPayout(const std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn)
|
||||||
|
{
|
||||||
|
if (importTx.vout.size() == 0) return Invalid("no-vouts");
|
||||||
|
|
||||||
|
// load data from vout[0]
|
||||||
|
MoMProof proof;
|
||||||
|
CTransaction disputeTx;
|
||||||
|
{
|
||||||
|
std::vector<unsigned char> vopret;
|
||||||
|
GetOpReturnData(importTx.vout[0].scriptPubKey, vopret);
|
||||||
|
if (!E_UNMARSHAL(vopret, ss >> proof; ss >> disputeTx))
|
||||||
|
return Invalid("invalid-payload");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check disputeTx.0 shows correct payouts
|
||||||
|
{
|
||||||
|
uint256 givenPayoutsHash;
|
||||||
|
GetOpReturnHash(disputeTx.vout[0].scriptPubKey, givenPayoutsHash);
|
||||||
|
std::vector<CTxOut> payouts(importTx.vout.begin() + 1, importTx.vout.end());
|
||||||
|
if (givenPayoutsHash != SerializeHash(payouts))
|
||||||
|
return Invalid("wrong-payouts");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check disputeTx spends sessionTx.0
|
||||||
|
// condition ImportPayout params is session ID from other chain
|
||||||
|
{
|
||||||
|
uint256 sessionHash;
|
||||||
|
if (!E_UNMARSHAL(params, ss >> sessionHash))
|
||||||
|
return Invalid("malformed-params");
|
||||||
|
if (disputeTx.vin[0].prevout != COutPoint(sessionHash, 0))
|
||||||
|
return Invalid("wrong-session");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check disputeTx solves momproof from vout[0]
|
||||||
|
{
|
||||||
|
NotarisationData data(0);
|
||||||
|
if (!GetNotarisationData(proof.notarisationHash, data))
|
||||||
|
return Invalid("coudnt-load-mom");
|
||||||
|
|
||||||
|
if (data.MoM != proof.branch.Exec(disputeTx.GetHash()))
|
||||||
|
return Invalid("mom-check-fail");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Crypto-Condition EVAL method that resolves a dispute of a session
|
||||||
|
*
|
||||||
|
* IN: vm - AppVM virtual machine to verify states
|
||||||
|
* IN: params - condition params
|
||||||
|
* IN: disputeTx - transaction attempting to resolve dispute
|
||||||
|
* IN: nIn - index of input of dispute tx
|
||||||
|
*
|
||||||
|
* disputeTx: attempt to resolve a dispute
|
||||||
|
*
|
||||||
|
* in 0: Spends Session TX first output, reveals DisputeHeader
|
||||||
|
* out 0: OP_RETURN hash of payouts
|
||||||
|
*/
|
||||||
|
bool Eval::DisputePayout(AppVM &vm, std::vector<uint8_t> params, const CTransaction &disputeTx, unsigned int nIn)
|
||||||
|
{
|
||||||
|
if (disputeTx.vout.size() == 0) return Invalid("no-vouts");
|
||||||
|
|
||||||
|
// get payouts hash
|
||||||
|
uint256 payoutHash;
|
||||||
|
if (!GetOpReturnHash(disputeTx.vout[0].scriptPubKey, payoutHash))
|
||||||
|
return Invalid("invalid-payout-hash");
|
||||||
|
|
||||||
|
// load params
|
||||||
|
uint16_t waitBlocks;
|
||||||
|
std::vector<uint8_t> vmParams;
|
||||||
|
if (!E_UNMARSHAL(params, ss >> VARINT(waitBlocks); ss >> vmParams))
|
||||||
|
return Invalid("malformed-params");
|
||||||
|
|
||||||
|
// ensure that enough time has passed
|
||||||
|
{
|
||||||
|
CTransaction sessionTx;
|
||||||
|
CBlockIndex sessionBlock;
|
||||||
|
|
||||||
|
// if unconformed its too soon
|
||||||
|
if (!GetTxConfirmed(disputeTx.vin[0].prevout.hash, sessionTx, sessionBlock))
|
||||||
|
return Error("couldnt-get-parent");
|
||||||
|
|
||||||
|
if (GetCurrentHeight() < sessionBlock.nHeight + waitBlocks)
|
||||||
|
return Invalid("dispute-too-soon"); // Not yet
|
||||||
|
}
|
||||||
|
|
||||||
|
// get spends
|
||||||
|
std::vector<CTransaction> spends;
|
||||||
|
if (!GetSpendsConfirmed(disputeTx.vin[0].prevout.hash, spends))
|
||||||
|
return Error("couldnt-get-spends");
|
||||||
|
|
||||||
|
// verify result from VM
|
||||||
|
int maxLength = -1;
|
||||||
|
uint256 bestPayout;
|
||||||
|
for (int i=1; i<spends.size(); i++)
|
||||||
|
{
|
||||||
|
std::vector<unsigned char> vmState;
|
||||||
|
if (spends[i].vout.size() == 0) continue;
|
||||||
|
if (!GetOpReturnData(spends[i].vout[0].scriptPubKey, vmState)) continue;
|
||||||
|
auto out = vm.evaluate(vmParams, vmState);
|
||||||
|
uint256 resultHash = SerializeHash(out.second);
|
||||||
|
if (out.first > maxLength) {
|
||||||
|
maxLength = out.first;
|
||||||
|
bestPayout = resultHash;
|
||||||
|
}
|
||||||
|
// The below means that if for any reason there is a draw, the first dispute wins
|
||||||
|
else if (out.first == maxLength) {
|
||||||
|
if (bestPayout != payoutHash) {
|
||||||
|
fprintf(stderr, "WARNING: VM has multiple solutions of same length\n");
|
||||||
|
bestPayout = resultHash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxLength == -1) return Invalid("no-evidence");
|
||||||
|
|
||||||
|
return bestPayout == payoutHash ? Valid() : Invalid("wrong-payout");
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include "cc/eval.h"
|
#include "cc/eval.h"
|
||||||
#include "pubkey.h"
|
#include "pubkey.h"
|
||||||
#include "primitives/block.h"
|
|
||||||
#include "primitives/transaction.h"
|
#include "primitives/transaction.h"
|
||||||
#include "cryptoconditions/include/cryptoconditions.h"
|
#include "cryptoconditions/include/cryptoconditions.h"
|
||||||
|
|
||||||
@@ -11,25 +10,18 @@
|
|||||||
class MoMProof
|
class MoMProof
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
int nIndex;
|
MerkleBranch branch;
|
||||||
std::vector<uint256> branch;
|
|
||||||
uint256 notarisationHash;
|
uint256 notarisationHash;
|
||||||
|
|
||||||
MoMProof() {}
|
|
||||||
MoMProof(int i, std::vector<uint256> b, uint256 n) : notarisationHash(n), nIndex(i), branch(b) {}
|
|
||||||
uint256 Exec(uint256 hash) const { return CBlock::CheckMerkleBranch(hash, branch, nIndex); }
|
|
||||||
|
|
||||||
ADD_SERIALIZE_METHODS;
|
ADD_SERIALIZE_METHODS;
|
||||||
|
|
||||||
template <typename Stream, typename Operation>
|
template <typename Stream, typename Operation>
|
||||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||||
READWRITE(VARINT(nIndex));
|
|
||||||
READWRITE(branch);
|
READWRITE(branch);
|
||||||
READWRITE(notarisationHash);
|
READWRITE(notarisationHash);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BetProtocol
|
class BetProtocol
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
#include <cryptoconditions.h>
|
|
||||||
|
|
||||||
#include "hash.h"
|
|
||||||
#include "chain.h"
|
|
||||||
#include "version.h"
|
|
||||||
#include "script/cc.h"
|
|
||||||
#include "cc/eval.h"
|
|
||||||
#include "cc/betprotocol.h"
|
|
||||||
#include "primitives/transaction.h"
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Crypto-Condition EVAL method that resolves a dispute of a session
|
|
||||||
*
|
|
||||||
* IN: vm - AppVM virtual machine to verify states
|
|
||||||
* IN: params - condition params
|
|
||||||
* IN: disputeTx - transaction attempting to resolve dispute
|
|
||||||
* IN: nIn - index of input of dispute tx
|
|
||||||
*
|
|
||||||
* disputeTx: attempt to resolve a dispute
|
|
||||||
*
|
|
||||||
* in 0: Spends Session TX first output, reveals DisputeHeader
|
|
||||||
* out 0: OP_RETURN hash of payouts
|
|
||||||
*/
|
|
||||||
bool Eval::DisputePayout(AppVM &vm, std::vector<uint8_t> params, const CTransaction &disputeTx, unsigned int nIn)
|
|
||||||
{
|
|
||||||
if (disputeTx.vout.size() == 0) return Invalid("no-vouts");
|
|
||||||
|
|
||||||
// get payouts hash
|
|
||||||
uint256 payoutHash;
|
|
||||||
if (!GetOpReturnHash(disputeTx.vout[0].scriptPubKey, payoutHash))
|
|
||||||
return Invalid("invalid-payout-hash");
|
|
||||||
|
|
||||||
// load params
|
|
||||||
uint16_t waitBlocks;
|
|
||||||
std::vector<uint8_t> vmParams;
|
|
||||||
if (!E_UNMARSHAL(params, ss >> VARINT(waitBlocks); ss >> vmParams))
|
|
||||||
return Invalid("malformed-params");
|
|
||||||
|
|
||||||
// ensure that enough time has passed
|
|
||||||
{
|
|
||||||
CTransaction sessionTx;
|
|
||||||
CBlockIndex sessionBlock;
|
|
||||||
|
|
||||||
// if unconformed its too soon
|
|
||||||
if (!GetTxConfirmed(disputeTx.vin[0].prevout.hash, sessionTx, sessionBlock))
|
|
||||||
return Error("couldnt-get-parent");
|
|
||||||
|
|
||||||
if (GetCurrentHeight() < sessionBlock.nHeight + waitBlocks)
|
|
||||||
return Invalid("dispute-too-soon"); // Not yet
|
|
||||||
}
|
|
||||||
|
|
||||||
// get spends
|
|
||||||
std::vector<CTransaction> spends;
|
|
||||||
if (!GetSpendsConfirmed(disputeTx.vin[0].prevout.hash, spends))
|
|
||||||
return Error("couldnt-get-spends");
|
|
||||||
|
|
||||||
// verify result from VM
|
|
||||||
int maxLength = -1;
|
|
||||||
uint256 bestPayout;
|
|
||||||
for (int i=1; i<spends.size(); i++)
|
|
||||||
{
|
|
||||||
std::vector<unsigned char> vmState;
|
|
||||||
if (spends[i].vout.size() == 0) continue;
|
|
||||||
if (!GetOpReturnData(spends[i].vout[0].scriptPubKey, vmState)) continue;
|
|
||||||
auto out = vm.evaluate(vmParams, vmState);
|
|
||||||
uint256 resultHash = SerializeHash(out.second);
|
|
||||||
if (out.first > maxLength) {
|
|
||||||
maxLength = out.first;
|
|
||||||
bestPayout = resultHash;
|
|
||||||
}
|
|
||||||
// The below means that if for any reason there is a draw, the first dispute wins
|
|
||||||
else if (out.first == maxLength) {
|
|
||||||
if (bestPayout != payoutHash) {
|
|
||||||
fprintf(stderr, "WARNING: VM has multiple solutions of same length\n");
|
|
||||||
bestPayout = resultHash;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maxLength == -1) return Invalid("no-evidence");
|
|
||||||
|
|
||||||
return bestPayout == payoutHash ? Valid() : Invalid("wrong-payout");
|
|
||||||
}
|
|
||||||
101
src/cc/eval.cpp
101
src/cc/eval.cpp
@@ -1,12 +1,15 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <cryptoconditions.h>
|
#include <cryptoconditions.h>
|
||||||
|
|
||||||
|
#include "primitives/block.h"
|
||||||
#include "primitives/transaction.h"
|
#include "primitives/transaction.h"
|
||||||
#include "script/cc.h"
|
#include "script/cc.h"
|
||||||
#include "cc/eval.h"
|
#include "cc/eval.h"
|
||||||
|
#include "cc/utils.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "chain.h"
|
#include "chain.h"
|
||||||
#include "core_io.h"
|
#include "core_io.h"
|
||||||
|
#include "crosschain.h"
|
||||||
|
|
||||||
|
|
||||||
Eval* EVAL_TEST = 0;
|
Eval* EVAL_TEST = 0;
|
||||||
@@ -14,9 +17,7 @@ Eval* EVAL_TEST = 0;
|
|||||||
|
|
||||||
bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn)
|
bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn)
|
||||||
{
|
{
|
||||||
Eval eval_;
|
EvalRef eval;
|
||||||
Eval *eval = EVAL_TEST;
|
|
||||||
if (!eval) eval = &eval_;
|
|
||||||
|
|
||||||
bool out = eval->Dispatch(cond, tx, nIn);
|
bool out = eval->Dispatch(cond, tx, nIn);
|
||||||
assert(eval->state.IsValid() == out);
|
assert(eval->state.IsValid() == out);
|
||||||
@@ -49,6 +50,10 @@ bool Eval::Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn)
|
|||||||
return ImportPayout(vparams, txTo, nIn);
|
return ImportPayout(vparams, txTo, nIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ecode == EVAL_IMPORTCOIN) {
|
||||||
|
return ImportCoin(vparams, txTo, nIn);
|
||||||
|
}
|
||||||
|
|
||||||
return Invalid("invalid-code");
|
return Invalid("invalid-code");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +149,7 @@ bool Eval::CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t t
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get MoM from a notarisation tx hash
|
* Get MoM from a notarisation tx hash (on KMD)
|
||||||
*/
|
*/
|
||||||
bool Eval::GetNotarisationData(const uint256 notaryHash, NotarisationData &data) const
|
bool Eval::GetNotarisationData(const uint256 notaryHash, NotarisationData &data) const
|
||||||
{
|
{
|
||||||
@@ -152,54 +157,84 @@ bool Eval::GetNotarisationData(const uint256 notaryHash, NotarisationData &data)
|
|||||||
CBlockIndex block;
|
CBlockIndex block;
|
||||||
if (!GetTxConfirmed(notaryHash, notarisationTx, block)) return false;
|
if (!GetTxConfirmed(notaryHash, notarisationTx, block)) return false;
|
||||||
if (!CheckNotaryInputs(notarisationTx, block.nHeight, block.nTime)) return false;
|
if (!CheckNotaryInputs(notarisationTx, block.nHeight, block.nTime)) return false;
|
||||||
if (notarisationTx.vout.size() < 2) return false;
|
if (!ParseNotarisationOpReturn(notarisationTx, data)) return false;
|
||||||
if (!data.Parse(notarisationTx.vout[1].scriptPubKey)) return false;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get MoMoM corresponding to a notarisation tx hash (on assetchain)
|
||||||
|
*/
|
||||||
|
bool Eval::GetProofRoot(uint256 kmdNotarisationHash, uint256 &momom) const
|
||||||
|
{
|
||||||
|
std::pair<uint256,NotarisationData> out;
|
||||||
|
if (!GetNextBacknotarisation(kmdNotarisationHash, out)) return false;
|
||||||
|
momom = out.second.MoMoM;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t Eval::GetAssetchainsCC() const
|
||||||
|
{
|
||||||
|
return ASSETCHAINS_CC;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string Eval::GetAssetchainsSymbol() const
|
||||||
|
{
|
||||||
|
return std::string(ASSETCHAINS_SYMBOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Notarisation data, ie, OP_RETURN payload in notarisation transactions
|
* Notarisation data, ie, OP_RETURN payload in notarisation transactions
|
||||||
*/
|
*/
|
||||||
extern char ASSETCHAINS_SYMBOL[16];
|
bool ParseNotarisationOpReturn(const CTransaction &tx, NotarisationData &data)
|
||||||
|
|
||||||
bool NotarisationData::Parse(const CScript scriptPK)
|
|
||||||
{
|
{
|
||||||
*this = NotarisationData();
|
if (tx.vout.size() < 2) return false;
|
||||||
|
|
||||||
std::vector<unsigned char> vdata;
|
std::vector<unsigned char> vdata;
|
||||||
if (!GetOpReturnData(scriptPK, vdata)) return false;
|
if (!GetOpReturnData(tx.vout[1].scriptPubKey, vdata)) return false;
|
||||||
|
bool out = E_UNMARSHAL(vdata, ss >> data);
|
||||||
CDataStream ss(vdata, SER_NETWORK, PROTOCOL_VERSION);
|
return out;
|
||||||
|
|
||||||
try {
|
|
||||||
ss >> blockHash;
|
|
||||||
ss >> height;
|
|
||||||
if (ASSETCHAINS_SYMBOL[0])
|
|
||||||
ss >> txHash;
|
|
||||||
|
|
||||||
char *nullPos = (char*) memchr(&ss[0], 0, ss.size());
|
|
||||||
if (!nullPos) return false;
|
|
||||||
ss.read(symbol, nullPos-&ss[0]+1);
|
|
||||||
|
|
||||||
if (ss.size() < 36) return false;
|
|
||||||
ss >> MoM;
|
|
||||||
ss >> MoMDepth;
|
|
||||||
} catch (...) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Misc
|
* Misc
|
||||||
*/
|
*/
|
||||||
|
|
||||||
std::string EvalToStr(EvalCode c)
|
std::string EvalToStr(EvalCode c)
|
||||||
{
|
{
|
||||||
FOREACH_EVAL(EVAL_GENERATE_STRING);
|
FOREACH_EVAL(EVAL_GENERATE_STRING);
|
||||||
char s[10];
|
char s[10];
|
||||||
sprintf(s, "0x%x", c);
|
sprintf(s, "0x%x", c);
|
||||||
return std::string(s);
|
return std::string(s);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint256 SafeCheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex)
|
||||||
|
{
|
||||||
|
if (nIndex == -1)
|
||||||
|
return uint256();
|
||||||
|
for (auto it(vMerkleBranch.begin()); it != vMerkleBranch.end(); ++it)
|
||||||
|
{
|
||||||
|
if (nIndex & 1) {
|
||||||
|
if (*it == hash) {
|
||||||
|
// non canonical. hash may be equal to node but never on the right.
|
||||||
|
return uint256();
|
||||||
|
}
|
||||||
|
hash = Hash(BEGIN(*it), END(*it), BEGIN(hash), END(hash));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
hash = Hash(BEGIN(hash), END(hash), BEGIN(*it), END(*it));
|
||||||
|
nIndex >>= 1;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint256 GetMerkleRoot(const std::vector<uint256>& vLeaves)
|
||||||
|
{
|
||||||
|
bool fMutated;
|
||||||
|
std::vector<uint256> vMerkleTree;
|
||||||
|
return BuildMerkleTree(&fMutated, vLeaves, vMerkleTree);
|
||||||
}
|
}
|
||||||
|
|||||||
175
src/cc/eval.h
175
src/cc/eval.h
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <cryptoconditions.h>
|
#include <cryptoconditions.h>
|
||||||
|
|
||||||
|
#include "cc/utils.h"
|
||||||
#include "chain.h"
|
#include "chain.h"
|
||||||
#include "streams.h"
|
#include "streams.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
@@ -20,8 +21,10 @@
|
|||||||
* a possible code is EVAL_BITCOIN_SCRIPT, where the entire binary
|
* a possible code is EVAL_BITCOIN_SCRIPT, where the entire binary
|
||||||
* after the code is interpreted as a bitcoin script.
|
* after the code is interpreted as a bitcoin script.
|
||||||
*/
|
*/
|
||||||
#define FOREACH_EVAL(EVAL) \
|
#define FOREACH_EVAL(EVAL) \
|
||||||
EVAL(EVAL_IMPORTPAYOUT, 0xe1)
|
EVAL(EVAL_IMPORTPAYOUT, 0xe1) \
|
||||||
|
EVAL(EVAL_IMPORTCOIN, 0xe2)
|
||||||
|
|
||||||
|
|
||||||
typedef uint8_t EvalCode;
|
typedef uint8_t EvalCode;
|
||||||
|
|
||||||
@@ -54,6 +57,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool ImportPayout(std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn);
|
bool ImportPayout(std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Import coin from another chain with same symbol
|
||||||
|
*/
|
||||||
|
bool ImportCoin(std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* IO functions
|
* IO functions
|
||||||
*/
|
*/
|
||||||
@@ -64,10 +72,30 @@ public:
|
|||||||
virtual bool GetBlock(uint256 hash, CBlockIndex& blockIdx) const;
|
virtual bool GetBlock(uint256 hash, CBlockIndex& blockIdx) const;
|
||||||
virtual int32_t GetNotaries(uint8_t pubkeys[64][33], int32_t height, uint32_t timestamp) const;
|
virtual int32_t GetNotaries(uint8_t pubkeys[64][33], int32_t height, uint32_t timestamp) const;
|
||||||
virtual bool GetNotarisationData(uint256 notarisationHash, NotarisationData &data) const;
|
virtual bool GetNotarisationData(uint256 notarisationHash, NotarisationData &data) const;
|
||||||
|
virtual bool GetProofRoot(uint256 kmdNotarisationHash, uint256 &momom) const;
|
||||||
virtual bool CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const;
|
virtual bool CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const;
|
||||||
|
virtual uint32_t GetAssetchainsCC() const;
|
||||||
|
virtual std::string GetAssetchainsSymbol() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
extern Eval* EVAL_TEST;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get a pointer to an Eval to use
|
||||||
|
*/
|
||||||
|
typedef std::unique_ptr<Eval,void(*)(Eval*)> EvalRef_;
|
||||||
|
class EvalRef : public EvalRef_
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EvalRef() : EvalRef_(
|
||||||
|
EVAL_TEST ? EVAL_TEST : new Eval(),
|
||||||
|
[](Eval* e){if (e!=EVAL_TEST) delete e;}) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn);
|
bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn);
|
||||||
|
|
||||||
|
|
||||||
@@ -88,22 +116,89 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
extern char ASSETCHAINS_SYMBOL[65];
|
||||||
* Data from notarisation OP_RETURN
|
|
||||||
*/
|
|
||||||
class NotarisationData {
|
|
||||||
public:
|
|
||||||
uint256 blockHash;
|
|
||||||
uint32_t height;
|
|
||||||
uint256 txHash; // Only get this guy in asset chains not in KMD
|
|
||||||
char symbol[64];
|
|
||||||
uint256 MoM;
|
|
||||||
uint32_t MoMDepth;
|
|
||||||
|
|
||||||
bool Parse(CScript scriptPubKey);
|
|
||||||
|
/*
|
||||||
|
* Data from notarisation OP_RETURN from chain being notarised
|
||||||
|
*/
|
||||||
|
class NotarisationData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int IsBackNotarisation = 0;
|
||||||
|
uint256 blockHash = uint256();
|
||||||
|
uint32_t height = 0;
|
||||||
|
uint256 txHash = uint256();
|
||||||
|
char symbol[64] = "\0";
|
||||||
|
uint256 MoM = uint256();
|
||||||
|
uint16_t MoMDepth = 0;
|
||||||
|
uint16_t ccId = 0;
|
||||||
|
uint256 MoMoM = uint256();
|
||||||
|
uint32_t MoMoMDepth = 0;
|
||||||
|
|
||||||
|
NotarisationData(int IsBack=2) : IsBackNotarisation(IsBack) {}
|
||||||
|
|
||||||
|
ADD_SERIALIZE_METHODS;
|
||||||
|
|
||||||
|
template <typename Stream, typename Operation>
|
||||||
|
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||||
|
|
||||||
|
bool IsBack = IsBackNotarisation;
|
||||||
|
if (2 == IsBackNotarisation) IsBack = DetectBackNotarisation(s, ser_action);
|
||||||
|
|
||||||
|
READWRITE(blockHash);
|
||||||
|
READWRITE(height);
|
||||||
|
if (IsBack)
|
||||||
|
READWRITE(txHash);
|
||||||
|
SerSymbol(s, ser_action);
|
||||||
|
if (s.size() == 0) return;
|
||||||
|
READWRITE(MoM);
|
||||||
|
READWRITE(MoMDepth);
|
||||||
|
READWRITE(ccId);
|
||||||
|
if (s.size() == 0) return;
|
||||||
|
if (IsBack) {
|
||||||
|
READWRITE(MoMoM);
|
||||||
|
READWRITE(MoMoMDepth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Stream>
|
||||||
|
void SerSymbol(Stream& s, CSerActionSerialize act)
|
||||||
|
{
|
||||||
|
s.write(symbol, strlen(symbol)+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Stream>
|
||||||
|
void SerSymbol(Stream& s, CSerActionUnserialize act)
|
||||||
|
{
|
||||||
|
char *nullPos = (char*) memchr(&s[0], 0, s.size());
|
||||||
|
if (!nullPos)
|
||||||
|
throw std::ios_base::failure("couldn't parse symbol");
|
||||||
|
s.read(symbol, nullPos-&s[0]+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Stream>
|
||||||
|
bool DetectBackNotarisation(Stream& s, CSerActionUnserialize act)
|
||||||
|
{
|
||||||
|
if (ASSETCHAINS_SYMBOL[0]) return 1;
|
||||||
|
if (s.size() >= 72) {
|
||||||
|
if (strcmp("BTC", &s[68]) == 0) return 1;
|
||||||
|
if (strcmp("KMD", &s[68]) == 0) return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Stream>
|
||||||
|
bool DetectBackNotarisation(Stream& s, CSerActionSerialize act)
|
||||||
|
{
|
||||||
|
return !txHash.IsNull();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
bool ParseNotarisationOpReturn(const CTransaction &tx, NotarisationData &data);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Eval code utilities.
|
* Eval code utilities.
|
||||||
*/
|
*/
|
||||||
@@ -116,28 +211,42 @@ std::string EvalToStr(EvalCode c);
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Serialisation boilerplate
|
* Merkle stuff
|
||||||
*/
|
*/
|
||||||
#define E_MARSHAL(body) SerializeF([&] (CDataStream &ss) {body;})
|
uint256 SafeCheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex);
|
||||||
template <class T>
|
|
||||||
std::vector<uint8_t> SerializeF(const T f)
|
|
||||||
{
|
|
||||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
|
||||||
f(ss);
|
|
||||||
return std::vector<unsigned char>(ss.begin(), ss.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
#define E_UNMARSHAL(params, body) DeserializeF(params, [&] (CDataStream &ss) {body;})
|
|
||||||
template <class T>
|
class MerkleBranch
|
||||||
bool DeserializeF(const std::vector<unsigned char> vIn, T f)
|
|
||||||
{
|
{
|
||||||
CDataStream ss(vIn, SER_NETWORK, PROTOCOL_VERSION);
|
public:
|
||||||
try {
|
int nIndex;
|
||||||
f(ss);
|
std::vector<uint256> branch;
|
||||||
if (ss.eof()) return true;
|
|
||||||
} catch(...) {}
|
MerkleBranch() {}
|
||||||
return false;
|
MerkleBranch(int i, std::vector<uint256> b) : nIndex(i), branch(b) {}
|
||||||
}
|
uint256 Exec(uint256 hash) const { return SafeCheckMerkleBranch(hash, branch, nIndex); }
|
||||||
|
|
||||||
|
MerkleBranch& operator<<(MerkleBranch append)
|
||||||
|
{
|
||||||
|
nIndex += append.nIndex << branch.size();
|
||||||
|
branch.insert(branch.end(), append.branch.begin(), append.branch.end());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ADD_SERIALIZE_METHODS;
|
||||||
|
|
||||||
|
template <typename Stream, typename Operation>
|
||||||
|
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||||
|
READWRITE(VARINT(nIndex));
|
||||||
|
READWRITE(branch);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef std::pair<uint256,MerkleBranch> TxProof;
|
||||||
|
|
||||||
|
|
||||||
|
uint256 GetMerkleRoot(const std::vector<uint256>& vLeaves);
|
||||||
|
|
||||||
|
|
||||||
#endif /* CC_EVAL_H */
|
#endif /* CC_EVAL_H */
|
||||||
|
|||||||
72
src/cc/import.cpp
Normal file
72
src/cc/import.cpp
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#include "cc/eval.h"
|
||||||
|
#include "cc/utils.h"
|
||||||
|
#include "importcoin.h"
|
||||||
|
#include "primitives/transaction.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CC Eval method for import coin.
|
||||||
|
*
|
||||||
|
* This method should control every parameter of the ImportCoin transaction, since it has no signature
|
||||||
|
* to protect it from malleability.
|
||||||
|
*/
|
||||||
|
bool Eval::ImportCoin(const std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn)
|
||||||
|
{
|
||||||
|
if (importTx.vout.size() < 2)
|
||||||
|
return Invalid("too-few-vouts");
|
||||||
|
|
||||||
|
// params
|
||||||
|
TxProof proof;
|
||||||
|
CTransaction burnTx;
|
||||||
|
std::vector<CTxOut> payouts;
|
||||||
|
|
||||||
|
if (!UnmarshalImportTx(importTx, proof, burnTx, payouts))
|
||||||
|
return Invalid("invalid-params");
|
||||||
|
|
||||||
|
// Control all aspects of this transaction
|
||||||
|
// It should not be at all malleable
|
||||||
|
if (MakeImportCoinTransaction(proof, burnTx, payouts).GetHash() != importTx.GetHash())
|
||||||
|
return Invalid("non-canonical");
|
||||||
|
|
||||||
|
// burn params
|
||||||
|
uint32_t targetCcid;
|
||||||
|
std::string targetSymbol;
|
||||||
|
uint256 payoutsHash;
|
||||||
|
|
||||||
|
if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCcid, payoutsHash))
|
||||||
|
return Invalid("invalid-burn-tx");
|
||||||
|
|
||||||
|
if (targetCcid != GetAssetchainsCC() || targetSymbol != GetAssetchainsSymbol())
|
||||||
|
return Invalid("importcoin-wrong-chain");
|
||||||
|
|
||||||
|
// check burn amount
|
||||||
|
{
|
||||||
|
uint64_t burnAmount = burnTx.vout[0].nValue;
|
||||||
|
if (burnAmount == 0)
|
||||||
|
return Invalid("invalid-burn-amount");
|
||||||
|
uint64_t totalOut = 0;
|
||||||
|
for (int i=0; i<importTx.vout.size(); i++)
|
||||||
|
totalOut += importTx.vout[i].nValue;
|
||||||
|
if (totalOut > burnAmount)
|
||||||
|
return Invalid("payout-too-high");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check burntx shows correct outputs hash
|
||||||
|
if (payoutsHash != SerializeHash(payouts))
|
||||||
|
return Invalid("wrong-payouts");
|
||||||
|
|
||||||
|
// Check proof confirms existance of burnTx
|
||||||
|
{
|
||||||
|
uint256 momom, target;
|
||||||
|
if (!GetProofRoot(proof.first, momom))
|
||||||
|
return Invalid("coudnt-load-momom");
|
||||||
|
|
||||||
|
target = proof.second.Exec(burnTx.GetHash());
|
||||||
|
if (momom != proof.second.Exec(burnTx.GetHash()))
|
||||||
|
return Invalid("momom-check-fail");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
#include <cryptoconditions.h>
|
|
||||||
|
|
||||||
#include "main.h"
|
|
||||||
#include "chain.h"
|
|
||||||
#include "streams.h"
|
|
||||||
#include "cc/eval.h"
|
|
||||||
#include "cc/betprotocol.h"
|
|
||||||
#include "primitives/transaction.h"
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Crypto-Condition EVAL method that verifies a payout against a transaction
|
|
||||||
* notarised on another chain.
|
|
||||||
*
|
|
||||||
* IN: params - condition params
|
|
||||||
* IN: importTx - Payout transaction on value chain (KMD)
|
|
||||||
* IN: nIn - index of input of stake
|
|
||||||
*
|
|
||||||
* importTx: Spends stakeTx with payouts from asset chain
|
|
||||||
*
|
|
||||||
* in 0: Spends Stake TX and contains ImportPayout CC
|
|
||||||
* out 0: OP_RETURN MomProof, disputeTx
|
|
||||||
* out 1-: arbitrary payouts
|
|
||||||
*
|
|
||||||
* disputeTx: Spends sessionTx.0 (opener on asset chain)
|
|
||||||
*
|
|
||||||
* in 0: spends sessionTx.0
|
|
||||||
* in 1-: anything
|
|
||||||
* out 0: OP_RETURN hash of payouts
|
|
||||||
* out 1-: anything
|
|
||||||
*/
|
|
||||||
bool Eval::ImportPayout(const std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn)
|
|
||||||
{
|
|
||||||
if (importTx.vout.size() == 0) return Invalid("no-vouts");
|
|
||||||
|
|
||||||
// load data from vout[0]
|
|
||||||
MoMProof proof;
|
|
||||||
CTransaction disputeTx;
|
|
||||||
{
|
|
||||||
std::vector<unsigned char> vopret;
|
|
||||||
GetOpReturnData(importTx.vout[0].scriptPubKey, vopret);
|
|
||||||
if (!E_UNMARSHAL(vopret, ss >> proof; ss >> disputeTx))
|
|
||||||
return Invalid("invalid-payload");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check disputeTx.0 shows correct payouts
|
|
||||||
{
|
|
||||||
uint256 givenPayoutsHash;
|
|
||||||
GetOpReturnHash(disputeTx.vout[0].scriptPubKey, givenPayoutsHash);
|
|
||||||
std::vector<CTxOut> payouts(importTx.vout.begin() + 1, importTx.vout.end());
|
|
||||||
if (givenPayoutsHash != SerializeHash(payouts))
|
|
||||||
return Invalid("wrong-payouts");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check disputeTx spends sessionTx.0
|
|
||||||
// condition ImportPayout params is session ID from other chain
|
|
||||||
{
|
|
||||||
uint256 sessionHash;
|
|
||||||
if (!E_UNMARSHAL(params, ss >> sessionHash))
|
|
||||||
return Invalid("malformed-params");
|
|
||||||
if (disputeTx.vin[0].prevout != COutPoint(sessionHash, 0))
|
|
||||||
return Invalid("wrong-session");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check disputeTx solves momproof from vout[0]
|
|
||||||
{
|
|
||||||
NotarisationData data;
|
|
||||||
if (!GetNotarisationData(proof.notarisationHash, data))
|
|
||||||
return Invalid("coudnt-load-mom");
|
|
||||||
|
|
||||||
if (data.MoM != proof.Exec(disputeTx.GetHash()))
|
|
||||||
return Invalid("mom-check-fail");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Valid();
|
|
||||||
}
|
|
||||||
0
src/cc/utils.cpp
Normal file
0
src/cc/utils.cpp
Normal file
34
src/cc/utils.h
Normal file
34
src/cc/utils.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#ifndef CC_UTILS_H
|
||||||
|
#define CC_UTILS_H
|
||||||
|
|
||||||
|
#include "streams.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Serialisation boilerplate
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
std::vector<uint8_t> SerializeF(const T f)
|
||||||
|
{
|
||||||
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
f(ss);
|
||||||
|
return std::vector<unsigned char>(ss.begin(), ss.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
bool DeserializeF(const std::vector<unsigned char> vIn, T f)
|
||||||
|
{
|
||||||
|
CDataStream ss(vIn, SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
try {
|
||||||
|
f(ss);
|
||||||
|
if (ss.eof()) return true;
|
||||||
|
} catch(...) {}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define E_MARSHAL(body) SerializeF([&] (CDataStream &ss) {body;})
|
||||||
|
#define E_UNMARSHAL(params, body) DeserializeF(params, [&] (CDataStream &ss) {body;})
|
||||||
|
|
||||||
|
#endif /* CC_UTILS_H */
|
||||||
@@ -536,11 +536,13 @@ public:
|
|||||||
BOOST_STATIC_ASSERT(equihash_parameters_acceptable(N, K));
|
BOOST_STATIC_ASSERT(equihash_parameters_acceptable(N, K));
|
||||||
nEquihashN = N;
|
nEquihashN = N;
|
||||||
nEquihashK = K;
|
nEquihashK = K;
|
||||||
|
|
||||||
genesis = CreateGenesisBlock(
|
genesis = CreateGenesisBlock(
|
||||||
1296688602,
|
1296688602,
|
||||||
uint256S("0x0000000000000000000000000000000000000000000000000000000000000009"),
|
uint256S("0x0000000000000000000000000000000000000000000000000000000000000009"),
|
||||||
ParseHex("01936b7db1eb4ac39f151b8704642d0a8bda13ec547d54cd5e43ba142fc6d8877cab07b3"),
|
ParseHex("01936b7db1eb4ac39f151b8704642d0a8bda13ec547d54cd5e43ba142fc6d8877cab07b3"),
|
||||||
|
|
||||||
|
|
||||||
KOMODO_MINDIFF_NBITS, 4, 0);
|
KOMODO_MINDIFF_NBITS, 4, 0);
|
||||||
consensus.hashGenesisBlock = genesis.GetHash();
|
consensus.hashGenesisBlock = genesis.GetHash();
|
||||||
assert(consensus.hashGenesisBlock == uint256S("0x029f11d80ef9765602235e1bc9727e3eb6ba20839319f761fee920d63401e327"));
|
assert(consensus.hashGenesisBlock == uint256S("0x029f11d80ef9765602235e1bc9727e3eb6ba20839319f761fee920d63401e327"));
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "policy/fees.h"
|
#include "policy/fees.h"
|
||||||
#include "komodo_defs.h"
|
#include "komodo_defs.h"
|
||||||
|
#include "importcoin.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
@@ -392,11 +393,13 @@ extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
|
|||||||
|
|
||||||
CAmount CCoinsViewCache::GetValueIn(int32_t nHeight,int64_t *interestp,const CTransaction& tx,uint32_t tiptime) const
|
CAmount CCoinsViewCache::GetValueIn(int32_t nHeight,int64_t *interestp,const CTransaction& tx,uint32_t tiptime) const
|
||||||
{
|
{
|
||||||
|
CAmount value,nResult = 0;
|
||||||
if ( interestp != 0 )
|
if ( interestp != 0 )
|
||||||
*interestp = 0;
|
*interestp = 0;
|
||||||
|
if ( tx.IsCoinImport() )
|
||||||
|
return GetCoinImportValue(tx);
|
||||||
if ( tx.IsCoinBase() != 0 )
|
if ( tx.IsCoinBase() != 0 )
|
||||||
return 0;
|
return 0;
|
||||||
CAmount value,nResult = 0;
|
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||||
{
|
{
|
||||||
value = GetOutputFor(tx.vin[i]).nValue;
|
value = GetOutputFor(tx.vin[i]).nValue;
|
||||||
@@ -421,6 +424,7 @@ CAmount CCoinsViewCache::GetValueIn(int32_t nHeight,int64_t *interestp,const CTr
|
|||||||
return nResult;
|
return nResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const
|
bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const
|
||||||
{
|
{
|
||||||
boost::unordered_map<uint256, ZCIncrementalMerkleTree, CCoinsKeyHasher> intermediates;
|
boost::unordered_map<uint256, ZCIncrementalMerkleTree, CCoinsKeyHasher> intermediates;
|
||||||
@@ -457,7 +461,7 @@ bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const
|
|||||||
|
|
||||||
bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
|
bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
|
||||||
{
|
{
|
||||||
if (!tx.IsCoinBase()) {
|
if (!tx.IsMint()) {
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
||||||
const COutPoint &prevout = tx.vin[i].prevout;
|
const COutPoint &prevout = tx.vin[i].prevout;
|
||||||
const CCoins* coins = AccessCoins(prevout.hash);
|
const CCoins* coins = AccessCoins(prevout.hash);
|
||||||
@@ -483,6 +487,9 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const
|
|||||||
if (tx.vjoinsplit.size() > 0) {
|
if (tx.vjoinsplit.size() > 0) {
|
||||||
return MAX_PRIORITY;
|
return MAX_PRIORITY;
|
||||||
}
|
}
|
||||||
|
if (tx.IsCoinImport()) {
|
||||||
|
return MAX_PRIORITY;
|
||||||
|
}
|
||||||
|
|
||||||
double dResult = 0.0;
|
double dResult = 0.0;
|
||||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
||||||
|
|||||||
336
src/crosschain.cpp
Normal file
336
src/crosschain.cpp
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
#include "cc/eval.h"
|
||||||
|
#include "crosschain.h"
|
||||||
|
#include "importcoin.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "notarisationdb.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The crosschain workflow.
|
||||||
|
*
|
||||||
|
* 3 chains, A, B, and KMD. We would like to prove TX on B.
|
||||||
|
* There is a notarisation, nA0, which will include TX via an MoM.
|
||||||
|
* The notarisation nA0 must fall between 2 notarisations of B,
|
||||||
|
* ie, nB0 and nB1. An MoMoM including this range is propagated to
|
||||||
|
* B in notarisation receipt (backnotarisation) bnB2.
|
||||||
|
*
|
||||||
|
* A: TX bnA0
|
||||||
|
* \ /
|
||||||
|
* KMD: nB0 nA0 nB1 nB2
|
||||||
|
* \ \ \
|
||||||
|
* B: bnB0 bnB1 bnB2
|
||||||
|
*/
|
||||||
|
|
||||||
|
// XXX: There are potential crashes wherever we access chainActive without a lock,
|
||||||
|
// because it might be disconnecting blocks at the same time.
|
||||||
|
|
||||||
|
|
||||||
|
int NOTARISATION_SCAN_LIMIT_BLOCKS = 1440;
|
||||||
|
|
||||||
|
|
||||||
|
/* On KMD */
|
||||||
|
uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeight,
|
||||||
|
std::vector<uint256> &moms, uint256 &destNotarisationTxid)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Notaries don't wait for confirmation on KMD before performing a backnotarisation,
|
||||||
|
* but we need a determinable range that will encompass all merkle roots. Include MoMs
|
||||||
|
* including the block height of the last notarisation until the height before the
|
||||||
|
* previous notarisation.
|
||||||
|
*
|
||||||
|
* kmdHeight notarisations-0 notarisations-1
|
||||||
|
* *********************|
|
||||||
|
* > scan backwards >
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (targetCCid <= 1)
|
||||||
|
return uint256();
|
||||||
|
|
||||||
|
if (kmdHeight < 0 || kmdHeight > chainActive.Height())
|
||||||
|
return uint256();
|
||||||
|
|
||||||
|
int seenOwnNotarisations = 0;
|
||||||
|
|
||||||
|
for (int i=0; i<NOTARISATION_SCAN_LIMIT_BLOCKS; i++) {
|
||||||
|
if (i > kmdHeight) break;
|
||||||
|
NotarisationsInBlock notarisations;
|
||||||
|
uint256 blockHash = *chainActive[kmdHeight-i]->phashBlock;
|
||||||
|
if (!GetBlockNotarisations(blockHash, notarisations))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// See if we have an own notarisation in this block
|
||||||
|
BOOST_FOREACH(Notarisation& nota, notarisations) {
|
||||||
|
if (strcmp(nota.second.symbol, symbol) == 0)
|
||||||
|
{
|
||||||
|
seenOwnNotarisations++;
|
||||||
|
if (seenOwnNotarisations == 1)
|
||||||
|
destNotarisationTxid = nota.first;
|
||||||
|
else if (seenOwnNotarisations == 2)
|
||||||
|
goto end;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seenOwnNotarisations == 1) {
|
||||||
|
BOOST_FOREACH(Notarisation& nota, notarisations) {
|
||||||
|
if (nota.second.ccId == targetCCid)
|
||||||
|
moms.push_back(nota.second.MoM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
return GetMerkleRoot(moms);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get a notarisation from a given height
|
||||||
|
*
|
||||||
|
* Will scan notarisations leveldb up to a limit
|
||||||
|
*/
|
||||||
|
template <typename IsTarget>
|
||||||
|
int ScanNotarisationsFromHeight(int nHeight, const IsTarget f, Notarisation &found)
|
||||||
|
{
|
||||||
|
int limit = std::min(nHeight + NOTARISATION_SCAN_LIMIT_BLOCKS, chainActive.Height());
|
||||||
|
|
||||||
|
for (int h=nHeight; h<limit; h++) {
|
||||||
|
NotarisationsInBlock notarisations;
|
||||||
|
|
||||||
|
if (!GetBlockNotarisations(*chainActive[h]->phashBlock, notarisations))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
BOOST_FOREACH(found, notarisations) {
|
||||||
|
if (f(found)) {
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* On KMD */
|
||||||
|
TxProof GetCrossChainProof(const uint256 txid, const char* targetSymbol, uint32_t targetCCid,
|
||||||
|
const TxProof assetChainProof)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Here we are given a proof generated by an assetchain A which goes from given txid to
|
||||||
|
* an assetchain MoM. We need to go from the notarisationTxid for A to the MoMoM range of the
|
||||||
|
* backnotarisation for B (given by kmdheight of notarisation), find the MoM within the MoMs for
|
||||||
|
* that range, and finally extend the proof to lead to the MoMoM (proof root).
|
||||||
|
*/
|
||||||
|
EvalRef eval;
|
||||||
|
uint256 MoM = assetChainProof.second.Exec(txid);
|
||||||
|
|
||||||
|
// Get a kmd height for given notarisation Txid
|
||||||
|
int kmdHeight;
|
||||||
|
{
|
||||||
|
CTransaction sourceNotarisation;
|
||||||
|
uint256 hashBlock;
|
||||||
|
CBlockIndex blockIdx;
|
||||||
|
if (!eval->GetTxConfirmed(assetChainProof.first, sourceNotarisation, blockIdx))
|
||||||
|
throw std::runtime_error("Notarisation not found");
|
||||||
|
kmdHeight = blockIdx.nHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We now have a kmdHeight of the notarisation from chain A. So we know that a MoM exists
|
||||||
|
// at that height.
|
||||||
|
// If we call CalculateProofRoot with that height, it'll scan backwards, until it finds
|
||||||
|
// a notarisation from B, and it might not include our notarisation from A
|
||||||
|
// at all. So, the thing we need to do is scan forwards to find the notarisation for B,
|
||||||
|
// that is inclusive of A.
|
||||||
|
Notarisation nota;
|
||||||
|
auto isTarget = [&](Notarisation ¬a) {
|
||||||
|
return strcmp(nota.second.symbol, targetSymbol) == 0;
|
||||||
|
};
|
||||||
|
kmdHeight = ScanNotarisationsFromHeight(kmdHeight, isTarget, nota);
|
||||||
|
if (!kmdHeight)
|
||||||
|
throw std::runtime_error("Cannot find notarisation for target inclusive of source");
|
||||||
|
|
||||||
|
// Get MoMs for kmd height and symbol
|
||||||
|
std::vector<uint256> moms;
|
||||||
|
uint256 targetChainNotarisationTxid;
|
||||||
|
uint256 MoMoM = CalculateProofRoot(targetSymbol, targetCCid, kmdHeight, moms, targetChainNotarisationTxid);
|
||||||
|
if (MoMoM.IsNull())
|
||||||
|
throw std::runtime_error("No MoMs found");
|
||||||
|
|
||||||
|
// Find index of source MoM in MoMoM
|
||||||
|
int nIndex;
|
||||||
|
for (nIndex=0; nIndex<moms.size(); nIndex++) {
|
||||||
|
if (moms[nIndex] == MoM)
|
||||||
|
goto cont;
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Couldn't find MoM within MoMoM set");
|
||||||
|
cont:
|
||||||
|
|
||||||
|
// Create a branch
|
||||||
|
std::vector<uint256> vBranch;
|
||||||
|
{
|
||||||
|
CBlock fakeBlock;
|
||||||
|
for (int i=0; i<moms.size(); i++) {
|
||||||
|
CTransaction fakeTx;
|
||||||
|
// first value in CTransaction memory is it's hash
|
||||||
|
memcpy((void*)&fakeTx, moms[i].begin(), 32);
|
||||||
|
fakeBlock.vtx.push_back(fakeTx);
|
||||||
|
}
|
||||||
|
vBranch = fakeBlock.GetMerkleBranch(nIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concatenate branches
|
||||||
|
MerkleBranch newBranch = assetChainProof.second;
|
||||||
|
newBranch << MerkleBranch(nIndex, vBranch);
|
||||||
|
|
||||||
|
// Check proof
|
||||||
|
if (newBranch.Exec(txid) != MoMoM)
|
||||||
|
throw std::runtime_error("Proof check failed");
|
||||||
|
|
||||||
|
return std::make_pair(targetChainNotarisationTxid,newBranch);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Takes an importTx that has proof leading to assetchain root
|
||||||
|
* and extends proof to cross chain root
|
||||||
|
*/
|
||||||
|
void CompleteImportTransaction(CTransaction &importTx)
|
||||||
|
{
|
||||||
|
TxProof proof;
|
||||||
|
CTransaction burnTx;
|
||||||
|
std::vector<CTxOut> payouts;
|
||||||
|
if (!UnmarshalImportTx(importTx, proof, burnTx, payouts))
|
||||||
|
throw std::runtime_error("Couldn't parse importTx");
|
||||||
|
|
||||||
|
std::string targetSymbol;
|
||||||
|
uint32_t targetCCid;
|
||||||
|
uint256 payoutsHash;
|
||||||
|
if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCCid, payoutsHash))
|
||||||
|
throw std::runtime_error("Couldn't parse burnTx");
|
||||||
|
|
||||||
|
proof = GetCrossChainProof(burnTx.GetHash(), targetSymbol.data(), targetCCid, proof);
|
||||||
|
|
||||||
|
importTx = MakeImportCoinTransaction(proof, burnTx, payouts);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool IsSameAssetChain(const Notarisation ¬a) {
|
||||||
|
return strcmp(nota.second.symbol, ASSETCHAINS_SYMBOL) == 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* On assetchain */
|
||||||
|
bool GetNextBacknotarisation(uint256 kmdNotarisationTxid, Notarisation &out)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Here we are given a txid, and a proof.
|
||||||
|
* We go from the KMD notarisation txid to the backnotarisation,
|
||||||
|
* then jump to the next backnotarisation, which contains the corresponding MoMoM.
|
||||||
|
*/
|
||||||
|
Notarisation bn;
|
||||||
|
if (!GetBackNotarisation(kmdNotarisationTxid, bn))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Need to get block height of that backnotarisation
|
||||||
|
EvalRef eval;
|
||||||
|
CBlockIndex block;
|
||||||
|
CTransaction tx;
|
||||||
|
if (!eval->GetTxConfirmed(bn.first, tx, block)){
|
||||||
|
fprintf(stderr, "Can't get height of backnotarisation, this should not happen\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (bool) ScanNotarisationsFromHeight(block.nHeight+1, &IsSameAssetChain, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* On assetchain
|
||||||
|
* in: txid
|
||||||
|
* out: pair<notarisationTxHash,merkleBranch>
|
||||||
|
*/
|
||||||
|
TxProof GetAssetchainProof(uint256 hash)
|
||||||
|
{
|
||||||
|
int nIndex;
|
||||||
|
CBlockIndex* blockIndex;
|
||||||
|
Notarisation nota;
|
||||||
|
std::vector<uint256> branch;
|
||||||
|
|
||||||
|
{
|
||||||
|
uint256 blockHash;
|
||||||
|
CTransaction tx;
|
||||||
|
if (!GetTransaction(hash, tx, blockHash, true))
|
||||||
|
throw std::runtime_error("cannot find transaction");
|
||||||
|
|
||||||
|
if (blockHash.IsNull())
|
||||||
|
throw std::runtime_error("tx still in mempool");
|
||||||
|
|
||||||
|
blockIndex = mapBlockIndex[blockHash];
|
||||||
|
int h = blockIndex->nHeight;
|
||||||
|
// The assumption here is that the first notarisation for a height GTE than
|
||||||
|
// the transaction block height will contain the corresponding MoM. If there
|
||||||
|
// are sequence issues with the notarisations this may fail.
|
||||||
|
auto isTarget = [&](Notarisation ¬a) {
|
||||||
|
if (!IsSameAssetChain(nota)) return false;
|
||||||
|
return nota.second.height >= blockIndex->nHeight;
|
||||||
|
};
|
||||||
|
if (!ScanNotarisationsFromHeight(blockIndex->nHeight, isTarget, nota))
|
||||||
|
throw std::runtime_error("backnotarisation not yet confirmed");
|
||||||
|
|
||||||
|
// index of block in MoM leaves
|
||||||
|
nIndex = nota.second.height - blockIndex->nHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// build merkle chain from blocks to MoM
|
||||||
|
{
|
||||||
|
std::vector<uint256> leaves, tree;
|
||||||
|
for (int i=0; i<nota.second.MoMDepth; i++) {
|
||||||
|
uint256 mRoot = chainActive[nota.second.height - i]->hashMerkleRoot;
|
||||||
|
leaves.push_back(mRoot);
|
||||||
|
}
|
||||||
|
bool fMutated;
|
||||||
|
BuildMerkleTree(&fMutated, leaves, tree);
|
||||||
|
branch = GetMerkleBranch(nIndex, leaves.size(), tree);
|
||||||
|
|
||||||
|
// Check branch
|
||||||
|
uint256 ourResult = SafeCheckMerkleBranch(blockIndex->hashMerkleRoot, branch, nIndex);
|
||||||
|
if (nota.second.MoM != ourResult)
|
||||||
|
throw std::runtime_error("Failed merkle block->MoM");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now get the tx merkle branch
|
||||||
|
{
|
||||||
|
CBlock block;
|
||||||
|
|
||||||
|
if (fHavePruned && !(blockIndex->nStatus & BLOCK_HAVE_DATA) && blockIndex->nTx > 0)
|
||||||
|
throw std::runtime_error("Block not available (pruned data)");
|
||||||
|
|
||||||
|
if(!ReadBlockFromDisk(block, blockIndex,1))
|
||||||
|
throw std::runtime_error("Can't read block from disk");
|
||||||
|
|
||||||
|
// Locate the transaction in the block
|
||||||
|
int nTxIndex;
|
||||||
|
for (nTxIndex = 0; nTxIndex < (int)block.vtx.size(); nTxIndex++)
|
||||||
|
if (block.vtx[nTxIndex].GetHash() == hash)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (nTxIndex == (int)block.vtx.size())
|
||||||
|
throw std::runtime_error("Error locating tx in block");
|
||||||
|
|
||||||
|
std::vector<uint256> txBranch = block.GetMerkleBranch(nTxIndex);
|
||||||
|
|
||||||
|
// Check branch
|
||||||
|
if (block.hashMerkleRoot != CBlock::CheckMerkleBranch(hash, txBranch, nTxIndex))
|
||||||
|
throw std::runtime_error("Failed merkle tx->block");
|
||||||
|
|
||||||
|
// concatenate branches
|
||||||
|
nIndex = (nIndex << txBranch.size()) + nTxIndex;
|
||||||
|
branch.insert(branch.begin(), txBranch.begin(), txBranch.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the proof
|
||||||
|
if (nota.second.MoM != CBlock::CheckMerkleBranch(hash, branch, nIndex))
|
||||||
|
throw std::runtime_error("Failed validating MoM");
|
||||||
|
|
||||||
|
// All done!
|
||||||
|
CDataStream ssProof(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
return std::make_pair(nota.second.txHash, MerkleBranch(nIndex, branch));
|
||||||
|
}
|
||||||
21
src/crosschain.h
Normal file
21
src/crosschain.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#ifndef CROSSCHAIN_H
|
||||||
|
#define CROSSCHAIN_H
|
||||||
|
|
||||||
|
#include "cc/eval.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* On assetchain */
|
||||||
|
TxProof GetAssetchainProof(uint256 hash);
|
||||||
|
|
||||||
|
/* On KMD */
|
||||||
|
uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeight,
|
||||||
|
std::vector<uint256> &moms, uint256 &destNotarisationTxid);
|
||||||
|
TxProof GetCrossChainProof(const uint256 txid, const char* targetSymbol, uint32_t targetCCid,
|
||||||
|
const TxProof assetChainProof);
|
||||||
|
void CompleteImportTransaction(CTransaction &importTx);
|
||||||
|
|
||||||
|
/* On assetchain */
|
||||||
|
bool GetNextBacknotarisation(uint256 txid, std::pair<uint256,NotarisationData> &bn);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* CROSSCHAIN_H */
|
||||||
@@ -36,3 +36,5 @@ curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dp
|
|||||||
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\":\"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\":\"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\":\"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\"}"
|
||||||
|
|||||||
@@ -31,3 +31,5 @@ echo dsec; fiat/dsec $1 $2 $3 $4
|
|||||||
echo glxt; fiat/glxt $1 $2 $3 $4
|
echo glxt; fiat/glxt $1 $2 $3 $4
|
||||||
echo eql; fiat/eql $1 $2 $3 $4
|
echo eql; fiat/eql $1 $2 $3 $4
|
||||||
echo zilla; fiat/zilla $1 $2 $3 $4
|
echo zilla; fiat/zilla $1 $2 $3 $4
|
||||||
|
echo vrsc; fiat/vrsc $1 $2 $3 $4
|
||||||
|
echo rfox; fiat/rfox $1 $2 $3 $4
|
||||||
|
|||||||
2
src/fiat/rfox
Executable file
2
src/fiat/rfox
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
./komodo-cli -ac_name=RFOX $1 $2 $3 $4 $5 $6
|
||||||
2
src/fiat/vrsc
Executable file
2
src/fiat/vrsc
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
./komodo-cli -ac_name=VRSC $1 $2 $3 $4 $5 $6
|
||||||
0
src/fiat/zilla
Normal file → Executable file
0
src/fiat/zilla
Normal file → Executable file
121
src/importcoin.cpp
Normal file
121
src/importcoin.cpp
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
#include "crosschain.h"
|
||||||
|
#include "importcoin.h"
|
||||||
|
#include "cc/utils.h"
|
||||||
|
#include "coins.h"
|
||||||
|
#include "hash.h"
|
||||||
|
#include "script/cc.h"
|
||||||
|
#include "primitives/transaction.h"
|
||||||
|
|
||||||
|
|
||||||
|
CTransaction MakeImportCoinTransaction(const TxProof proof, const CTransaction burnTx, const std::vector<CTxOut> payouts)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> payload = E_MARSHAL(ss << EVAL_IMPORTCOIN);
|
||||||
|
CMutableTransaction mtx;
|
||||||
|
mtx.vin.push_back(CTxIn(COutPoint(burnTx.GetHash(), 10e8), CScript() << payload));
|
||||||
|
mtx.vout = payouts;
|
||||||
|
auto importData = E_MARSHAL(ss << proof; ss << burnTx);
|
||||||
|
mtx.vout.insert(mtx.vout.begin(), CTxOut(0, CScript() << OP_RETURN << importData));
|
||||||
|
return CTransaction(mtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector<CTxOut> payouts)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> opret = E_MARSHAL(ss << VARINT(targetCCid);
|
||||||
|
ss << targetSymbol;
|
||||||
|
ss << SerializeHash(payouts));
|
||||||
|
return CTxOut(value, CScript() << OP_RETURN << opret);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool UnmarshalImportTx(const CTransaction &importTx, TxProof &proof, CTransaction &burnTx,
|
||||||
|
std::vector<CTxOut> &payouts)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> vData;
|
||||||
|
GetOpReturnData(importTx.vout[0].scriptPubKey, vData);
|
||||||
|
if (importTx.vout.size() < 1) return false;
|
||||||
|
payouts = std::vector<CTxOut>(importTx.vout.begin()+1, importTx.vout.end());
|
||||||
|
return importTx.vin.size() == 1 &&
|
||||||
|
importTx.vin[0].scriptSig == (CScript() << E_MARSHAL(ss << EVAL_IMPORTCOIN)) &&
|
||||||
|
E_UNMARSHAL(vData, ss >> proof; ss >> burnTx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool UnmarshalBurnTx(const CTransaction &burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> burnOpret;
|
||||||
|
if (burnTx.vout.size() == 0) return false;
|
||||||
|
GetOpReturnData(burnTx.vout[0].scriptPubKey, burnOpret);
|
||||||
|
return E_UNMARSHAL(burnOpret, ss >> VARINT(*targetCCid);
|
||||||
|
ss >> targetSymbol;
|
||||||
|
ss >> payoutsHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Required by main
|
||||||
|
*/
|
||||||
|
CAmount GetCoinImportValue(const CTransaction &tx)
|
||||||
|
{
|
||||||
|
TxProof proof;
|
||||||
|
CTransaction burnTx;
|
||||||
|
std::vector<CTxOut> payouts;
|
||||||
|
if (UnmarshalImportTx(tx, proof, burnTx, payouts)) {
|
||||||
|
return burnTx.vout.size() ? burnTx.vout[0].nValue : 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CoinImport is different enough from normal script execution that it's not worth
|
||||||
|
* making all the mods neccesary in the interpreter to do the dispatch correctly.
|
||||||
|
*/
|
||||||
|
bool VerifyCoinImport(const CScript& scriptSig, TransactionSignatureChecker& checker, CValidationState &state)
|
||||||
|
{
|
||||||
|
auto pc = scriptSig.begin();
|
||||||
|
opcodetype opcode;
|
||||||
|
std::vector<uint8_t> evalScript;
|
||||||
|
|
||||||
|
auto f = [&] () {
|
||||||
|
if (!scriptSig.GetOp(pc, opcode, evalScript))
|
||||||
|
return false;
|
||||||
|
if (pc != scriptSig.end())
|
||||||
|
return false;
|
||||||
|
if (evalScript.size() == 0)
|
||||||
|
return false;
|
||||||
|
if (evalScript.begin()[0] != EVAL_IMPORTCOIN)
|
||||||
|
return false;
|
||||||
|
// Ok, all looks good so far...
|
||||||
|
CC *cond = CCNewEval(evalScript);
|
||||||
|
bool out = checker.CheckEvalCondition(cond);
|
||||||
|
cc_free(cond);
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
return f() ? true : state.Invalid(false, 0, "invalid-coin-import");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AddImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs, int nHeight)
|
||||||
|
{
|
||||||
|
uint256 burnHash = importTx.vin[0].prevout.hash;
|
||||||
|
CCoinsModifier modifier = inputs.ModifyCoins(burnHash);
|
||||||
|
modifier->nHeight = nHeight;
|
||||||
|
modifier->nVersion = 1;
|
||||||
|
modifier->vout.push_back(CTxOut(0, CScript() << OP_0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RemoveImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs)
|
||||||
|
{
|
||||||
|
uint256 burnHash = importTx.vin[0].prevout.hash;
|
||||||
|
inputs.ModifyCoins(burnHash)->Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int ExistsImportTombstone(const CTransaction &importTx, const CCoinsViewCache &inputs)
|
||||||
|
{
|
||||||
|
uint256 burnHash = importTx.vin[0].prevout.hash;
|
||||||
|
return inputs.HaveCoins(burnHash);
|
||||||
|
}
|
||||||
28
src/importcoin.h
Normal file
28
src/importcoin.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#ifndef IMPORTCOIN_H
|
||||||
|
#define IMPORTCOIN_H
|
||||||
|
|
||||||
|
#include "cc/eval.h"
|
||||||
|
#include "coins.h"
|
||||||
|
#include "primitives/transaction.h"
|
||||||
|
#include "script/interpreter.h"
|
||||||
|
#include <cryptoconditions.h>
|
||||||
|
|
||||||
|
|
||||||
|
CAmount GetCoinImportValue(const CTransaction &tx);
|
||||||
|
|
||||||
|
CTransaction MakeImportCoinTransaction(const TxProof proof,
|
||||||
|
const CTransaction burnTx, const std::vector<CTxOut> payouts);
|
||||||
|
|
||||||
|
CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector<CTxOut> payouts);
|
||||||
|
|
||||||
|
bool UnmarshalBurnTx(const CTransaction &burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash);
|
||||||
|
bool UnmarshalImportTx(const CTransaction &importTx, TxProof &proof, CTransaction &burnTx,
|
||||||
|
std::vector<CTxOut> &payouts);
|
||||||
|
|
||||||
|
bool VerifyCoinImport(const CScript& scriptSig, TransactionSignatureChecker& checker, CValidationState &state);
|
||||||
|
|
||||||
|
void AddImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs, int nHeight);
|
||||||
|
void RemoveImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs);
|
||||||
|
int ExistsImportTombstone(const CTransaction &importTx, const CCoinsViewCache &inputs);
|
||||||
|
|
||||||
|
#endif /* IMPORTCOIN_H */
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
#include "httpserver.h"
|
#include "httpserver.h"
|
||||||
#include "httprpc.h"
|
#include "httprpc.h"
|
||||||
#include "key.h"
|
#include "key.h"
|
||||||
|
#include "notarisationdb.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "metrics.h"
|
#include "metrics.h"
|
||||||
#include "miner.h"
|
#include "miner.h"
|
||||||
@@ -1413,11 +1414,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
|||||||
delete pcoinsdbview;
|
delete pcoinsdbview;
|
||||||
delete pcoinscatcher;
|
delete pcoinscatcher;
|
||||||
delete pblocktree;
|
delete pblocktree;
|
||||||
|
delete pnotarisations;
|
||||||
|
|
||||||
pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex, dbCompression, dbMaxOpenFiles);
|
pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex, dbCompression, dbMaxOpenFiles);
|
||||||
pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex);
|
pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex);
|
||||||
pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview);
|
pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview);
|
||||||
pcoinsTip = new CCoinsViewCache(pcoinscatcher);
|
pcoinsTip = new CCoinsViewCache(pcoinscatcher);
|
||||||
|
pnotarisations = new NotarisationDB(100*1024*1024, false, fReindex);
|
||||||
|
|
||||||
|
|
||||||
if (fReindex) {
|
if (fReindex) {
|
||||||
pblocktree->WriteReindexing(true);
|
pblocktree->WriteReindexing(true);
|
||||||
|
|||||||
@@ -1097,10 +1097,10 @@ uint32_t komodo_stake(int32_t validateflag,arith_uint256 bnTarget,int32_t nHeigh
|
|||||||
fprintf(stderr,"komodo_stake null %.8f %u %u %u\n",dstr(value),txtime,blocktime,prevtime);
|
fprintf(stderr,"komodo_stake null %.8f %u %u %u\n",dstr(value),txtime,blocktime,prevtime);
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
if ( (minage= nHeight*3) > 6000 )
|
if ( (minage= nHeight*3) > 6000 ) // about 100 blocks
|
||||||
minage = 6000;
|
minage = 6000;
|
||||||
pindex = 0;
|
pindex = 0;
|
||||||
if ( (pindex= komodo_chainactive(nHeight>200?nHeight-200:1)) != 0 )
|
if ( (pindex= komodo_chainactive(nHeight>50?nHeight-50:1)) != 0 )
|
||||||
{
|
{
|
||||||
vcalc_sha256(0,(uint8_t *)&addrhash,(uint8_t *)address,(int32_t)strlen(address));
|
vcalc_sha256(0,(uint8_t *)&addrhash,(uint8_t *)address,(int32_t)strlen(address));
|
||||||
segid = ((nHeight + addrhash.uints[0]) & 0x3f);
|
segid = ((nHeight + addrhash.uints[0]) & 0x3f);
|
||||||
@@ -1296,6 +1296,8 @@ int64_t komodo_checkcommission(CBlock *pblock,int32_t height)
|
|||||||
return(checktoshis);
|
return(checktoshis);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool KOMODO_TEST_ASSETCHAIN_SKIP_POW = 0;
|
||||||
|
|
||||||
int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height)
|
int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height)
|
||||||
{
|
{
|
||||||
uint256 hash; arith_uint256 bnTarget,bhash; bool fNegative,fOverflow; uint8_t *script,pubkey33[33],pubkeys[64][33]; int32_t i,possible,PoSperc,is_PoSblock=0,n,failed = 0,notaryid = -1; int64_t checktoshis,value; CBlockIndex *pprev;
|
uint256 hash; arith_uint256 bnTarget,bhash; bool fNegative,fOverflow; uint8_t *script,pubkey33[33],pubkeys[64][33]; int32_t i,possible,PoSperc,is_PoSblock=0,n,failed = 0,notaryid = -1; int64_t checktoshis,value; CBlockIndex *pprev;
|
||||||
@@ -1335,6 +1337,7 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height)
|
|||||||
}
|
}
|
||||||
else if ( possible == 0 || ASSETCHAINS_SYMBOL[0] != 0 )
|
else if ( possible == 0 || ASSETCHAINS_SYMBOL[0] != 0 )
|
||||||
{
|
{
|
||||||
|
if (KOMODO_TEST_ASSETCHAIN_SKIP_POW) return(0);
|
||||||
fprintf(stderr,"pow violation and no chance it is notary ht.%d %s\n",height,hash.ToString().c_str());
|
fprintf(stderr,"pow violation and no chance it is notary ht.%d %s\n",height,hash.ToString().c_str());
|
||||||
return(-1);
|
return(-1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,54 +19,29 @@
|
|||||||
struct komodo_ccdata *CC_data;
|
struct komodo_ccdata *CC_data;
|
||||||
int32_t CC_firstheight;
|
int32_t CC_firstheight;
|
||||||
|
|
||||||
bits256 iguana_merkle(bits256 *tree,int32_t txn_count)
|
uint256 BuildMerkleTree(bool* fMutated, const std::vector<uint256> leaves, std::vector<uint256> &vMerkleTree);
|
||||||
{
|
|
||||||
int32_t i,n=0,prev; uint8_t serialized[sizeof(bits256) * 2];
|
|
||||||
if ( txn_count == 1 )
|
|
||||||
return(tree[0]);
|
|
||||||
prev = 0;
|
|
||||||
while ( txn_count > 1 )
|
|
||||||
{
|
|
||||||
if ( (txn_count & 1) != 0 )
|
|
||||||
tree[prev + txn_count] = tree[prev + txn_count-1], txn_count++;
|
|
||||||
n += txn_count;
|
|
||||||
for (i=0; i<txn_count; i+=2)
|
|
||||||
{
|
|
||||||
iguana_rwbignum(1,serialized,sizeof(*tree),tree[prev + i].bytes);
|
|
||||||
iguana_rwbignum(1,&serialized[sizeof(*tree)],sizeof(*tree),tree[prev + i + 1].bytes);
|
|
||||||
tree[n + (i >> 1)] = bits256_doublesha256(0,serialized,sizeof(serialized));
|
|
||||||
}
|
|
||||||
prev = n;
|
|
||||||
txn_count >>= 1;
|
|
||||||
}
|
|
||||||
return(tree[n]);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth)
|
uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth)
|
||||||
{
|
{
|
||||||
static uint256 zero; bits256 MoM,*tree; CBlockIndex *pindex; int32_t i;
|
static uint256 zero; CBlockIndex *pindex; int32_t i; std::vector<uint256> tree, leaves;
|
||||||
MoMdepth &= 0xffff;
|
bool fMutated;
|
||||||
|
MoMdepth &= 0xffff; // In case it includes the ccid
|
||||||
if ( MoMdepth >= height )
|
if ( MoMdepth >= height )
|
||||||
return(zero);
|
return(zero);
|
||||||
tree = (bits256 *)calloc(MoMdepth * 3,sizeof(*tree));
|
|
||||||
for (i=0; i<MoMdepth; i++)
|
for (i=0; i<MoMdepth; i++)
|
||||||
{
|
{
|
||||||
if ( (pindex= komodo_chainactive(height - i)) != 0 )
|
if ( (pindex= komodo_chainactive(height - i)) != 0 )
|
||||||
memcpy(&tree[i],&pindex->hashMerkleRoot,sizeof(bits256));
|
leaves.push_back(pindex->hashMerkleRoot);
|
||||||
else
|
else
|
||||||
{
|
|
||||||
free(tree);
|
|
||||||
return(zero);
|
return(zero);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
MoM = iguana_merkle(tree,MoMdepth);
|
return BuildMerkleTree(&fMutated, leaves, tree);
|
||||||
free(tree);
|
|
||||||
return(*(uint256 *)&MoM);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct komodo_ccdata_entry *komodo_allMoMs(int32_t *nump,uint256 *MoMoMp,int32_t kmdstarti,int32_t kmdendi)
|
struct komodo_ccdata_entry *komodo_allMoMs(int32_t *nump,uint256 *MoMoMp,int32_t kmdstarti,int32_t kmdendi)
|
||||||
{
|
{
|
||||||
struct komodo_ccdata_entry *allMoMs=0; bits256 *tree,tmp; struct komodo_ccdata *ccdata,*tmpptr; int32_t i,num,max;
|
struct komodo_ccdata_entry *allMoMs=0; struct komodo_ccdata *ccdata,*tmpptr; int32_t i,num,max;
|
||||||
|
bool fMutated; std::vector<uint256> tree, leaves;
|
||||||
num = max = 0;
|
num = max = 0;
|
||||||
portable_mutex_lock(&KOMODO_CC_mutex);
|
portable_mutex_lock(&KOMODO_CC_mutex);
|
||||||
DL_FOREACH_SAFE(CC_data,ccdata,tmpptr)
|
DL_FOREACH_SAFE(CC_data,ccdata,tmpptr)
|
||||||
@@ -91,11 +66,9 @@ struct komodo_ccdata_entry *komodo_allMoMs(int32_t *nump,uint256 *MoMoMp,int32_t
|
|||||||
portable_mutex_unlock(&KOMODO_CC_mutex);
|
portable_mutex_unlock(&KOMODO_CC_mutex);
|
||||||
if ( (*nump= num) > 0 )
|
if ( (*nump= num) > 0 )
|
||||||
{
|
{
|
||||||
tree = (bits256 *)calloc(sizeof(bits256),num*3);
|
|
||||||
for (i=0; i<num; i++)
|
for (i=0; i<num; i++)
|
||||||
memcpy(&tree[i],&allMoMs[i].MoM,sizeof(tree[i]));
|
leaves.push_back(allMoMs[i].MoM);
|
||||||
tmp = iguana_merkle(tree,num);
|
*MoMoMp = BuildMerkleTree(&fMutated, leaves, tree);
|
||||||
memcpy(MoMoMp,&tmp,sizeof(*MoMoMp));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -384,18 +384,35 @@ int32_t komodo_chosennotary(int32_t *notaryidp,int32_t height,uint8_t *pubkey33,
|
|||||||
|
|
||||||
//struct komodo_state *komodo_stateptr(char *symbol,char *dest);
|
//struct komodo_state *komodo_stateptr(char *symbol,char *dest);
|
||||||
|
|
||||||
struct notarized_checkpoint *komodo_npptr(int32_t height)
|
struct notarized_checkpoint *komodo_npptr_for_height(int32_t height, int *idx)
|
||||||
{
|
{
|
||||||
char symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; int32_t i; struct komodo_state *sp; struct notarized_checkpoint *np = 0;
|
char symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; int32_t i; struct komodo_state *sp; struct notarized_checkpoint *np = 0;
|
||||||
if ( (sp= komodo_stateptr(symbol,dest)) != 0 )
|
if ( (sp= komodo_stateptr(symbol,dest)) != 0 )
|
||||||
{
|
{
|
||||||
for (i=sp->NUM_NPOINTS-1; i>=0; i--)
|
for (i=sp->NUM_NPOINTS-1; i>=0; i--)
|
||||||
{
|
{
|
||||||
|
*idx = i;
|
||||||
np = &sp->NPOINTS[i];
|
np = &sp->NPOINTS[i];
|
||||||
if ( np->MoMdepth != 0 && height > np->notarized_height-(np->MoMdepth&0xffff) && height <= np->notarized_height )
|
if ( np->MoMdepth != 0 && height > np->notarized_height-(np->MoMdepth&0xffff) && height <= np->notarized_height )
|
||||||
return(np);
|
return(np);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*idx = -1;
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct notarized_checkpoint *komodo_npptr(int32_t height)
|
||||||
|
{
|
||||||
|
int idx;
|
||||||
|
return komodo_npptr_for_height(height, &idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct notarized_checkpoint *komodo_npptr_at(int idx)
|
||||||
|
{
|
||||||
|
char symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; struct komodo_state *sp;
|
||||||
|
if ( (sp= komodo_stateptr(symbol,dest)) != 0 )
|
||||||
|
if (idx < sp->NUM_NPOINTS)
|
||||||
|
return &sp->NPOINTS[idx];
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
207
src/main.cpp
207
src/main.cpp
@@ -10,6 +10,7 @@
|
|||||||
#include "addrman.h"
|
#include "addrman.h"
|
||||||
#include "alert.h"
|
#include "alert.h"
|
||||||
#include "arith_uint256.h"
|
#include "arith_uint256.h"
|
||||||
|
#include "importcoin.h"
|
||||||
#include "chainparams.h"
|
#include "chainparams.h"
|
||||||
#include "checkpoints.h"
|
#include "checkpoints.h"
|
||||||
#include "checkqueue.h"
|
#include "checkqueue.h"
|
||||||
@@ -19,8 +20,10 @@
|
|||||||
#include "init.h"
|
#include "init.h"
|
||||||
#include "merkleblock.h"
|
#include "merkleblock.h"
|
||||||
#include "metrics.h"
|
#include "metrics.h"
|
||||||
|
#include "notarisationdb.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
#include "pow.h"
|
#include "pow.h"
|
||||||
|
#include "script/interpreter.h"
|
||||||
#include "txdb.h"
|
#include "txdb.h"
|
||||||
#include "txmempool.h"
|
#include "txmempool.h"
|
||||||
#include "ui_interface.h"
|
#include "ui_interface.h"
|
||||||
@@ -826,7 +829,10 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs,
|
|||||||
{
|
{
|
||||||
if (tx.IsCoinBase())
|
if (tx.IsCoinBase())
|
||||||
return true; // Coinbases don't use vin normally
|
return true; // Coinbases don't use vin normally
|
||||||
|
|
||||||
|
if (tx.IsCoinImport())
|
||||||
|
return tx.vin[0].scriptSig.IsCoinImport();
|
||||||
|
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||||
{
|
{
|
||||||
const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]);
|
const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]);
|
||||||
@@ -897,7 +903,7 @@ unsigned int GetLegacySigOpCount(const CTransaction& tx)
|
|||||||
|
|
||||||
unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& inputs)
|
unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& inputs)
|
||||||
{
|
{
|
||||||
if (tx.IsCoinBase())
|
if (tx.IsCoinBase() || tx.IsCoinImport())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
unsigned int nSigOps = 0;
|
unsigned int nSigOps = 0;
|
||||||
@@ -954,8 +960,8 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state,
|
|||||||
return state.DoS(dosLevel, error("ContextualCheckTransaction(): transaction is expired"), REJECT_INVALID, "tx-overwinter-expired");
|
return state.DoS(dosLevel, error("ContextualCheckTransaction(): transaction is expired"), REJECT_INVALID, "tx-overwinter-expired");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(tx.IsCoinBase() || tx.vjoinsplit.empty())) {
|
if (!(tx.IsMint() || tx.vjoinsplit.empty())) {
|
||||||
auto consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus());
|
auto consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus());
|
||||||
// Empty output script.
|
// Empty output script.
|
||||||
CScript scriptCode;
|
CScript scriptCode;
|
||||||
@@ -1010,7 +1016,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state,
|
|||||||
if (!CheckTransactionWithoutProofVerification(tx, state)) {
|
if (!CheckTransactionWithoutProofVerification(tx, state)) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// Ensure that zk-SNARKs verify
|
// Ensure that zk-SNARKs v|| y
|
||||||
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
|
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
|
||||||
if (!joinsplit.Verify(*pzcashParams, verifier, tx.joinSplitPubKey)) {
|
if (!joinsplit.Verify(*pzcashParams, verifier, tx.joinSplitPubKey)) {
|
||||||
return state.DoS(100, error("CheckTransaction(): joinsplit does not verify"),
|
return state.DoS(100, error("CheckTransaction(): joinsplit does not verify"),
|
||||||
@@ -1068,6 +1074,7 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio
|
|||||||
|
|
||||||
// Transactions can contain empty `vin` and `vout` so long as
|
// Transactions can contain empty `vin` and `vout` so long as
|
||||||
// `vjoinsplit` is non-empty.
|
// `vjoinsplit` is non-empty.
|
||||||
|
// Migrations may also have empty `vin`
|
||||||
if (tx.vin.empty() && tx.vjoinsplit.empty())
|
if (tx.vin.empty() && tx.vjoinsplit.empty())
|
||||||
return state.DoS(10, error("CheckTransaction(): vin empty"),
|
return state.DoS(10, error("CheckTransaction(): vin empty"),
|
||||||
REJECT_INVALID, "bad-txns-vin-empty");
|
REJECT_INVALID, "bad-txns-vin-empty");
|
||||||
@@ -1176,7 +1183,7 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tx.IsCoinBase())
|
if (tx.IsMint())
|
||||||
{
|
{
|
||||||
// There should be no joinsplits in a coinbase transaction
|
// There should be no joinsplits in a coinbase transaction
|
||||||
if (tx.vjoinsplit.size() > 0)
|
if (tx.vjoinsplit.size() > 0)
|
||||||
@@ -1293,7 +1300,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||||||
if (pool.exists(hash))
|
if (pool.exists(hash))
|
||||||
{
|
{
|
||||||
fprintf(stderr,"already in mempool\n");
|
fprintf(stderr,"already in mempool\n");
|
||||||
return false;
|
return state.Invalid(false, REJECT_DUPLICATE, "already in mempool");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for conflicts with in-memory transactions
|
// Check for conflicts with in-memory transactions
|
||||||
@@ -1338,28 +1345,37 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||||||
if (view.HaveCoins(hash))
|
if (view.HaveCoins(hash))
|
||||||
{
|
{
|
||||||
fprintf(stderr,"view.HaveCoins(hash) error\n");
|
fprintf(stderr,"view.HaveCoins(hash) error\n");
|
||||||
return false;
|
return state.Invalid(false, REJECT_DUPLICATE, "already have coins");
|
||||||
}
|
}
|
||||||
|
|
||||||
// do all inputs exist?
|
if (tx.IsCoinImport())
|
||||||
// Note that this does not check for the presence of actual outputs (see the next check for that),
|
|
||||||
// and only helps with filling in pfMissingInputs (to determine missing vs spent).
|
|
||||||
BOOST_FOREACH(const CTxIn txin, tx.vin)
|
|
||||||
{
|
{
|
||||||
if (!view.HaveCoins(txin.prevout.hash))
|
// Inverse of normal case; if input exists, it's been spent
|
||||||
|
if (ExistsImportTombstone(tx, view))
|
||||||
|
return state.Invalid(false, REJECT_DUPLICATE, "import tombstone exists");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// do all inputs exist?
|
||||||
|
// Note that this does not check for the presence of actual outputs (see the next check for that),
|
||||||
|
// and only helps with filling in pfMissingInputs (to determine missing vs spent).
|
||||||
|
BOOST_FOREACH(const CTxIn txin, tx.vin)
|
||||||
{
|
{
|
||||||
if (pfMissingInputs)
|
if (!view.HaveCoins(txin.prevout.hash))
|
||||||
*pfMissingInputs = true;
|
{
|
||||||
//fprintf(stderr,"missing inputs\n");
|
if (pfMissingInputs)
|
||||||
return false;
|
*pfMissingInputs = true;
|
||||||
|
//fprintf(stderr,"missing inputs\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// are the actual inputs available?
|
||||||
|
if (!view.HaveInputs(tx))
|
||||||
|
{
|
||||||
|
//fprintf(stderr,"accept failure.1\n");
|
||||||
|
return state.Invalid(error("AcceptToMemoryPool: inputs already spent"),REJECT_DUPLICATE, "bad-txns-inputs-spent");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// are the actual inputs available?
|
|
||||||
if (!view.HaveInputs(tx))
|
|
||||||
{
|
|
||||||
//fprintf(stderr,"accept failure.1\n");
|
|
||||||
return state.Invalid(error("AcceptToMemoryPool: inputs already spent"),REJECT_DUPLICATE, "bad-txns-inputs-spent");
|
|
||||||
}
|
}
|
||||||
// are the joinsplit's requirements met?
|
// are the joinsplit's requirements met?
|
||||||
if (!view.HaveJoinSplitRequirements(tx))
|
if (!view.HaveJoinSplitRequirements(tx))
|
||||||
@@ -1402,11 +1418,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||||||
// Keep track of transactions that spend a coinbase, which we re-scan
|
// Keep track of transactions that spend a coinbase, which we re-scan
|
||||||
// during reorgs to ensure COINBASE_MATURITY is still met.
|
// during reorgs to ensure COINBASE_MATURITY is still met.
|
||||||
bool fSpendsCoinbase = false;
|
bool fSpendsCoinbase = false;
|
||||||
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
|
if (!tx.IsCoinImport()) {
|
||||||
const CCoins *coins = view.AccessCoins(txin.prevout.hash);
|
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
|
||||||
if (coins->IsCoinBase()) {
|
const CCoins *coins = view.AccessCoins(txin.prevout.hash);
|
||||||
fSpendsCoinbase = true;
|
if (coins->IsCoinBase()) {
|
||||||
break;
|
fSpendsCoinbase = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1487,6 +1505,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||||||
// There is a similar check in CreateNewBlock() to prevent creating
|
// There is a similar check in CreateNewBlock() to prevent creating
|
||||||
// invalid blocks, however allowing such transactions into the mempool
|
// invalid blocks, however allowing such transactions into the mempool
|
||||||
// can be exploited as a DoS attack.
|
// can be exploited as a DoS attack.
|
||||||
|
// XXX: is this neccesary for CryptoConditions?
|
||||||
if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId))
|
if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId))
|
||||||
{
|
{
|
||||||
fprintf(stderr,"accept failure.10\n");
|
fprintf(stderr,"accept failure.10\n");
|
||||||
@@ -1976,7 +1995,7 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state
|
|||||||
|
|
||||||
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight)
|
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight)
|
||||||
{
|
{
|
||||||
if (!tx.IsCoinBase()) // mark inputs spent
|
if (!tx.IsMint()) // mark inputs spent
|
||||||
{
|
{
|
||||||
txundo.vprevout.reserve(tx.vin.size());
|
txundo.vprevout.reserve(tx.vin.size());
|
||||||
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
|
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
|
||||||
@@ -2002,6 +2021,13 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight); // add outputs
|
inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight); // add outputs
|
||||||
|
|
||||||
|
// Unorthodox state
|
||||||
|
if (tx.IsCoinImport()) {
|
||||||
|
// add a tombstone for the burnTx
|
||||||
|
AddImportTombstone(tx, inputs, nHeight);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
|
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
|
||||||
@@ -2012,7 +2038,8 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
|
|||||||
|
|
||||||
bool CScriptCheck::operator()() {
|
bool CScriptCheck::operator()() {
|
||||||
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
|
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
|
||||||
if (!VerifyScript(scriptSig, scriptPubKey, nFlags, ServerTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), consensusBranchId, &error)) {
|
ServerTransactionSignatureChecker checker(ptxTo, nIn, amount, cacheStore, *txdata);
|
||||||
|
if (!VerifyScript(scriptSig, scriptPubKey, nFlags, checker, consensusBranchId, &error)) {
|
||||||
return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error));
|
return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -2122,7 +2149,7 @@ bool ContextualCheckInputs(
|
|||||||
uint32_t consensusBranchId,
|
uint32_t consensusBranchId,
|
||||||
std::vector<CScriptCheck> *pvChecks)
|
std::vector<CScriptCheck> *pvChecks)
|
||||||
{
|
{
|
||||||
if (!tx.IsCoinBase())
|
if (!tx.IsMint())
|
||||||
{
|
{
|
||||||
if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs), consensusParams)) {
|
if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs), consensusParams)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -2174,7 +2201,13 @@ bool ContextualCheckInputs(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tx.IsCoinImport())
|
||||||
|
{
|
||||||
|
ServerTransactionSignatureChecker checker(&tx, 0, 0, false, txdata);
|
||||||
|
return VerifyCoinImport(tx.vin[0].scriptSig, checker, state);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2325,6 +2358,37 @@ static bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const CO
|
|||||||
return fClean;
|
return fClean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ConnectNotarisations(const CBlock &block, int height)
|
||||||
|
{
|
||||||
|
// Record Notarisations
|
||||||
|
NotarisationsInBlock notarisations = ScanBlockNotarisations(block, height);
|
||||||
|
if (notarisations.size() > 0) {
|
||||||
|
CLevelDBBatch batch;
|
||||||
|
batch.Write(block.GetHash(), notarisations);
|
||||||
|
WriteBackNotarisations(notarisations, batch);
|
||||||
|
pnotarisations->WriteBatch(batch, true);
|
||||||
|
LogPrintf("ConnectBlock: wrote %i block notarisations in block: %s\n",
|
||||||
|
notarisations.size(), block.GetHash().GetHex().data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DisconnectNotarisations(const CBlock &block)
|
||||||
|
{
|
||||||
|
// Delete from notarisations cache
|
||||||
|
NotarisationsInBlock nibs;
|
||||||
|
if (GetBlockNotarisations(block.GetHash(), nibs)) {
|
||||||
|
CLevelDBBatch batch;
|
||||||
|
batch.Erase(block.GetHash());
|
||||||
|
EraseBackNotarisations(nibs, batch);
|
||||||
|
pnotarisations->WriteBatch(batch, true);
|
||||||
|
LogPrintf("DisconnectTip: deleted %i block notarisations in block: %s\n",
|
||||||
|
nibs.size(), block.GetHash().GetHex().data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean)
|
bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean)
|
||||||
{
|
{
|
||||||
assert(pindex->GetBlockHash() == view.GetBestBlock());
|
assert(pindex->GetBlockHash() == view.GetBestBlock());
|
||||||
@@ -2365,7 +2429,8 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
|
|||||||
// undo unspent index
|
// undo unspent index
|
||||||
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(2, uint160(hashBytes), hash, k), CAddressUnspentValue()));
|
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(2, uint160(hashBytes), hash, k), CAddressUnspentValue()));
|
||||||
|
|
||||||
} else if (out.scriptPubKey.IsPayToPublicKeyHash()) {
|
}
|
||||||
|
else if (out.scriptPubKey.IsPayToPublicKeyHash()) {
|
||||||
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23);
|
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23);
|
||||||
|
|
||||||
// undo receiving activity
|
// undo receiving activity
|
||||||
@@ -2374,7 +2439,18 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
|
|||||||
// undo unspent index
|
// undo unspent index
|
||||||
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, uint160(hashBytes), hash, k), CAddressUnspentValue()));
|
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, uint160(hashBytes), hash, k), CAddressUnspentValue()));
|
||||||
|
|
||||||
} else {
|
}
|
||||||
|
else if (out.scriptPubKey.IsPayToPublicKey()) {
|
||||||
|
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34);
|
||||||
|
|
||||||
|
// undo receiving activity
|
||||||
|
addressIndex.push_back(make_pair(CAddressIndexKey(1, Hash160(hashBytes), pindex->nHeight, i, hash, k, false), out.nValue));
|
||||||
|
|
||||||
|
// undo unspent index
|
||||||
|
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, Hash160(hashBytes), hash, k), CAddressUnspentValue()));
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2409,7 +2485,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
|
|||||||
}
|
}
|
||||||
|
|
||||||
// restore inputs
|
// restore inputs
|
||||||
if (i > 0) { // not coinbases
|
if (!tx.IsMint()) {
|
||||||
const CTxUndo &txundo = blockUndo.vtxundo[i-1];
|
const CTxUndo &txundo = blockUndo.vtxundo[i-1];
|
||||||
if (txundo.vprevout.size() != tx.vin.size())
|
if (txundo.vprevout.size() != tx.vin.size())
|
||||||
return error("DisconnectBlock(): transaction and undo data inconsistent");
|
return error("DisconnectBlock(): transaction and undo data inconsistent");
|
||||||
@@ -2438,7 +2514,8 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
|
|||||||
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(2, uint160(hashBytes), input.prevout.hash, input.prevout.n), CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight)));
|
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(2, uint160(hashBytes), input.prevout.hash, input.prevout.n), CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight)));
|
||||||
|
|
||||||
|
|
||||||
} else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) {
|
}
|
||||||
|
else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) {
|
||||||
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23);
|
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23);
|
||||||
|
|
||||||
// undo spending activity
|
// undo spending activity
|
||||||
@@ -2447,14 +2524,29 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
|
|||||||
// restore unspent index
|
// restore unspent index
|
||||||
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, uint160(hashBytes), input.prevout.hash, input.prevout.n), CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight)));
|
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, uint160(hashBytes), input.prevout.hash, input.prevout.n), CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight)));
|
||||||
|
|
||||||
} else {
|
}
|
||||||
|
else if (prevout.scriptPubKey.IsPayToPublicKey()) {
|
||||||
|
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34);
|
||||||
|
|
||||||
|
// undo spending activity
|
||||||
|
addressIndex.push_back(make_pair(CAddressIndexKey(1, Hash160(hashBytes), pindex->nHeight, 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;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (tx.IsCoinImport())
|
||||||
|
{
|
||||||
|
RemoveImportTombstone(tx, view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the old best anchor back
|
// set the old best anchor back
|
||||||
view.PopAnchor(blockUndo.old_tree_root);
|
view.PopAnchor(blockUndo.old_tree_root);
|
||||||
|
|
||||||
@@ -2474,6 +2566,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
|
|||||||
return AbortNode(state, "Failed to write address unspent index");
|
return AbortNode(state, "Failed to write address unspent index");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fClean;
|
return fClean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2571,6 +2664,7 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int64_t nTimeVerify = 0;
|
static int64_t nTimeVerify = 0;
|
||||||
static int64_t nTimeConnect = 0;
|
static int64_t nTimeConnect = 0;
|
||||||
static int64_t nTimeIndex = 0;
|
static int64_t nTimeIndex = 0;
|
||||||
@@ -2690,7 +2784,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||||||
return state.DoS(100, error("ConnectBlock(): too many sigops"),
|
return state.DoS(100, error("ConnectBlock(): too many sigops"),
|
||||||
REJECT_INVALID, "bad-blk-sigops");
|
REJECT_INVALID, "bad-blk-sigops");
|
||||||
//fprintf(stderr,"ht.%d vout0 t%u\n",pindex->nHeight,tx.nLockTime);
|
//fprintf(stderr,"ht.%d vout0 t%u\n",pindex->nHeight,tx.nLockTime);
|
||||||
if (!tx.IsCoinBase())
|
if (!tx.IsMint())
|
||||||
{
|
{
|
||||||
if (!view.HaveInputs(tx))
|
if (!view.HaveInputs(tx))
|
||||||
{
|
{
|
||||||
@@ -2713,10 +2807,16 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||||||
if (prevout.scriptPubKey.IsPayToScriptHash()) {
|
if (prevout.scriptPubKey.IsPayToScriptHash()) {
|
||||||
hashBytes = uint160(vector <unsigned char>(prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22));
|
hashBytes = uint160(vector <unsigned char>(prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22));
|
||||||
addressType = 2;
|
addressType = 2;
|
||||||
} else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) {
|
}
|
||||||
|
else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) {
|
||||||
hashBytes = uint160(vector <unsigned char>(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23));
|
hashBytes = uint160(vector <unsigned char>(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23));
|
||||||
addressType = 1;
|
addressType = 1;
|
||||||
} else {
|
}
|
||||||
|
else if (prevout.scriptPubKey.IsPayToPublicKey()) {
|
||||||
|
hashBytes = Hash160(vector <unsigned char>(prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34));
|
||||||
|
addressType = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
hashBytes.SetNull();
|
hashBytes.SetNull();
|
||||||
addressType = 0;
|
addressType = 0;
|
||||||
}
|
}
|
||||||
@@ -2762,7 +2862,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||||||
if (fAddressIndex) {
|
if (fAddressIndex) {
|
||||||
for (unsigned int k = 0; k < tx.vout.size(); k++) {
|
for (unsigned int k = 0; k < tx.vout.size(); k++) {
|
||||||
const CTxOut &out = tx.vout[k];
|
const CTxOut &out = tx.vout[k];
|
||||||
|
//fprintf(stderr,"add %d vouts\n",(int32_t)tx.vout.size());
|
||||||
if (out.scriptPubKey.IsPayToScriptHash()) {
|
if (out.scriptPubKey.IsPayToScriptHash()) {
|
||||||
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22);
|
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22);
|
||||||
|
|
||||||
@@ -2772,7 +2872,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||||||
// record unspent output
|
// record unspent output
|
||||||
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(2, uint160(hashBytes), txhash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->nHeight)));
|
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(2, uint160(hashBytes), txhash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->nHeight)));
|
||||||
|
|
||||||
} else if (out.scriptPubKey.IsPayToPublicKeyHash()) {
|
}
|
||||||
|
else if (out.scriptPubKey.IsPayToPublicKeyHash()) {
|
||||||
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23);
|
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23);
|
||||||
|
|
||||||
// record receiving activity
|
// record receiving activity
|
||||||
@@ -2781,7 +2882,18 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||||||
// record unspent output
|
// record unspent output
|
||||||
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, uint160(hashBytes), txhash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->nHeight)));
|
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, uint160(hashBytes), txhash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->nHeight)));
|
||||||
|
|
||||||
} else {
|
}
|
||||||
|
else if (out.scriptPubKey.IsPayToPublicKey()) {
|
||||||
|
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34);
|
||||||
|
|
||||||
|
// record receiving activity
|
||||||
|
addressIndex.push_back(make_pair(CAddressIndexKey(1, Hash160(hashBytes), pindex->nHeight, i, txhash, k, false), out.nValue));
|
||||||
|
|
||||||
|
// record unspent output
|
||||||
|
addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, Hash160(hashBytes), txhash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->nHeight)));
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2807,7 +2919,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||||||
vPos.push_back(std::make_pair(tx.GetHash(), pos));
|
vPos.push_back(std::make_pair(tx.GetHash(), pos));
|
||||||
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
|
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
view.PushAnchor(tree);
|
view.PushAnchor(tree);
|
||||||
if (!fJustCheck) {
|
if (!fJustCheck) {
|
||||||
pindex->hashAnchorEnd = tree.root();
|
pindex->hashAnchorEnd = tree.root();
|
||||||
@@ -2876,6 +2988,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||||||
pindex->RaiseValidity(BLOCK_VALID_SCRIPTS);
|
pindex->RaiseValidity(BLOCK_VALID_SCRIPTS);
|
||||||
setDirtyBlockIndex.insert(pindex);
|
setDirtyBlockIndex.insert(pindex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConnectNotarisations(block, pindex->nHeight);
|
||||||
|
|
||||||
if (fTxIndex)
|
if (fTxIndex)
|
||||||
if (!pblocktree->WriteTxIndex(vPos))
|
if (!pblocktree->WriteTxIndex(vPos))
|
||||||
@@ -3113,6 +3227,7 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) {
|
|||||||
if (!DisconnectBlock(block, state, pindexDelete, view))
|
if (!DisconnectBlock(block, state, pindexDelete, view))
|
||||||
return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString());
|
return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString());
|
||||||
assert(view.Flush());
|
assert(view.Flush());
|
||||||
|
DisconnectNotarisations(block);
|
||||||
}
|
}
|
||||||
LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
|
LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
|
||||||
uint256 anchorAfterDisconnect = pcoinsTip->GetBestAnchor();
|
uint256 anchorAfterDisconnect = pcoinsTip->GetBestAnchor();
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "amount.h"
|
#include "amount.h"
|
||||||
#include "base58.h"
|
#include "base58.h"
|
||||||
#include "chainparams.h"
|
#include "chainparams.h"
|
||||||
|
#include "importcoin.h"
|
||||||
#include "consensus/consensus.h"
|
#include "consensus/consensus.h"
|
||||||
#include "consensus/upgrades.h"
|
#include "consensus/upgrades.h"
|
||||||
#include "consensus/validation.h"
|
#include "consensus/validation.h"
|
||||||
@@ -230,48 +231,55 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn,int32_t gpucount)
|
|||||||
double dPriority = 0;
|
double dPriority = 0;
|
||||||
CAmount nTotalIn = 0;
|
CAmount nTotalIn = 0;
|
||||||
bool fMissingInputs = false;
|
bool fMissingInputs = false;
|
||||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
if (tx.IsCoinImport())
|
||||||
{
|
{
|
||||||
// Read prev transaction
|
CAmount nValueIn = GetCoinImportValue(tx);
|
||||||
if (!view.HaveCoins(txin.prevout.hash))
|
|
||||||
{
|
|
||||||
// This should never happen; all transactions in the memory
|
|
||||||
// pool should connect to either transactions in the chain
|
|
||||||
// or other transactions in the memory pool.
|
|
||||||
if (!mempool.mapTx.count(txin.prevout.hash))
|
|
||||||
{
|
|
||||||
LogPrintf("ERROR: mempool transaction missing input\n");
|
|
||||||
if (fDebug) assert("mempool transaction missing input" == 0);
|
|
||||||
fMissingInputs = true;
|
|
||||||
if (porphan)
|
|
||||||
vOrphan.pop_back();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Has to wait for dependencies
|
|
||||||
if (!porphan)
|
|
||||||
{
|
|
||||||
// Use list for automatic deletion
|
|
||||||
vOrphan.push_back(COrphan(&tx));
|
|
||||||
porphan = &vOrphan.back();
|
|
||||||
}
|
|
||||||
mapDependers[txin.prevout.hash].push_back(porphan);
|
|
||||||
porphan->setDependsOn.insert(txin.prevout.hash);
|
|
||||||
nTotalIn += mempool.mapTx.find(txin.prevout.hash)->GetTx().vout[txin.prevout.n].nValue;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
|
|
||||||
assert(coins);
|
|
||||||
|
|
||||||
CAmount nValueIn = coins->vout[txin.prevout.n].nValue;
|
|
||||||
nTotalIn += nValueIn;
|
nTotalIn += nValueIn;
|
||||||
|
dPriority += (double)nValueIn * 1000; // flat multiplier
|
||||||
int nConf = nHeight - coins->nHeight;
|
} else {
|
||||||
|
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
||||||
dPriority += (double)nValueIn * nConf;
|
{
|
||||||
|
// Read prev transaction
|
||||||
|
if (!view.HaveCoins(txin.prevout.hash))
|
||||||
|
{
|
||||||
|
// This should never happen; all transactions in the memory
|
||||||
|
// pool should connect to either transactions in the chain
|
||||||
|
// or other transactions in the memory pool.
|
||||||
|
if (!mempool.mapTx.count(txin.prevout.hash))
|
||||||
|
{
|
||||||
|
LogPrintf("ERROR: mempool transaction missing input\n");
|
||||||
|
if (fDebug) assert("mempool transaction missing input" == 0);
|
||||||
|
fMissingInputs = true;
|
||||||
|
if (porphan)
|
||||||
|
vOrphan.pop_back();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has to wait for dependencies
|
||||||
|
if (!porphan)
|
||||||
|
{
|
||||||
|
// Use list for automatic deletion
|
||||||
|
vOrphan.push_back(COrphan(&tx));
|
||||||
|
porphan = &vOrphan.back();
|
||||||
|
}
|
||||||
|
mapDependers[txin.prevout.hash].push_back(porphan);
|
||||||
|
porphan->setDependsOn.insert(txin.prevout.hash);
|
||||||
|
nTotalIn += mempool.mapTx.find(txin.prevout.hash)->GetTx().vout[txin.prevout.n].nValue;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
|
||||||
|
assert(coins);
|
||||||
|
|
||||||
|
CAmount nValueIn = coins->vout[txin.prevout.n].nValue;
|
||||||
|
nTotalIn += nValueIn;
|
||||||
|
|
||||||
|
int nConf = nHeight - coins->nHeight;
|
||||||
|
|
||||||
|
dPriority += (double)nValueIn * nConf;
|
||||||
|
}
|
||||||
|
nTotalIn += tx.GetJoinSplitValueIn();
|
||||||
}
|
}
|
||||||
nTotalIn += tx.GetJoinSplitValueIn();
|
|
||||||
|
|
||||||
if (fMissingInputs) continue;
|
if (fMissingInputs) continue;
|
||||||
|
|
||||||
// Priority is sum(valuein * age) / modified_txsize
|
// Priority is sum(valuein * age) / modified_txsize
|
||||||
|
|||||||
84
src/notarisationdb.cpp
Normal file
84
src/notarisationdb.cpp
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
#include "leveldbwrapper.h"
|
||||||
|
#include "notarisationdb.h"
|
||||||
|
#include "uint256.h"
|
||||||
|
#include "cc/eval.h"
|
||||||
|
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
NotarisationDB *pnotarisations;
|
||||||
|
|
||||||
|
|
||||||
|
NotarisationDB::NotarisationDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDBWrapper(GetDataDir() / "notarisations", nCacheSize, fMemory, fWipe, false, 64) { }
|
||||||
|
|
||||||
|
|
||||||
|
NotarisationsInBlock ScanBlockNotarisations(const CBlock &block, int nHeight)
|
||||||
|
{
|
||||||
|
EvalRef eval;
|
||||||
|
NotarisationsInBlock vNotarisations;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < block.vtx.size(); i++) {
|
||||||
|
CTransaction tx = block.vtx[i];
|
||||||
|
|
||||||
|
// Special case for TXSCL. Should prob be removed at some point.
|
||||||
|
bool isTxscl = 0;
|
||||||
|
{
|
||||||
|
NotarisationData data;
|
||||||
|
if (ParseNotarisationOpReturn(tx, data))
|
||||||
|
if (strlen(data.symbol) >= 5 && strncmp(data.symbol, "TXSCL", 5) == 0)
|
||||||
|
isTxscl = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTxscl || eval->CheckNotaryInputs(tx, nHeight, block.nTime)) {
|
||||||
|
NotarisationData data;
|
||||||
|
if (ParseNotarisationOpReturn(tx, data)) {
|
||||||
|
vNotarisations.push_back(std::make_pair(tx.GetHash(), data));
|
||||||
|
//printf("Parsed a notarisation for: %s, txid:%s, ccid:%i, momdepth:%i\n",
|
||||||
|
// data.symbol, tx.GetHash().GetHex().data(), data.ccId, data.MoMDepth);
|
||||||
|
//if (!data.MoMoM.IsNull()) printf("MoMoM:%s\n", data.MoMoM.GetHex().data());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
LogPrintf("WARNING: Couldn't parse notarisation for tx: %s at height %i\n",
|
||||||
|
tx.GetHash().GetHex().data(), nHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vNotarisations;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool GetBlockNotarisations(uint256 blockHash, NotarisationsInBlock &nibs)
|
||||||
|
{
|
||||||
|
return pnotarisations->Read(blockHash, nibs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool GetBackNotarisation(uint256 notarisationHash, Notarisation &n)
|
||||||
|
{
|
||||||
|
return pnotarisations->Read(notarisationHash, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write an index of KMD notarisation id -> backnotarisation
|
||||||
|
*/
|
||||||
|
void WriteBackNotarisations(const NotarisationsInBlock notarisations, CLevelDBBatch &batch)
|
||||||
|
{
|
||||||
|
int wrote = 0;
|
||||||
|
BOOST_FOREACH(const Notarisation &n, notarisations)
|
||||||
|
{
|
||||||
|
if (!n.second.txHash.IsNull()) {
|
||||||
|
batch.Write(n.second.txHash, n);
|
||||||
|
wrote++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void EraseBackNotarisations(const NotarisationsInBlock notarisations, CLevelDBBatch &batch)
|
||||||
|
{
|
||||||
|
BOOST_FOREACH(const Notarisation &n, notarisations)
|
||||||
|
{
|
||||||
|
if (!n.second.txHash.IsNull())
|
||||||
|
batch.Erase(n.second.txHash);
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/notarisationdb.h
Normal file
27
src/notarisationdb.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#ifndef NOTARISATIONDB_H
|
||||||
|
#define NOTARISATIONDB_H
|
||||||
|
|
||||||
|
#include "uint256.h"
|
||||||
|
#include "leveldbwrapper.h"
|
||||||
|
#include "cc/eval.h"
|
||||||
|
|
||||||
|
|
||||||
|
class NotarisationDB : public CLevelDBWrapper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NotarisationDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
extern NotarisationDB *pnotarisations;
|
||||||
|
|
||||||
|
typedef std::pair<uint256,NotarisationData> Notarisation;
|
||||||
|
typedef std::vector<Notarisation> NotarisationsInBlock;
|
||||||
|
|
||||||
|
NotarisationsInBlock ScanBlockNotarisations(const CBlock &block, int nHeight);
|
||||||
|
bool GetBlockNotarisations(uint256 blockHash, NotarisationsInBlock &nibs);
|
||||||
|
bool GetBackNotarisation(uint256 notarisationHash, Notarisation &n);
|
||||||
|
void WriteBackNotarisations(const NotarisationsInBlock notarisations, CLevelDBBatch &batch);
|
||||||
|
void EraseBackNotarisations(const NotarisationsInBlock notarisations, CLevelDBBatch &batch);
|
||||||
|
|
||||||
|
#endif /* NOTARISATIONDB_H */
|
||||||
@@ -15,7 +15,9 @@ uint256 CBlockHeader::GetHash() const
|
|||||||
return SerializeHash(*this);
|
return SerializeHash(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 CBlock::BuildMerkleTree(bool* fMutated) const
|
|
||||||
|
uint256 BuildMerkleTree(bool* fMutated, const std::vector<uint256> leaves,
|
||||||
|
std::vector<uint256> &vMerkleTree)
|
||||||
{
|
{
|
||||||
/* WARNING! If you're reading this because you're learning about crypto
|
/* WARNING! If you're reading this because you're learning about crypto
|
||||||
and/or designing a new system that will use merkle trees, keep in mind
|
and/or designing a new system that will use merkle trees, keep in mind
|
||||||
@@ -28,10 +30,10 @@ uint256 CBlock::BuildMerkleTree(bool* fMutated) const
|
|||||||
transactions leading to the same merkle root. For example, these two
|
transactions leading to the same merkle root. For example, these two
|
||||||
trees:
|
trees:
|
||||||
|
|
||||||
A A
|
A A
|
||||||
/ \ / \
|
/ \ / \
|
||||||
B C B C
|
B C B C
|
||||||
/ \ | / \ / \
|
/ \ \ / \ / \
|
||||||
D E F D E F F
|
D E F D E F F
|
||||||
/ \ / \ / \ / \ / \ / \ / \
|
/ \ / \ / \ / \ / \ / \ / \
|
||||||
1 2 3 4 5 6 1 2 3 4 5 6 5 6
|
1 2 3 4 5 6 1 2 3 4 5 6 5 6
|
||||||
@@ -52,13 +54,14 @@ uint256 CBlock::BuildMerkleTree(bool* fMutated) const
|
|||||||
known ways of changing the transactions without affecting the merkle
|
known ways of changing the transactions without affecting the merkle
|
||||||
root.
|
root.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
vMerkleTree.clear();
|
vMerkleTree.clear();
|
||||||
vMerkleTree.reserve(vtx.size() * 2 + 16); // Safe upper bound for the number of total nodes.
|
vMerkleTree.reserve(leaves.size() * 2 + 16); // Safe upper bound for the number of total nodes.
|
||||||
for (std::vector<CTransaction>::const_iterator it(vtx.begin()); it != vtx.end(); ++it)
|
for (std::vector<uint256>::const_iterator it(leaves.begin()); it != leaves.end(); ++it)
|
||||||
vMerkleTree.push_back(it->GetHash());
|
vMerkleTree.push_back(*it);
|
||||||
int j = 0;
|
int j = 0;
|
||||||
bool mutated = false;
|
bool mutated = false;
|
||||||
for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
|
for (int nSize = leaves.size(); nSize > 1; nSize = (nSize + 1) / 2)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < nSize; i += 2)
|
for (int i = 0; i < nSize; i += 2)
|
||||||
{
|
{
|
||||||
@@ -78,13 +81,20 @@ uint256 CBlock::BuildMerkleTree(bool* fMutated) const
|
|||||||
return (vMerkleTree.empty() ? uint256() : vMerkleTree.back());
|
return (vMerkleTree.empty() ? uint256() : vMerkleTree.back());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint256> CBlock::GetMerkleBranch(int nIndex) const
|
|
||||||
|
uint256 CBlock::BuildMerkleTree(bool* fMutated) const
|
||||||
|
{
|
||||||
|
std::vector<uint256> leaves;
|
||||||
|
for (int i=0; i<vtx.size(); i++) leaves.push_back(vtx[i].GetHash());
|
||||||
|
return ::BuildMerkleTree(fMutated, leaves, vMerkleTree);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<uint256> GetMerkleBranch(int nIndex, int nLeaves, const std::vector<uint256> &vMerkleTree)
|
||||||
{
|
{
|
||||||
if (vMerkleTree.empty())
|
|
||||||
BuildMerkleTree();
|
|
||||||
std::vector<uint256> vMerkleBranch;
|
std::vector<uint256> vMerkleBranch;
|
||||||
int j = 0;
|
int j = 0;
|
||||||
for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
|
for (int nSize = nLeaves; nSize > 1; nSize = (nSize + 1) / 2)
|
||||||
{
|
{
|
||||||
int i = std::min(nIndex^1, nSize-1);
|
int i = std::min(nIndex^1, nSize-1);
|
||||||
vMerkleBranch.push_back(vMerkleTree[j+i]);
|
vMerkleBranch.push_back(vMerkleTree[j+i]);
|
||||||
@@ -94,6 +104,15 @@ std::vector<uint256> CBlock::GetMerkleBranch(int nIndex) const
|
|||||||
return vMerkleBranch;
|
return vMerkleBranch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<uint256> CBlock::GetMerkleBranch(int nIndex) const
|
||||||
|
{
|
||||||
|
if (vMerkleTree.empty())
|
||||||
|
BuildMerkleTree();
|
||||||
|
return ::GetMerkleBranch(nIndex, vtx.size(), vMerkleTree);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
uint256 CBlock::CheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex)
|
uint256 CBlock::CheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex)
|
||||||
{
|
{
|
||||||
if (nIndex == -1)
|
if (nIndex == -1)
|
||||||
|
|||||||
@@ -139,6 +139,12 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
uint256 BuildMerkleTree(bool* fMutated, const std::vector<uint256> leaves,
|
||||||
|
std::vector<uint256> &vMerkleTree);
|
||||||
|
|
||||||
|
std::vector<uint256> GetMerkleBranch(int nIndex, int nLeaves, const std::vector<uint256> &vMerkleTree);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom serializer for CBlockHeader that omits the nonce and solution, for use
|
* Custom serializer for CBlockHeader that omits the nonce and solution, for use
|
||||||
* as input to Equihash.
|
* as input to Equihash.
|
||||||
|
|||||||
@@ -452,11 +452,21 @@ public:
|
|||||||
// Compute modified tx size for priority calculation (optionally given tx size)
|
// Compute modified tx size for priority calculation (optionally given tx size)
|
||||||
unsigned int CalculateModifiedSize(unsigned int nTxSize=0) const;
|
unsigned int CalculateModifiedSize(unsigned int nTxSize=0) const;
|
||||||
|
|
||||||
|
bool IsMint() const
|
||||||
|
{
|
||||||
|
return IsCoinImport() || IsCoinBase();
|
||||||
|
}
|
||||||
|
|
||||||
bool IsCoinBase() const
|
bool IsCoinBase() const
|
||||||
{
|
{
|
||||||
return (vin.size() == 1 && vin[0].prevout.IsNull());
|
return (vin.size() == 1 && vin[0].prevout.IsNull());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsCoinImport() const
|
||||||
|
{
|
||||||
|
return (vin.size() == 1 && vin[0].prevout.n == 10e8);
|
||||||
|
}
|
||||||
|
|
||||||
friend bool operator==(const CTransaction& a, const CTransaction& b)
|
friend bool operator==(const CTransaction& a, const CTransaction& b)
|
||||||
{
|
{
|
||||||
return a.hash == b.hash;
|
return a.hash == b.hash;
|
||||||
|
|||||||
@@ -7,9 +7,10 @@
|
|||||||
#include "chain.h"
|
#include "chain.h"
|
||||||
#include "chainparams.h"
|
#include "chainparams.h"
|
||||||
#include "checkpoints.h"
|
#include "checkpoints.h"
|
||||||
|
#include "crosschain.h"
|
||||||
#include "base58.h"
|
#include "base58.h"
|
||||||
#include "consensus/validation.h"
|
#include "consensus/validation.h"
|
||||||
#include "cc/betprotocol.h"
|
#include "cc/eval.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "primitives/transaction.h"
|
#include "primitives/transaction.h"
|
||||||
#include "rpcserver.h"
|
#include "rpcserver.h"
|
||||||
@@ -171,9 +172,11 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex)
|
|||||||
if (GetSpentIndex(spentKey, spentInfo)) {
|
if (GetSpentIndex(spentKey, spentInfo)) {
|
||||||
if (spentInfo.addressType == 1) {
|
if (spentInfo.addressType == 1) {
|
||||||
delta.push_back(Pair("address", CBitcoinAddress(CKeyID(spentInfo.addressHash)).ToString()));
|
delta.push_back(Pair("address", CBitcoinAddress(CKeyID(spentInfo.addressHash)).ToString()));
|
||||||
} else if (spentInfo.addressType == 2) {
|
}
|
||||||
|
else if (spentInfo.addressType == 2) {
|
||||||
delta.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString()));
|
delta.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString()));
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
delta.push_back(Pair("satoshis", -1 * spentInfo.satoshis));
|
delta.push_back(Pair("satoshis", -1 * spentInfo.satoshis));
|
||||||
@@ -202,10 +205,21 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex)
|
|||||||
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22);
|
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22);
|
||||||
delta.push_back(Pair("address", CBitcoinAddress(CScriptID(uint160(hashBytes))).ToString()));
|
delta.push_back(Pair("address", CBitcoinAddress(CScriptID(uint160(hashBytes))).ToString()));
|
||||||
|
|
||||||
} else if (out.scriptPubKey.IsPayToPublicKeyHash()) {
|
}
|
||||||
|
else if (out.scriptPubKey.IsPayToPublicKeyHash()) {
|
||||||
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23);
|
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23);
|
||||||
delta.push_back(Pair("address", CBitcoinAddress(CKeyID(uint160(hashBytes))).ToString()));
|
delta.push_back(Pair("address", CBitcoinAddress(CKeyID(uint160(hashBytes))).ToString()));
|
||||||
} else {
|
}
|
||||||
|
else if (out.scriptPubKey.IsPayToPublicKey()) {
|
||||||
|
CTxDestination address;
|
||||||
|
if (ExtractDestination(out.scriptPubKey, address))
|
||||||
|
{
|
||||||
|
//vector<unsigned char> hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34);
|
||||||
|
//xxx delta.push_back(Pair("address", CBitcoinAddress(CKeyID(uint160(hashBytes))).ToString()));
|
||||||
|
delta.push_back(Pair("address", CBitcoinAddress(address).ToString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -758,16 +772,33 @@ int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestam
|
|||||||
char *bitcoin_address(char *coinaddr,uint8_t addrtype,uint8_t *pubkey_or_rmd160,int32_t len);
|
char *bitcoin_address(char *coinaddr,uint8_t addrtype,uint8_t *pubkey_or_rmd160,int32_t len);
|
||||||
int32_t komodo_minerids(uint8_t *minerids,int32_t height,int32_t width);
|
int32_t komodo_minerids(uint8_t *minerids,int32_t height,int32_t width);
|
||||||
int32_t komodo_kvsearch(uint256 *refpubkeyp,int32_t current_height,uint32_t *flagsp,int32_t *heightp,uint8_t value[IGUANA_MAXSCRIPTSIZE],uint8_t *key,int32_t keylen);
|
int32_t komodo_kvsearch(uint256 *refpubkeyp,int32_t current_height,uint32_t *flagsp,int32_t *heightp,uint8_t value[IGUANA_MAXSCRIPTSIZE],uint8_t *key,int32_t keylen);
|
||||||
int32_t komodo_MoM(int32_t *notarized_htp,uint256 *MoMp,uint256 *kmdtxidp,int32_t nHeight,uint256 *MoMoMp,int32_t *MoMoMoffsetp,int32_t *MoMoMdepthp,int32_t *kmdstartip,int32_t *kmdendip);
|
|
||||||
int32_t komodo_MoMoMdata(char *hexstr,int32_t hexsize,struct komodo_ccdataMoMoM *mdata,char *symbol,int32_t kmdheight,int32_t notarized_height);
|
|
||||||
struct komodo_ccdata_entry *komodo_allMoMs(int32_t *nump,uint256 *MoMoMp,int32_t kmdstarti,int32_t kmdendi);
|
|
||||||
uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth);
|
|
||||||
|
|
||||||
UniValue kvsearch(const UniValue& params, bool fHelp)
|
UniValue kvsearch(const UniValue& params, bool fHelp)
|
||||||
{
|
{
|
||||||
UniValue ret(UniValue::VOBJ); uint32_t flags; uint8_t value[IGUANA_MAXSCRIPTSIZE],key[IGUANA_MAXSCRIPTSIZE]; int32_t duration,j,height,valuesize,keylen; uint256 refpubkey; static uint256 zeroes;
|
UniValue ret(UniValue::VOBJ); uint32_t flags; uint8_t value[IGUANA_MAXSCRIPTSIZE],key[IGUANA_MAXSCRIPTSIZE]; int32_t duration,j,height,valuesize,keylen; uint256 refpubkey; static uint256 zeroes;
|
||||||
if (fHelp || params.size() != 1 )
|
if (fHelp || params.size() != 1 )
|
||||||
throw runtime_error("kvsearch key");
|
throw runtime_error(
|
||||||
|
"kvsearch key\n"
|
||||||
|
"\nSearch for a key stored via the kvupdate command. This feature is only available for asset chains.\n"
|
||||||
|
"\nArguments:\n"
|
||||||
|
"1. key (string, required) search the chain for this key\n"
|
||||||
|
"\nResult:\n"
|
||||||
|
"{\n"
|
||||||
|
" \"coin\": \"xxxxx\", (string) chain the key is stored on\n"
|
||||||
|
" \"currentheight\": xxxxx, (numeric) current height of the chain\n"
|
||||||
|
" \"key\": \"xxxxx\", (string) key\n"
|
||||||
|
" \"keylen\": xxxxx, (string) length of the key \n"
|
||||||
|
" \"owner\": \"xxxxx\" (string) hex string representing the owner of the key \n"
|
||||||
|
" \"height\": xxxxx, (numeric) height the key was stored at\n"
|
||||||
|
" \"expiration\": xxxxx, (numeric) height the key will expire\n"
|
||||||
|
" \"flags\": x (numeric) 1 if the key was created with a password; 0 otherwise.\n"
|
||||||
|
" \"value\": \"xxxxx\", (string) stored value\n"
|
||||||
|
" \"valuesize\": xxxxx (string) amount of characters stored\n"
|
||||||
|
"}\n"
|
||||||
|
"\nExamples:\n"
|
||||||
|
+ HelpExampleCli("kvsearch", "examplekey")
|
||||||
|
+ HelpExampleRpc("kvsearch", "examplekey")
|
||||||
|
);
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
if ( (keylen= (int32_t)strlen(params[0].get_str().c_str())) > 0 )
|
if ( (keylen= (int32_t)strlen(params[0].get_str().c_str())) > 0 )
|
||||||
{
|
{
|
||||||
@@ -798,221 +829,6 @@ UniValue kvsearch(const UniValue& params, bool fHelp)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
UniValue allMoMs(const UniValue& params, bool fHelp)
|
|
||||||
{
|
|
||||||
struct komodo_ccdata_entry *allMoMs; uint256 MoMoM; int32_t num,i,kmdstarti,kmdendi; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR);
|
|
||||||
if ( fHelp || params.size() != 2 )
|
|
||||||
throw runtime_error("allMoMs kmdstarti kmdendi\n");
|
|
||||||
LOCK(cs_main);
|
|
||||||
kmdstarti = atoi(params[0].get_str().c_str());
|
|
||||||
kmdendi = atoi(params[1].get_str().c_str());
|
|
||||||
ret.push_back(Pair("kmdstarti",kmdstarti));
|
|
||||||
ret.push_back(Pair("kmdendi",kmdendi));
|
|
||||||
if ( (allMoMs= komodo_allMoMs(&num,&MoMoM,kmdstarti,kmdendi)) != 0 )
|
|
||||||
{
|
|
||||||
for (i=0; i<num; i++)
|
|
||||||
{
|
|
||||||
UniValue item(UniValue::VOBJ);
|
|
||||||
item.push_back(Pair("MoM",allMoMs[i].MoM.ToString()));
|
|
||||||
item.push_back(Pair("coin",allMoMs[i].symbol));
|
|
||||||
item.push_back(Pair("notarized_height",allMoMs[i].notarized_height));
|
|
||||||
item.push_back(Pair("kmdheight",allMoMs[i].kmdheight));
|
|
||||||
item.push_back(Pair("txi",allMoMs[i].txi));
|
|
||||||
a.push_back(item);
|
|
||||||
}
|
|
||||||
ret.push_back(Pair("MoMs",a));
|
|
||||||
ret.push_back(Pair("MoMoM",MoMoM.ToString()));
|
|
||||||
ret.push_back(Pair("MoMoMdepth",(int)num));
|
|
||||||
free(allMoMs);
|
|
||||||
}
|
|
||||||
return(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
UniValue MoMoMdata(const UniValue& params, bool fHelp)
|
|
||||||
{
|
|
||||||
char *symbol,hexstr[16384+1]; struct komodo_ccdataMoMoM mdata; int32_t i,kmdheight,notarized_height; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR);
|
|
||||||
if ( fHelp || params.size() != 3 )
|
|
||||||
throw runtime_error("MoMoMdata symbol kmdheight notarized_height\n");
|
|
||||||
LOCK(cs_main);
|
|
||||||
symbol = (char *)params[0].get_str().c_str();
|
|
||||||
kmdheight = atoi(params[1].get_str().c_str());
|
|
||||||
notarized_height = atoi(params[2].get_str().c_str());
|
|
||||||
ret.push_back(Pair("coin",symbol));
|
|
||||||
ret.push_back(Pair("kmdheight",kmdheight));
|
|
||||||
ret.push_back(Pair("notarized_height",notarized_height));
|
|
||||||
memset(&mdata,0,sizeof(mdata));
|
|
||||||
if ( komodo_MoMoMdata(hexstr,sizeof(hexstr),&mdata,symbol,kmdheight,notarized_height) == 0 )
|
|
||||||
{
|
|
||||||
ret.push_back(Pair("kmdstarti",mdata.kmdstarti));
|
|
||||||
ret.push_back(Pair("kmdendi",mdata.kmdendi));
|
|
||||||
ret.push_back(Pair("MoMoM",mdata.MoMoM.ToString()));
|
|
||||||
ret.push_back(Pair("MoMoMdepth",mdata.MoMoMdepth));
|
|
||||||
ret.push_back(Pair("numnotarizations",mdata.numpairs));
|
|
||||||
if ( mdata.pairs != 0 )
|
|
||||||
{
|
|
||||||
//fprintf(stderr,"mdata.pairs free %p, numpairs.%d\n",mdata.pairs,mdata.numpairs);
|
|
||||||
for (i=0; i<mdata.numpairs; i++)
|
|
||||||
{
|
|
||||||
UniValue item(UniValue::VOBJ);
|
|
||||||
item.push_back(Pair("height",(int)mdata.pairs[i].notarized_height));
|
|
||||||
item.push_back(Pair("MoMoMoffset",(int)mdata.pairs[i].MoMoMoffset));
|
|
||||||
a.push_back(item);
|
|
||||||
}
|
|
||||||
free(mdata.pairs);
|
|
||||||
}
|
|
||||||
ret.push_back(Pair("notarizations",a));
|
|
||||||
ret.push_back(Pair("data",hexstr));
|
|
||||||
} else ret.push_back(Pair("error","cant calculate MoMoM"));
|
|
||||||
return(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
UniValue calc_MoM(const UniValue& params, bool fHelp)
|
|
||||||
{
|
|
||||||
int32_t height,MoMdepth; uint256 MoM; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR);
|
|
||||||
if ( fHelp || params.size() != 2 )
|
|
||||||
throw runtime_error("calc_MoM height MoMdepth\n");
|
|
||||||
LOCK(cs_main);
|
|
||||||
height = atoi(params[0].get_str().c_str());
|
|
||||||
MoMdepth = atoi(params[1].get_str().c_str());
|
|
||||||
if ( height <= 0 || MoMdepth <= 0 || MoMdepth >= height )
|
|
||||||
throw runtime_error("calc_MoM illegal height or MoMdepth\n");
|
|
||||||
//fprintf(stderr,"height_MoM height.%d\n",height);
|
|
||||||
MoM = komodo_calcMoM(height,MoMdepth);
|
|
||||||
ret.push_back(Pair("coin",(char *)(ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL)));
|
|
||||||
ret.push_back(Pair("height",height));
|
|
||||||
ret.push_back(Pair("MoMdepth",MoMdepth));
|
|
||||||
ret.push_back(Pair("MoM",MoM.GetHex()));
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
UniValue height_MoM(const UniValue& params, bool fHelp)
|
|
||||||
{
|
|
||||||
int32_t height,depth,notarized_height,MoMoMdepth,MoMoMoffset,kmdstarti,kmdendi; uint256 MoM,MoMoM,kmdtxid; uint32_t timestamp = 0; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR);
|
|
||||||
if ( fHelp || params.size() != 1 )
|
|
||||||
throw runtime_error("height_MoM height\n");
|
|
||||||
LOCK(cs_main);
|
|
||||||
height = atoi(params[0].get_str().c_str());
|
|
||||||
if ( height <= 0 )
|
|
||||||
{
|
|
||||||
if ( chainActive.Tip() == 0 )
|
|
||||||
{
|
|
||||||
ret.push_back(Pair("error",(char *)"no active chain yet"));
|
|
||||||
return(ret);
|
|
||||||
}
|
|
||||||
height = chainActive.Tip()->nHeight;
|
|
||||||
}
|
|
||||||
//fprintf(stderr,"height_MoM height.%d\n",height);
|
|
||||||
depth = komodo_MoM(¬arized_height,&MoM,&kmdtxid,height,&MoMoM,&MoMoMoffset,&MoMoMdepth,&kmdstarti,&kmdendi);
|
|
||||||
ret.push_back(Pair("coin",(char *)(ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL)));
|
|
||||||
ret.push_back(Pair("height",height));
|
|
||||||
ret.push_back(Pair("timestamp",(uint64_t)timestamp));
|
|
||||||
if ( depth > 0 )
|
|
||||||
{
|
|
||||||
ret.push_back(Pair("depth",depth));
|
|
||||||
ret.push_back(Pair("notarized_height",notarized_height));
|
|
||||||
ret.push_back(Pair("MoM",MoM.GetHex()));
|
|
||||||
ret.push_back(Pair("kmdtxid",kmdtxid.GetHex()));
|
|
||||||
if ( ASSETCHAINS_SYMBOL[0] != 0 )
|
|
||||||
{
|
|
||||||
ret.push_back(Pair("MoMoM",MoMoM.GetHex()));
|
|
||||||
ret.push_back(Pair("MoMoMoffset",MoMoMoffset));
|
|
||||||
ret.push_back(Pair("MoMoMdepth",MoMoMdepth));
|
|
||||||
ret.push_back(Pair("kmdstarti",kmdstarti));
|
|
||||||
ret.push_back(Pair("kmdendi",kmdendi));
|
|
||||||
}
|
|
||||||
} else ret.push_back(Pair("error",(char *)"no MoM for height"));
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
UniValue txMoMproof(const UniValue& params, bool fHelp)
|
|
||||||
{
|
|
||||||
uint256 hash, notarisationHash, MoM,MoMoM; int32_t notarisedHeight, depth; CBlockIndex* blockIndex;
|
|
||||||
std::vector<uint256> branch;
|
|
||||||
int nIndex,MoMoMdepth,MoMoMoffset,kmdstarti,kmdendi;
|
|
||||||
|
|
||||||
// parse params and get notarisation data for tx
|
|
||||||
{
|
|
||||||
if ( fHelp || params.size() != 1)
|
|
||||||
throw runtime_error("txMoMproof needs a txid");
|
|
||||||
|
|
||||||
hash = uint256S(params[0].get_str());
|
|
||||||
|
|
||||||
uint256 blockHash;
|
|
||||||
CTransaction tx;
|
|
||||||
if (!GetTransaction(hash, tx, blockHash, true))
|
|
||||||
throw runtime_error("cannot find transaction");
|
|
||||||
|
|
||||||
blockIndex = mapBlockIndex[blockHash];
|
|
||||||
|
|
||||||
depth = komodo_MoM(¬arisedHeight, &MoM, ¬arisationHash, blockIndex->nHeight,&MoMoM,&MoMoMoffset,&MoMoMdepth,&kmdstarti,&kmdendi);
|
|
||||||
|
|
||||||
if (!depth)
|
|
||||||
throw runtime_error("notarisation not found");
|
|
||||||
|
|
||||||
// index of block in MoM leaves
|
|
||||||
nIndex = notarisedHeight - blockIndex->nHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
// build merkle chain from blocks to MoM
|
|
||||||
{
|
|
||||||
// since the merkle branch code is tied up in a block class
|
|
||||||
// and we want to make a merkle branch for something that isnt transactions
|
|
||||||
CBlock fakeBlock;
|
|
||||||
for (int i=0; i<depth; i++) {
|
|
||||||
uint256 mRoot = chainActive[notarisedHeight - i]->hashMerkleRoot;
|
|
||||||
CTransaction fakeTx;
|
|
||||||
// first value in CTransaction memory is it's hash
|
|
||||||
memcpy((void*)&fakeTx, mRoot.begin(), 32);
|
|
||||||
fakeBlock.vtx.push_back(fakeTx);
|
|
||||||
}
|
|
||||||
branch = fakeBlock.GetMerkleBranch(nIndex);
|
|
||||||
|
|
||||||
// Check branch
|
|
||||||
if (MoM != CBlock::CheckMerkleBranch(blockIndex->hashMerkleRoot, branch, nIndex))
|
|
||||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Failed merkle block->MoM");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now get the tx merkle branch
|
|
||||||
{
|
|
||||||
CBlock block;
|
|
||||||
|
|
||||||
if (fHavePruned && !(blockIndex->nStatus & BLOCK_HAVE_DATA) && blockIndex->nTx > 0)
|
|
||||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)");
|
|
||||||
|
|
||||||
if(!ReadBlockFromDisk(block, blockIndex,1))
|
|
||||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
|
|
||||||
|
|
||||||
// Locate the transaction in the block
|
|
||||||
int nTxIndex;
|
|
||||||
for (nTxIndex = 0; nTxIndex < (int)block.vtx.size(); nTxIndex++)
|
|
||||||
if (block.vtx[nTxIndex].GetHash() == hash)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (nTxIndex == (int)block.vtx.size())
|
|
||||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Error locating tx in block");
|
|
||||||
|
|
||||||
std::vector<uint256> txBranch = block.GetMerkleBranch(nTxIndex);
|
|
||||||
|
|
||||||
// Check branch
|
|
||||||
if (block.hashMerkleRoot != CBlock::CheckMerkleBranch(hash, txBranch, nTxIndex))
|
|
||||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Failed merkle tx->block");
|
|
||||||
|
|
||||||
// concatenate branches
|
|
||||||
nIndex = (nIndex << txBranch.size()) + nTxIndex;
|
|
||||||
branch.insert(branch.begin(), txBranch.begin(), txBranch.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the proof
|
|
||||||
if (MoM != CBlock::CheckMerkleBranch(hash, branch, nIndex))
|
|
||||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Failed validating MoM");
|
|
||||||
|
|
||||||
// Encode and return
|
|
||||||
CDataStream ssProof(SER_NETWORK, PROTOCOL_VERSION);
|
|
||||||
ssProof << MoMProof(nIndex, branch, notarisationHash);
|
|
||||||
return HexStr(ssProof.begin(), ssProof.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
UniValue minerids(const UniValue& params, bool fHelp)
|
UniValue minerids(const UniValue& params, bool fHelp)
|
||||||
{
|
{
|
||||||
uint32_t timestamp = 0; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR); uint8_t minerids[2000],pubkeys[65][33]; int32_t i,j,n,numnotaries,tally[129];
|
uint32_t timestamp = 0; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR); uint8_t minerids[2000],pubkeys[65][33]; int32_t i,j,n,numnotaries,tally[129];
|
||||||
|
|||||||
@@ -134,17 +134,20 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||||||
{ "paxprices", 3 },
|
{ "paxprices", 3 },
|
||||||
{ "paxpending", 0 },
|
{ "paxpending", 0 },
|
||||||
{ "notaries", 2 },
|
{ "notaries", 2 },
|
||||||
{ "height_MoM", 1 },
|
|
||||||
{ "MoMoMdata", 3 },
|
|
||||||
{ "allMoMs", 2 },
|
|
||||||
{ "txMoMproof", 1 },
|
|
||||||
{ "minerids", 1 },
|
{ "minerids", 1 },
|
||||||
{ "kvsearch", 1 },
|
{ "kvsearch", 1 },
|
||||||
{ "kvupdate", 4 },
|
{ "kvupdate", 4 },
|
||||||
{ "z_importkey", 2 },
|
{ "z_importkey", 2 },
|
||||||
{ "z_importviewingkey", 2 },
|
{ "z_importviewingkey", 2 },
|
||||||
{ "z_getpaymentdisclosure", 1},
|
{ "z_getpaymentdisclosure", 1},
|
||||||
{ "z_getpaymentdisclosure", 2}
|
{ "z_getpaymentdisclosure", 2},
|
||||||
|
|
||||||
|
// crosschain
|
||||||
|
{ "assetchainproof", 1},
|
||||||
|
{ "crosschainproof", 1},
|
||||||
|
{ "getproofroot", 2},
|
||||||
|
{ "height_MoM", 1},
|
||||||
|
{ "calc_MoM", 2},
|
||||||
};
|
};
|
||||||
|
|
||||||
class CRPCConvertTable
|
class CRPCConvertTable
|
||||||
|
|||||||
253
src/rpccrosschain.cpp
Normal file
253
src/rpccrosschain.cpp
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
#include "amount.h"
|
||||||
|
#include "chain.h"
|
||||||
|
#include "chainparams.h"
|
||||||
|
#include "checkpoints.h"
|
||||||
|
#include "crosschain.h"
|
||||||
|
#include "importcoin.h"
|
||||||
|
#include "base58.h"
|
||||||
|
#include "consensus/validation.h"
|
||||||
|
#include "cc/eval.h"
|
||||||
|
#include "cc/utils.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "primitives/transaction.h"
|
||||||
|
#include "rpcserver.h"
|
||||||
|
#include "sync.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "script/script.h"
|
||||||
|
#include "script/script_error.h"
|
||||||
|
#include "script/sign.h"
|
||||||
|
#include "script/standard.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <univalue.h>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int32_t komodo_MoM(int32_t *notarized_htp,uint256 *MoMp,uint256 *kmdtxidp,int32_t nHeight,uint256 *MoMoMp,int32_t *MoMoMoffsetp,int32_t *MoMoMdepthp,int32_t *kmdstartip,int32_t *kmdendip);
|
||||||
|
int32_t komodo_MoMoMdata(char *hexstr,int32_t hexsize,struct komodo_ccdataMoMoM *mdata,char *symbol,int32_t kmdheight,int32_t notarized_height);
|
||||||
|
struct komodo_ccdata_entry *komodo_allMoMs(int32_t *nump,uint256 *MoMoMp,int32_t kmdstarti,int32_t kmdendi);
|
||||||
|
uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth);
|
||||||
|
|
||||||
|
|
||||||
|
UniValue assetchainproof(const UniValue& params, bool fHelp)
|
||||||
|
{
|
||||||
|
uint256 hash;
|
||||||
|
|
||||||
|
// parse params and get notarisation data for tx
|
||||||
|
if ( fHelp || params.size() != 1)
|
||||||
|
throw runtime_error("assetchainproof needs a txid");
|
||||||
|
|
||||||
|
hash = uint256S(params[0].get_str());
|
||||||
|
|
||||||
|
auto proof = GetAssetchainProof(hash);
|
||||||
|
auto proofData = E_MARSHAL(ss << proof);
|
||||||
|
return HexStr(proofData);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
UniValue crosschainproof(const UniValue& params, bool fHelp)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
UniValue height_MoM(const UniValue& params, bool fHelp)
|
||||||
|
{
|
||||||
|
int32_t height,depth,notarized_height,MoMoMdepth,MoMoMoffset,kmdstarti,kmdendi; uint256 MoM,MoMoM,kmdtxid; uint32_t timestamp = 0; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR);
|
||||||
|
if ( fHelp || params.size() != 1 )
|
||||||
|
throw runtime_error("height_MoM height\n");
|
||||||
|
LOCK(cs_main);
|
||||||
|
height = atoi(params[0].get_str().c_str());
|
||||||
|
if ( height <= 0 )
|
||||||
|
{
|
||||||
|
if ( chainActive.Tip() == 0 )
|
||||||
|
{
|
||||||
|
ret.push_back(Pair("error",(char *)"no active chain yet"));
|
||||||
|
return(ret);
|
||||||
|
}
|
||||||
|
height = chainActive.Tip()->nHeight;
|
||||||
|
}
|
||||||
|
//fprintf(stderr,"height_MoM height.%d\n",height);
|
||||||
|
depth = komodo_MoM(¬arized_height,&MoM,&kmdtxid,height,&MoMoM,&MoMoMoffset,&MoMoMdepth,&kmdstarti,&kmdendi);
|
||||||
|
ret.push_back(Pair("coin",(char *)(ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL)));
|
||||||
|
ret.push_back(Pair("height",height));
|
||||||
|
ret.push_back(Pair("timestamp",(uint64_t)timestamp));
|
||||||
|
if ( depth > 0 )
|
||||||
|
{
|
||||||
|
ret.push_back(Pair("depth",depth));
|
||||||
|
ret.push_back(Pair("notarized_height",notarized_height));
|
||||||
|
ret.push_back(Pair("MoM",MoM.GetHex()));
|
||||||
|
ret.push_back(Pair("kmdtxid",kmdtxid.GetHex()));
|
||||||
|
if ( ASSETCHAINS_SYMBOL[0] != 0 )
|
||||||
|
{
|
||||||
|
ret.push_back(Pair("MoMoM",MoMoM.GetHex()));
|
||||||
|
ret.push_back(Pair("MoMoMoffset",MoMoMoffset));
|
||||||
|
ret.push_back(Pair("MoMoMdepth",MoMoMdepth));
|
||||||
|
ret.push_back(Pair("kmdstarti",kmdstarti));
|
||||||
|
ret.push_back(Pair("kmdendi",kmdendi));
|
||||||
|
}
|
||||||
|
} else ret.push_back(Pair("error",(char *)"no MoM for height"));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue MoMoMdata(const UniValue& params, bool fHelp)
|
||||||
|
{
|
||||||
|
if ( fHelp || params.size() != 3 )
|
||||||
|
throw runtime_error("MoMoMdata symbol kmdheight ccid\n");
|
||||||
|
UniValue ret(UniValue::VOBJ);
|
||||||
|
char* symbol = (char *)params[0].get_str().c_str();
|
||||||
|
int kmdheight = atoi(params[1].get_str().c_str());
|
||||||
|
uint32_t ccid = atoi(params[2].get_str().c_str());
|
||||||
|
ret.push_back(Pair("coin",symbol));
|
||||||
|
ret.push_back(Pair("kmdheight",kmdheight));
|
||||||
|
ret.push_back(Pair("ccid", (int) ccid));
|
||||||
|
|
||||||
|
uint256 destNotarisationTxid;
|
||||||
|
std::vector<uint256> moms;
|
||||||
|
uint256 MoMoM = CalculateProofRoot(symbol, ccid, kmdheight, moms, destNotarisationTxid);
|
||||||
|
|
||||||
|
UniValue valMoms(UniValue::VARR);
|
||||||
|
for (int i=0; i<moms.size(); i++) valMoms.push_back(moms[i].GetHex());
|
||||||
|
ret.push_back(Pair("MoMs", valMoms));
|
||||||
|
ret.push_back(Pair("notarization_hash", destNotarisationTxid.GetHex()));
|
||||||
|
ret.push_back(Pair("MoMoM", MoMoM.GetHex()));
|
||||||
|
auto vmomomdata = E_MARSHAL(ss << MoMoM; ss << ((uint32_t)0));
|
||||||
|
ret.push_back(Pair("data", HexStr(vmomomdata)));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
UniValue calc_MoM(const UniValue& params, bool fHelp)
|
||||||
|
{
|
||||||
|
int32_t height,MoMdepth; uint256 MoM; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR);
|
||||||
|
if ( fHelp || params.size() != 2 )
|
||||||
|
throw runtime_error("calc_MoM height MoMdepth\n");
|
||||||
|
LOCK(cs_main);
|
||||||
|
height = atoi(params[0].get_str().c_str());
|
||||||
|
MoMdepth = atoi(params[1].get_str().c_str());
|
||||||
|
if ( height <= 0 || MoMdepth <= 0 || MoMdepth >= height )
|
||||||
|
throw runtime_error("calc_MoM illegal height or MoMdepth\n");
|
||||||
|
//fprintf(stderr,"height_MoM height.%d\n",height);
|
||||||
|
MoM = komodo_calcMoM(height,MoMdepth);
|
||||||
|
ret.push_back(Pair("coin",(char *)(ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL)));
|
||||||
|
ret.push_back(Pair("height",height));
|
||||||
|
ret.push_back(Pair("MoMdepth",MoMdepth));
|
||||||
|
ret.push_back(Pair("MoM",MoM.GetHex()));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
UniValue migrate_converttoexport(const UniValue& params, bool fHelp)
|
||||||
|
{
|
||||||
|
if (fHelp || params.size() != 3)
|
||||||
|
throw runtime_error(
|
||||||
|
"migrate_converttoexport rawTx dest_symbol export_amount\n"
|
||||||
|
"\nConvert a raw transaction to a cross-chain export.\n"
|
||||||
|
"If neccesary, the transaction should be funded using fundrawtransaction.\n"
|
||||||
|
"Finally, the transaction should be signed using signrawtransaction\n"
|
||||||
|
"The finished export transaction, plus the payouts, should be passed to "
|
||||||
|
"the \"migrate_createimporttransaction\" method on a KMD node to get the corresponding "
|
||||||
|
"import transaction.\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ASSETCHAINS_CC < 2)
|
||||||
|
throw runtime_error("-ac_cc < 2");
|
||||||
|
|
||||||
|
if (ASSETCHAINS_SYMBOL[0] == 0)
|
||||||
|
throw runtime_error("Must be called on assetchain");
|
||||||
|
|
||||||
|
vector<uint8_t> txData(ParseHexV(params[0], "argument 1"));
|
||||||
|
CMutableTransaction tx;
|
||||||
|
if (!E_UNMARSHAL(txData, ss >> tx))
|
||||||
|
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
||||||
|
|
||||||
|
string targetSymbol = params[1].get_str();
|
||||||
|
if (targetSymbol.size() == 0 || targetSymbol.size() > 32)
|
||||||
|
throw runtime_error("targetSymbol length must be >0 and <=32");
|
||||||
|
|
||||||
|
CAmount burnAmount = AmountFromValue(params[2]);
|
||||||
|
if (burnAmount <= 0)
|
||||||
|
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for export");
|
||||||
|
{
|
||||||
|
CAmount needed = 0;
|
||||||
|
for (int i=0; i<tx.vout.size(); i++) needed += tx.vout[i].nValue;
|
||||||
|
if (burnAmount < needed)
|
||||||
|
throw runtime_error("export_amount too small");
|
||||||
|
}
|
||||||
|
|
||||||
|
CTxOut burnOut = MakeBurnOutput(burnAmount, ASSETCHAINS_CC, targetSymbol, tx.vout);
|
||||||
|
UniValue ret(UniValue::VOBJ);
|
||||||
|
ret.push_back(Pair("payouts", HexStr(E_MARSHAL(ss << tx.vout))));
|
||||||
|
tx.vout.clear();
|
||||||
|
tx.vout.push_back(burnOut);
|
||||||
|
ret.push_back(Pair("exportTx", HexStr(E_MARSHAL(ss << tx))));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The process to migrate funds
|
||||||
|
*
|
||||||
|
* Create a transaction on assetchain:
|
||||||
|
*
|
||||||
|
* generaterawtransaction
|
||||||
|
* migrate_converttoexport
|
||||||
|
* fundrawtransaction
|
||||||
|
* signrawtransaction
|
||||||
|
*
|
||||||
|
* migrate_createimportransaction
|
||||||
|
* migrate_completeimporttransaction
|
||||||
|
*/
|
||||||
|
|
||||||
|
UniValue migrate_createimporttransaction(const UniValue& params, bool fHelp)
|
||||||
|
{
|
||||||
|
if (fHelp || params.size() != 2)
|
||||||
|
throw runtime_error("migrate_createimporttransaction burnTx payouts\n\n"
|
||||||
|
"Create an importTx given a burnTx and the corresponding payouts, hex encoded");
|
||||||
|
|
||||||
|
if (ASSETCHAINS_CC < 2)
|
||||||
|
throw runtime_error("-ac_cc < 2");
|
||||||
|
|
||||||
|
if (ASSETCHAINS_SYMBOL[0] == 0)
|
||||||
|
throw runtime_error("Must be called on assetchain");
|
||||||
|
|
||||||
|
vector<uint8_t> txData(ParseHexV(params[0], "argument 1"));
|
||||||
|
|
||||||
|
CTransaction burnTx;
|
||||||
|
if (!E_UNMARSHAL(txData, ss >> burnTx))
|
||||||
|
throw runtime_error("Couldn't parse burnTx");
|
||||||
|
|
||||||
|
|
||||||
|
vector<CTxOut> payouts;
|
||||||
|
if (!E_UNMARSHAL(ParseHexV(params[1], "argument 2"), ss >> payouts))
|
||||||
|
throw runtime_error("Couldn't parse payouts");
|
||||||
|
|
||||||
|
uint256 txid = burnTx.GetHash();
|
||||||
|
TxProof proof = GetAssetchainProof(burnTx.GetHash());
|
||||||
|
|
||||||
|
CTransaction importTx = MakeImportCoinTransaction(proof, burnTx, payouts);
|
||||||
|
return HexStr(E_MARSHAL(ss << importTx));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
UniValue migrate_completeimporttransaction(const UniValue& params, bool fHelp)
|
||||||
|
{
|
||||||
|
if (fHelp || params.size() != 1)
|
||||||
|
throw runtime_error("migrate_completeimporttransaction importTx\n\n"
|
||||||
|
"Takes a cross chain import tx with proof generated on assetchain "
|
||||||
|
"and extends proof to target chain proof root");
|
||||||
|
|
||||||
|
if (ASSETCHAINS_SYMBOL[0] != 0)
|
||||||
|
throw runtime_error("Must be called on KMD");
|
||||||
|
|
||||||
|
CTransaction importTx;
|
||||||
|
if (!E_UNMARSHAL(ParseHexV(params[0], "argument 1"), ss >> importTx))
|
||||||
|
throw runtime_error("Couldn't parse importTx");
|
||||||
|
|
||||||
|
CompleteImportTransaction(importTx);
|
||||||
|
|
||||||
|
return HexStr(E_MARSHAL(ss << importTx));
|
||||||
|
}
|
||||||
@@ -603,7 +603,8 @@ bool getAddressFromIndex(const int &type, const uint160 &hash, std::string &addr
|
|||||||
address = CBitcoinAddress(CScriptID(hash)).ToString();
|
address = CBitcoinAddress(CScriptID(hash)).ToString();
|
||||||
} else if (type == 1) {
|
} else if (type == 1) {
|
||||||
address = CBitcoinAddress(CKeyID(hash)).ToString();
|
address = CBitcoinAddress(CKeyID(hash)).ToString();
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -134,6 +134,9 @@ void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, UniValue&
|
|||||||
UniValue in(UniValue::VOBJ);
|
UniValue in(UniValue::VOBJ);
|
||||||
if (tx.IsCoinBase())
|
if (tx.IsCoinBase())
|
||||||
in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
|
in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
|
||||||
|
else if (tx.IsCoinImport()) {
|
||||||
|
in.push_back(Pair("is_import", "1"));
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
in.push_back(Pair("txid", txin.prevout.hash.GetHex()));
|
in.push_back(Pair("txid", txin.prevout.hash.GetHex()));
|
||||||
in.push_back(Pair("vout", (int64_t)txin.prevout.n));
|
in.push_back(Pair("vout", (int64_t)txin.prevout.n));
|
||||||
@@ -158,7 +161,8 @@ void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, UniValue&
|
|||||||
in.push_back(Pair("valueSat", spentInfo.satoshis));
|
in.push_back(Pair("valueSat", spentInfo.satoshis));
|
||||||
if (spentInfo.addressType == 1) {
|
if (spentInfo.addressType == 1) {
|
||||||
in.push_back(Pair("address", CBitcoinAddress(CKeyID(spentInfo.addressHash)).ToString()));
|
in.push_back(Pair("address", CBitcoinAddress(CKeyID(spentInfo.addressHash)).ToString()));
|
||||||
} else if (spentInfo.addressType == 2) {
|
}
|
||||||
|
else if (spentInfo.addressType == 2) {
|
||||||
in.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString()));
|
in.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -302,15 +302,20 @@ static const CRPCCommand vRPCCommands[] =
|
|||||||
{ "blockchain", "paxpending", &paxpending, true },
|
{ "blockchain", "paxpending", &paxpending, true },
|
||||||
{ "blockchain", "paxprices", &paxprices, true },
|
{ "blockchain", "paxprices", &paxprices, true },
|
||||||
{ "blockchain", "notaries", ¬aries, true },
|
{ "blockchain", "notaries", ¬aries, true },
|
||||||
{ "blockchain", "allMoMs", &allMoMs, true },
|
|
||||||
{ "blockchain", "MoMoMdata", &MoMoMdata, true },
|
|
||||||
{ "blockchain", "calc_MoM", &calc_MoM, true },
|
|
||||||
{ "blockchain", "height_MoM", &height_MoM, true },
|
|
||||||
{ "blockchain", "txMoMproof", &txMoMproof, true },
|
|
||||||
{ "blockchain", "minerids", &minerids, true },
|
{ "blockchain", "minerids", &minerids, true },
|
||||||
{ "blockchain", "kvsearch", &kvsearch, true },
|
{ "blockchain", "kvsearch", &kvsearch, true },
|
||||||
{ "blockchain", "kvupdate", &kvupdate, true },
|
{ "blockchain", "kvupdate", &kvupdate, true },
|
||||||
|
|
||||||
|
/* Cross chain utilities */
|
||||||
|
{ "crosschain", "MoMoMdata", &MoMoMdata, true },
|
||||||
|
{ "crosschain", "calc_MoM", &calc_MoM, true },
|
||||||
|
{ "crosschain", "height_MoM", &height_MoM, true },
|
||||||
|
{ "crosschain", "assetchainproof", &assetchainproof, true },
|
||||||
|
{ "crosschain", "crosschainproof", &crosschainproof, true },
|
||||||
|
{ "crosschain", "migrate_converttoexport", &migrate_converttoexport, true },
|
||||||
|
{ "crosschain", "migrate_createimporttransaction", &migrate_createimporttransaction, true },
|
||||||
|
{ "crosschain", "migrate_completeimporttransaction", &migrate_completeimporttransaction, true },
|
||||||
|
|
||||||
/* Mining */
|
/* Mining */
|
||||||
{ "mining", "getblocktemplate", &getblocktemplate, true },
|
{ "mining", "getblocktemplate", &getblocktemplate, true },
|
||||||
{ "mining", "getmininginfo", &getmininginfo, true },
|
{ "mining", "getmininginfo", &getmininginfo, true },
|
||||||
|
|||||||
@@ -313,11 +313,15 @@ extern UniValue z_validateaddress(const UniValue& params, bool fHelp); // in rpc
|
|||||||
extern UniValue z_getpaymentdisclosure(const UniValue& params, bool fHelp); // in rpcdisclosure.cpp
|
extern UniValue z_getpaymentdisclosure(const UniValue& params, bool fHelp); // in rpcdisclosure.cpp
|
||||||
extern UniValue z_validatepaymentdisclosure(const UniValue ¶ms, bool fHelp); // in rpcdisclosure.cpp
|
extern UniValue z_validatepaymentdisclosure(const UniValue ¶ms, bool fHelp); // in rpcdisclosure.cpp
|
||||||
|
|
||||||
extern UniValue allMoMs(const UniValue& params, bool fHelp);
|
|
||||||
extern UniValue MoMoMdata(const UniValue& params, bool fHelp);
|
extern UniValue MoMoMdata(const UniValue& params, bool fHelp);
|
||||||
extern UniValue calc_MoM(const UniValue& params, bool fHelp);
|
extern UniValue calc_MoM(const UniValue& params, bool fHelp);
|
||||||
extern UniValue height_MoM(const UniValue& params, bool fHelp);
|
extern UniValue height_MoM(const UniValue& params, bool fHelp);
|
||||||
extern UniValue txMoMproof(const UniValue& params, bool fHelp);
|
extern UniValue assetchainproof(const UniValue& params, bool fHelp);
|
||||||
|
extern UniValue crosschainproof(const UniValue& params, bool fHelp);
|
||||||
|
extern UniValue migrate_converttoexport(const UniValue& params, bool fHelp);
|
||||||
|
extern UniValue migrate_createimporttransaction(const UniValue& params, bool fHelp);
|
||||||
|
extern UniValue migrate_completeimporttransaction(const UniValue& params, bool fHelp);
|
||||||
|
|
||||||
extern UniValue notaries(const UniValue& params, bool fHelp);
|
extern UniValue notaries(const UniValue& params, bool fHelp);
|
||||||
extern UniValue minerids(const UniValue& params, bool fHelp);
|
extern UniValue minerids(const UniValue& params, bool fHelp);
|
||||||
extern UniValue kvsearch(const UniValue& params, bool fHelp);
|
extern UniValue kvsearch(const UniValue& params, bool fHelp);
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#ifndef SCRIPT_CC_H
|
#ifndef SCRIPT_CC_H
|
||||||
#define SCRIPT_CC_H
|
#define SCRIPT_CC_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "pubkey.h"
|
#include "pubkey.h"
|
||||||
#include "script/script.h"
|
#include "script/script.h"
|
||||||
#include "cryptoconditions/include/cryptoconditions.h"
|
#include "cryptoconditions/include/cryptoconditions.h"
|
||||||
@@ -79,5 +81,4 @@ bool GetPushData(const CScript &sig, std::vector<unsigned char> &data);
|
|||||||
*/
|
*/
|
||||||
bool GetOpReturnData(const CScript &sig, std::vector<unsigned char> &data);
|
bool GetOpReturnData(const CScript &sig, std::vector<unsigned char> &data);
|
||||||
|
|
||||||
|
|
||||||
#endif /* SCRIPT_CC_H */
|
#endif /* SCRIPT_CC_H */
|
||||||
|
|||||||
@@ -3,17 +3,20 @@
|
|||||||
// Distributed under the MIT software license, see the accompanying
|
// Distributed under the MIT software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include <cryptoconditions.h>
|
||||||
|
|
||||||
#include "interpreter.h"
|
#include "interpreter.h"
|
||||||
|
|
||||||
#include "consensus/upgrades.h"
|
#include "consensus/upgrades.h"
|
||||||
#include "primitives/transaction.h"
|
#include "primitives/transaction.h"
|
||||||
|
#include "cc/eval.h"
|
||||||
#include "crypto/ripemd160.h"
|
#include "crypto/ripemd160.h"
|
||||||
#include "crypto/sha1.h"
|
#include "crypto/sha1.h"
|
||||||
#include "crypto/sha256.h"
|
#include "crypto/sha256.h"
|
||||||
#include "pubkey.h"
|
#include "pubkey.h"
|
||||||
#include "script/script.h"
|
#include "script/script.h"
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
#include "cryptoconditions/include/cryptoconditions.h"
|
|
||||||
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|||||||
@@ -186,5 +186,4 @@ bool VerifyScript(
|
|||||||
const BaseSignatureChecker& checker,
|
const BaseSignatureChecker& checker,
|
||||||
uint32_t consensusBranchId,
|
uint32_t consensusBranchId,
|
||||||
ScriptError* serror = NULL);
|
ScriptError* serror = NULL);
|
||||||
|
|
||||||
#endif // BITCOIN_SCRIPT_INTERPRETER_H
|
#endif // BITCOIN_SCRIPT_INTERPRETER_H
|
||||||
|
|||||||
@@ -8,8 +8,10 @@
|
|||||||
#include "tinyformat.h"
|
#include "tinyformat.h"
|
||||||
#include "utilstrencodings.h"
|
#include "utilstrencodings.h"
|
||||||
#include "script/cc.h"
|
#include "script/cc.h"
|
||||||
|
#include "cc/eval.h"
|
||||||
#include "cryptoconditions/include/cryptoconditions.h"
|
#include "cryptoconditions/include/cryptoconditions.h"
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
inline std::string ValueString(const std::vector<unsigned char>& vch)
|
inline std::string ValueString(const std::vector<unsigned char>& vch)
|
||||||
{
|
{
|
||||||
@@ -227,6 +229,14 @@ bool CScript::IsPayToPublicKeyHash() const
|
|||||||
(*this)[24] == OP_CHECKSIG);
|
(*this)[24] == OP_CHECKSIG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CScript::IsPayToPublicKey() const
|
||||||
|
{
|
||||||
|
// Extra-fast test for pay-to-pubkey CScripts:
|
||||||
|
return (this->size() == 35 &&
|
||||||
|
(*this)[0] == 33 &&
|
||||||
|
(*this)[34] == OP_CHECKSIG);
|
||||||
|
}
|
||||||
|
|
||||||
bool CScript::IsPayToScriptHash() const
|
bool CScript::IsPayToScriptHash() const
|
||||||
{
|
{
|
||||||
// Extra-fast test for pay-to-script-hash CScripts:
|
// Extra-fast test for pay-to-script-hash CScripts:
|
||||||
@@ -266,6 +276,17 @@ bool CScript::MayAcceptCryptoCondition() const
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CScript::IsCoinImport() const
|
||||||
|
{
|
||||||
|
const_iterator pc = this->begin();
|
||||||
|
vector<unsigned char> data;
|
||||||
|
opcodetype opcode;
|
||||||
|
if (this->GetOp(pc, opcode, data))
|
||||||
|
if (opcode > OP_0 && opcode <= OP_PUSHDATA4)
|
||||||
|
return data.begin()[0] == EVAL_IMPORTCOIN;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool CScript::IsPushOnly() const
|
bool CScript::IsPushOnly() const
|
||||||
{
|
{
|
||||||
const_iterator pc = begin();
|
const_iterator pc = begin();
|
||||||
|
|||||||
@@ -567,9 +567,11 @@ public:
|
|||||||
unsigned int GetSigOpCount(const CScript& scriptSig) const;
|
unsigned int GetSigOpCount(const CScript& scriptSig) const;
|
||||||
|
|
||||||
bool IsPayToPublicKeyHash() const;
|
bool IsPayToPublicKeyHash() const;
|
||||||
|
bool IsPayToPublicKey() const;
|
||||||
|
|
||||||
bool IsPayToScriptHash() const;
|
bool IsPayToScriptHash() const;
|
||||||
bool IsPayToCryptoCondition() const;
|
bool IsPayToCryptoCondition() const;
|
||||||
|
bool IsCoinImport() const;
|
||||||
bool MayAcceptCryptoCondition() const;
|
bool MayAcceptCryptoCondition() const;
|
||||||
|
|
||||||
/** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */
|
/** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */
|
||||||
|
|||||||
@@ -1,14 +1,22 @@
|
|||||||
#include "key.h"
|
#include "key.h"
|
||||||
|
#include "base58.h"
|
||||||
#include "chainparams.h"
|
#include "chainparams.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "crypto/common.h"
|
#include "crypto/common.h"
|
||||||
|
#include "testutils.h"
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
assert(init_and_check_sodium() != -1);
|
assert(init_and_check_sodium() != -1);
|
||||||
ECC_Start();
|
ECC_Start();
|
||||||
|
ECCVerifyHandle handle; // Inits secp256k1 verify context
|
||||||
SelectParams(CBaseChainParams::REGTEST);
|
SelectParams(CBaseChainParams::REGTEST);
|
||||||
|
|
||||||
|
CBitcoinSecret vchSecret;
|
||||||
|
// this returns false due to network prefix mismatch but works anyway
|
||||||
|
vchSecret.SetString(notarySecret);
|
||||||
|
notaryKey = vchSecret.GetKey();
|
||||||
|
|
||||||
testing::InitGoogleTest(&argc, argv);
|
testing::InitGoogleTest(&argc, argv);
|
||||||
return RUN_ALL_TESTS();
|
return RUN_ALL_TESTS();
|
||||||
}
|
}
|
||||||
|
|||||||
257
src/test-komodo/test_coinimport.cpp
Normal file
257
src/test-komodo/test_coinimport.cpp
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
|
||||||
|
#include <cryptoconditions.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "cc/eval.h"
|
||||||
|
#include "importcoin.h"
|
||||||
|
#include "base58.h"
|
||||||
|
#include "core_io.h"
|
||||||
|
#include "key.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "primitives/transaction.h"
|
||||||
|
#include "script/cc.h"
|
||||||
|
#include "script/interpreter.h"
|
||||||
|
#include "script/serverchecker.h"
|
||||||
|
#include "txmempool.h"
|
||||||
|
|
||||||
|
#include "testutils.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern Eval* EVAL_TEST;
|
||||||
|
|
||||||
|
namespace TestCoinImport {
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t testNum = 0;
|
||||||
|
|
||||||
|
class TestCoinImport : public ::testing::Test, public Eval {
|
||||||
|
public:
|
||||||
|
CMutableTransaction burnTx;
|
||||||
|
std::vector<CTxOut> payouts;
|
||||||
|
TxProof proof;
|
||||||
|
uint256 MoMoM;
|
||||||
|
CMutableTransaction importTx;
|
||||||
|
uint32_t testCcid = 2;
|
||||||
|
std::string testSymbol = "PIZZA";
|
||||||
|
CAmount amount = 100;
|
||||||
|
|
||||||
|
void SetImportTx() {
|
||||||
|
burnTx.vout.resize(0);
|
||||||
|
burnTx.vout.push_back(MakeBurnOutput(amount, testCcid, testSymbol, payouts));
|
||||||
|
importTx = CMutableTransaction(MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts));
|
||||||
|
MoMoM = burnTx.GetHash(); // TODO: an actual branch
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GetAssetchainsCC() const { return testCcid; }
|
||||||
|
std::string GetAssetchainsSymbol() const { return testSymbol; }
|
||||||
|
|
||||||
|
bool GetProofRoot(uint256 hash, uint256 &momom) const
|
||||||
|
{
|
||||||
|
if (MoMoM.IsNull()) return false;
|
||||||
|
momom = MoMoM;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void SetUpTestCase() { setupChain(); }
|
||||||
|
virtual void SetUp() {
|
||||||
|
ASSETCHAINS_CC = 1;
|
||||||
|
EVAL_TEST = this;
|
||||||
|
|
||||||
|
std::vector<uint8_t> fakepk;
|
||||||
|
fakepk.resize(33);
|
||||||
|
fakepk.begin()[0] = testNum++;
|
||||||
|
payouts.push_back(CTxOut(amount, CScript() << fakepk << OP_CHECKSIG));
|
||||||
|
SetImportTx();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TestRunCCEval(CMutableTransaction mtx)
|
||||||
|
{
|
||||||
|
CTransaction importTx(mtx);
|
||||||
|
PrecomputedTransactionData txdata(importTx);
|
||||||
|
ServerTransactionSignatureChecker checker(&importTx, 0, 0, false, txdata);
|
||||||
|
CValidationState verifystate;
|
||||||
|
if (!VerifyCoinImport(importTx.vin[0].scriptSig, checker, verifystate))
|
||||||
|
printf("TestRunCCEval: %s\n", verifystate.GetRejectReason().data());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(TestCoinImport, testProcessImportThroughPipeline)
|
||||||
|
{
|
||||||
|
CValidationState mainstate;
|
||||||
|
CTransaction tx(importTx);
|
||||||
|
|
||||||
|
// first should work
|
||||||
|
acceptTxFail(tx);
|
||||||
|
|
||||||
|
// should fail in mempool
|
||||||
|
ASSERT_FALSE(acceptTx(tx, mainstate));
|
||||||
|
EXPECT_EQ("already in mempool", mainstate.GetRejectReason());
|
||||||
|
|
||||||
|
// should be in persisted UTXO set
|
||||||
|
generateBlock();
|
||||||
|
ASSERT_FALSE(acceptTx(tx, mainstate));
|
||||||
|
EXPECT_EQ("already have coins", mainstate.GetRejectReason());
|
||||||
|
ASSERT_TRUE(pcoinsTip->HaveCoins(tx.GetHash()));
|
||||||
|
|
||||||
|
// Now disconnect the block
|
||||||
|
CValidationState invalstate;
|
||||||
|
if (!InvalidateBlock(invalstate, chainActive.Tip())) {
|
||||||
|
FAIL() << invalstate.GetRejectReason();
|
||||||
|
}
|
||||||
|
ASSERT_FALSE(pcoinsTip->HaveCoins(tx.GetHash()));
|
||||||
|
|
||||||
|
// should be back in mempool
|
||||||
|
ASSERT_FALSE(acceptTx(tx, mainstate));
|
||||||
|
EXPECT_EQ("already in mempool", mainstate.GetRejectReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(TestCoinImport, testImportTombstone)
|
||||||
|
{
|
||||||
|
CValidationState mainstate;
|
||||||
|
// By setting an unspendable output, there will be no addition to UTXO
|
||||||
|
// Nonetheless, we dont want to be able to import twice
|
||||||
|
payouts[0].scriptPubKey = CScript() << OP_RETURN;
|
||||||
|
SetImportTx();
|
||||||
|
MoMoM = burnTx.GetHash(); // TODO: an actual branch
|
||||||
|
CTransaction tx(importTx);
|
||||||
|
|
||||||
|
// first should work
|
||||||
|
acceptTxFail(tx);
|
||||||
|
|
||||||
|
// should be in persisted UTXO set
|
||||||
|
generateBlock();
|
||||||
|
ASSERT_FALSE(acceptTx(tx, mainstate));
|
||||||
|
EXPECT_EQ("import tombstone exists", mainstate.GetRejectReason());
|
||||||
|
ASSERT_TRUE(pcoinsTip->HaveCoins(burnTx.GetHash()));
|
||||||
|
|
||||||
|
// Now disconnect the block
|
||||||
|
CValidationState invalstate;
|
||||||
|
if (!InvalidateBlock(invalstate, chainActive.Tip())) {
|
||||||
|
FAIL() << invalstate.GetRejectReason();
|
||||||
|
}
|
||||||
|
// Tombstone should be gone from utxo set
|
||||||
|
ASSERT_FALSE(pcoinsTip->HaveCoins(burnTx.GetHash()));
|
||||||
|
|
||||||
|
// should be back in mempool
|
||||||
|
ASSERT_FALSE(acceptTx(tx, mainstate));
|
||||||
|
EXPECT_EQ("already in mempool", mainstate.GetRejectReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(TestCoinImport, testNoVouts)
|
||||||
|
{
|
||||||
|
importTx.vout.resize(0);
|
||||||
|
TestRunCCEval(importTx);
|
||||||
|
EXPECT_EQ("too-few-vouts", state.GetRejectReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(TestCoinImport, testInvalidParams)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> payload = E_MARSHAL(ss << EVAL_IMPORTCOIN; ss << 'a');
|
||||||
|
importTx.vin[0].scriptSig = CScript() << payload;
|
||||||
|
TestRunCCEval(importTx);
|
||||||
|
EXPECT_EQ("invalid-params", state.GetRejectReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(TestCoinImport, testNonCanonical)
|
||||||
|
{
|
||||||
|
importTx.nLockTime = 10;
|
||||||
|
TestRunCCEval(importTx);
|
||||||
|
EXPECT_EQ("non-canonical", state.GetRejectReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(TestCoinImport, testInvalidBurnOutputs)
|
||||||
|
{
|
||||||
|
burnTx.vout.resize(0);
|
||||||
|
MoMoM = burnTx.GetHash(); // TODO: an actual branch
|
||||||
|
CTransaction tx = MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts);
|
||||||
|
TestRunCCEval(tx);
|
||||||
|
EXPECT_EQ("invalid-burn-tx", state.GetRejectReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(TestCoinImport, testInvalidBurnParams)
|
||||||
|
{
|
||||||
|
burnTx.vout[0].scriptPubKey = CScript() << OP_RETURN << E_MARSHAL(ss << VARINT(testCcid));
|
||||||
|
MoMoM = burnTx.GetHash(); // TODO: an actual branch
|
||||||
|
CTransaction tx = MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts);
|
||||||
|
TestRunCCEval(tx);
|
||||||
|
EXPECT_EQ("invalid-burn-tx", state.GetRejectReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(TestCoinImport, testWrongChainId)
|
||||||
|
{
|
||||||
|
testCcid = 0;
|
||||||
|
TestRunCCEval(importTx);
|
||||||
|
EXPECT_EQ("importcoin-wrong-chain", state.GetRejectReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(TestCoinImport, testInvalidBurnAmount)
|
||||||
|
{
|
||||||
|
burnTx.vout[0].nValue = 0;
|
||||||
|
MoMoM = burnTx.GetHash(); // TODO: an actual branch
|
||||||
|
CTransaction tx = MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts);
|
||||||
|
TestRunCCEval(tx);
|
||||||
|
EXPECT_EQ("invalid-burn-amount", state.GetRejectReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(TestCoinImport, testPayoutTooHigh)
|
||||||
|
{
|
||||||
|
importTx.vout[1].nValue = 101;
|
||||||
|
TestRunCCEval(importTx);
|
||||||
|
EXPECT_EQ("payout-too-high", state.GetRejectReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(TestCoinImport, testAmountInOpret)
|
||||||
|
{
|
||||||
|
importTx.vout[0].nValue = 1;
|
||||||
|
TestRunCCEval(importTx);
|
||||||
|
EXPECT_EQ("non-canonical", state.GetRejectReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(TestCoinImport, testInvalidPayouts)
|
||||||
|
{
|
||||||
|
importTx.vout[1].nValue = 40;
|
||||||
|
importTx.vout.push_back(importTx.vout[0]);
|
||||||
|
TestRunCCEval(importTx);
|
||||||
|
EXPECT_EQ("wrong-payouts", state.GetRejectReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(TestCoinImport, testCouldntLoadMomom)
|
||||||
|
{
|
||||||
|
MoMoM.SetNull();
|
||||||
|
TestRunCCEval(importTx);
|
||||||
|
EXPECT_EQ("coudnt-load-momom", state.GetRejectReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(TestCoinImport, testMomomCheckFail)
|
||||||
|
{
|
||||||
|
MoMoM.SetNull();
|
||||||
|
MoMoM.begin()[0] = 1;
|
||||||
|
TestRunCCEval(importTx);
|
||||||
|
EXPECT_EQ("momom-check-fail", state.GetRejectReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(TestCoinImport, testGetCoinImportValue)
|
||||||
|
{
|
||||||
|
ASSERT_EQ(100, GetCoinImportValue(importTx));
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* namespace TestCoinImport */
|
||||||
213
src/test-komodo/test_crosschain.cpp
Normal file
213
src/test-komodo/test_crosschain.cpp
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
#include <zmq.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <cryptoconditions.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "cc/eval.h"
|
||||||
|
#include "importcoin.h"
|
||||||
|
#include "base58.h"
|
||||||
|
#include "core_io.h"
|
||||||
|
#include "crosschain.h"
|
||||||
|
#include "key.h"
|
||||||
|
#include "komodo_structs.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "notarisationdb.h"
|
||||||
|
#include "primitives/block.h"
|
||||||
|
#include "primitives/transaction.h"
|
||||||
|
#include "script/cc.h"
|
||||||
|
#include "script/interpreter.h"
|
||||||
|
#include "script/serverchecker.h"
|
||||||
|
#include "txmempool.h"
|
||||||
|
#include "crosschain.h"
|
||||||
|
|
||||||
|
#include "testutils.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth);
|
||||||
|
extern bool KOMODO_TEST_ASSETCHAIN_SKIP_POW;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tests for the whole process of creating and validating notary proofs
|
||||||
|
* using proof roots (MoMoMs). This is to support coin imports.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace TestCrossChainProof {
|
||||||
|
|
||||||
|
|
||||||
|
class TestCrossChain : public ::testing::Test, public Eval {
|
||||||
|
public:
|
||||||
|
bool CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const
|
||||||
|
{
|
||||||
|
NotarisationData data(2);
|
||||||
|
return ParseNotarisationOpReturn(tx, data); // If it parses it's valid
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
static void SetUpTestCase() { }
|
||||||
|
virtual void SetUp() {
|
||||||
|
KOMODO_TEST_ASSETCHAIN_SKIP_POW = 1;
|
||||||
|
ASSETCHAINS_CC = 1;
|
||||||
|
EVAL_TEST = this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
uint256 endianHash(uint256 h)
|
||||||
|
{
|
||||||
|
uint256 out;
|
||||||
|
for (int i=0; i<32; i++) {
|
||||||
|
out.begin()[31-i] = h.begin()[i];
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(TestCrossChain, testCreateAndValidateImportProof)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This tests the full process of creation of a cross chain proof.
|
||||||
|
* For the purposes of the test we will use one assetchain and a KMD chain.
|
||||||
|
*
|
||||||
|
* In order to do this test, we need 2 blockchains, so we'll fork and make a socket
|
||||||
|
* for IPC.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int childPid = fork();
|
||||||
|
void *ctx = zmq_ctx_new();
|
||||||
|
void *socket = zmq_socket(ctx, ZMQ_PAIR);
|
||||||
|
if (!childPid)
|
||||||
|
strcpy(ASSETCHAINS_SYMBOL, "PIZZA");
|
||||||
|
setupChain();
|
||||||
|
std::vector<CBlock> blocks;
|
||||||
|
blocks.resize(1000);
|
||||||
|
NotarisationData a2kmd(0), kmd2a(1);
|
||||||
|
int numTestNotarisations = 10;
|
||||||
|
|
||||||
|
|
||||||
|
auto SendIPC = [&] (std::vector<uint8_t> v) {
|
||||||
|
assert(v.size() == zmq_send(socket, v.data(), v.size(), 0));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto RecvIPC = [&] () {
|
||||||
|
std::vector<uint8_t> out;
|
||||||
|
out.resize(100000);
|
||||||
|
int len = zmq_recv(socket, out.data(), out.size(), 0);
|
||||||
|
assert(len != -1);
|
||||||
|
out.resize(len);
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto RecordNotarisation = [&] (CTransaction inputTx, NotarisationData data) {
|
||||||
|
CMutableTransaction mtx = spendTx(inputTx);
|
||||||
|
mtx.vout.resize(2);
|
||||||
|
mtx.vout[0].scriptPubKey << VCH(notaryKey.GetPubKey().begin(), 33) << OP_CHECKSIG;
|
||||||
|
mtx.vout[1].scriptPubKey << OP_RETURN << E_MARSHAL(ss << data);
|
||||||
|
mtx.vout[1].nValue = 0;
|
||||||
|
mtx.vin[0].scriptSig << getSig(mtx, inputTx.vout[0].scriptPubKey);
|
||||||
|
|
||||||
|
acceptTxFail(CTransaction(mtx));
|
||||||
|
return mtx.GetHash();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto RunTestAssetchain = [&] ()
|
||||||
|
{
|
||||||
|
NotarisationData n(0), back(1);
|
||||||
|
strcpy(n.symbol, "PIZZA");
|
||||||
|
n.ccId = 2;
|
||||||
|
int height = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send notarisations and write backnotarisations
|
||||||
|
*/
|
||||||
|
for (int ni=0; ni<numTestNotarisations; ni++)
|
||||||
|
{
|
||||||
|
generateBlock(&blocks[++height]);
|
||||||
|
generateBlock(&blocks[++height]);
|
||||||
|
n.blockHash = blocks[height].GetHash();
|
||||||
|
n.MoM = endianHash(komodo_calcMoM(n.height=height, n.MoMDepth=2));
|
||||||
|
SendIPC(E_MARSHAL(ss << n));
|
||||||
|
assert(E_UNMARSHAL(RecvIPC(), ss >> back));
|
||||||
|
RecordNotarisation(blocks[height].vtx[0], back);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test a proof
|
||||||
|
*/
|
||||||
|
uint256 txid = blocks[7].vtx[0].GetHash();
|
||||||
|
TxProof proof = GetAssetchainProof(txid);
|
||||||
|
SendIPC(E_MARSHAL(ss << txid; ss << proof));
|
||||||
|
E_UNMARSHAL(RecvIPC(), ss >> proof);
|
||||||
|
|
||||||
|
std::pair<uint256,NotarisationData> bn;
|
||||||
|
if (!GetNextBacknotarisation(proof.first, bn)) {
|
||||||
|
printf("GetNextBackNotarisation failed\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (proof.second.Exec(txid) != bn.second.MoMoM) {
|
||||||
|
printf("MoMom incorrect\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto RunTestKmd = [&] ()
|
||||||
|
{
|
||||||
|
NotarisationData n(0);
|
||||||
|
int height = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write notarisations and send backnotarisations
|
||||||
|
*/
|
||||||
|
for (int ni=0; ni<numTestNotarisations; ni++)
|
||||||
|
{
|
||||||
|
n.IsBackNotarisation = 0;
|
||||||
|
E_UNMARSHAL(RecvIPC(), ss >> n);
|
||||||
|
// Grab a coinbase input to fund notarisation
|
||||||
|
generateBlock(&blocks[++height]);
|
||||||
|
n.txHash = RecordNotarisation(blocks[height].vtx[0], n);
|
||||||
|
{
|
||||||
|
std::vector<uint256> moms;
|
||||||
|
uint256 destNotarisationTxid;
|
||||||
|
n.MoMoM = CalculateProofRoot(n.symbol, 2, height, moms, destNotarisationTxid);
|
||||||
|
}
|
||||||
|
n.IsBackNotarisation = 1;
|
||||||
|
SendIPC(E_MARSHAL(ss << n));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extend proof
|
||||||
|
*/
|
||||||
|
TxProof proof;
|
||||||
|
uint256 txid;
|
||||||
|
// Extend proof to MoMoM
|
||||||
|
assert(E_UNMARSHAL(RecvIPC(), ss >> txid; ss >> proof));
|
||||||
|
proof = GetCrossChainProof(txid, (char*)"PIZZA", 2, proof);
|
||||||
|
SendIPC(E_MARSHAL(ss << proof));
|
||||||
|
};
|
||||||
|
|
||||||
|
const char endpoint[] = "ipc://tmpKomodoTestCrossChainSock";
|
||||||
|
|
||||||
|
if (!childPid) {
|
||||||
|
assert(0 == zmq_connect(socket, endpoint));
|
||||||
|
usleep(20000);
|
||||||
|
int out = RunTestAssetchain();
|
||||||
|
if (!out) printf("Assetchain success\n");
|
||||||
|
exit(out);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert(0 == zmq_bind(socket, endpoint));
|
||||||
|
RunTestKmd();
|
||||||
|
int returnStatus;
|
||||||
|
waitpid(childPid, &returnStatus, 0);
|
||||||
|
unlink("tmpKomodoTestCrossChainSock");
|
||||||
|
ASSERT_EQ(0, returnStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} /* namespace TestCrossChainProof */
|
||||||
@@ -12,11 +12,6 @@
|
|||||||
#include "testutils.h"
|
#include "testutils.h"
|
||||||
|
|
||||||
|
|
||||||
CKey notaryKey;
|
|
||||||
|
|
||||||
std::string pubkey = "0205a8ad0c1dbc515f149af377981aab58b836af008d4d7ab21bd76faf80550b47";
|
|
||||||
std::string secret = "UxFWWxsf1d7w7K5TvAWSkeX4H95XQKwdwGv49DXwWUTzPTTjHBbU";
|
|
||||||
|
|
||||||
|
|
||||||
class CCTest : public ::testing::Test {
|
class CCTest : public ::testing::Test {
|
||||||
public:
|
public:
|
||||||
@@ -32,11 +27,6 @@ protected:
|
|||||||
virtual void SetUp() {
|
virtual void SetUp() {
|
||||||
// enable CC
|
// enable CC
|
||||||
ASSETCHAINS_CC = 1;
|
ASSETCHAINS_CC = 1;
|
||||||
// Notary key
|
|
||||||
CBitcoinSecret vchSecret;
|
|
||||||
// this returns false due to network prefix mismatch but works anyway
|
|
||||||
vchSecret.SetString(secret);
|
|
||||||
notaryKey = vchSecret.GetKey();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ public:
|
|||||||
int nIndex = 5;
|
int nIndex = 5;
|
||||||
std::vector<uint256> vBranch;
|
std::vector<uint256> vBranch;
|
||||||
vBranch.resize(3);
|
vBranch.resize(3);
|
||||||
return MoMProof(nIndex, vBranch, EvalMock::NotarisationHash());
|
return {MerkleBranch(nIndex, vBranch), EvalMock::NotarisationHash()};
|
||||||
}
|
}
|
||||||
|
|
||||||
CMutableTransaction ImportPayoutTx()
|
CMutableTransaction ImportPayoutTx()
|
||||||
@@ -237,7 +237,7 @@ public:
|
|||||||
eval.currentHeight = currentHeight;
|
eval.currentHeight = currentHeight;
|
||||||
|
|
||||||
MoMProof proof = GetMoMProof();
|
MoMProof proof = GetMoMProof();
|
||||||
eval.MoM = proof.Exec(DisputeTx(Player2).GetHash());
|
eval.MoM = proof.branch.Exec(DisputeTx(Player2).GetHash());
|
||||||
|
|
||||||
EVAL_TEST = &eval;
|
EVAL_TEST = &eval;
|
||||||
return eval;
|
return eval;
|
||||||
@@ -585,7 +585,7 @@ TEST_F(TestBet, testImportPayoutMomFail)
|
|||||||
EvalMock eval = ebet.SetEvalMock(12);
|
EvalMock eval = ebet.SetEvalMock(12);
|
||||||
|
|
||||||
MoMProof proof = ebet.GetMoMProof();
|
MoMProof proof = ebet.GetMoMProof();
|
||||||
proof.nIndex ^= 1;
|
proof.branch.nIndex ^= 1;
|
||||||
CMutableTransaction importTx = ebet.bet.MakeImportPayoutTx(
|
CMutableTransaction importTx = ebet.bet.MakeImportPayoutTx(
|
||||||
ebet.Payouts(Player2), ebet.DisputeTx(Player2), uint256(), proof);
|
ebet.Payouts(Player2), ebet.DisputeTx(Player2), uint256(), proof);
|
||||||
|
|
||||||
|
|||||||
@@ -15,93 +15,90 @@
|
|||||||
#include "testutils.h"
|
#include "testutils.h"
|
||||||
|
|
||||||
|
|
||||||
extern Eval* EVAL_TEST;
|
|
||||||
extern int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp);
|
extern int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp);
|
||||||
|
|
||||||
|
|
||||||
namespace TestEvalNotarisation {
|
namespace TestEvalNotarisation {
|
||||||
|
|
||||||
|
|
||||||
class EvalMock : public Eval
|
class EvalMock : public Eval
|
||||||
{
|
|
||||||
public:
|
|
||||||
uint32_t nNotaries;
|
|
||||||
uint8_t notaries[64][33];
|
|
||||||
std::map<uint256, CTransaction> txs;
|
|
||||||
std::map<uint256, CBlockIndex> blocks;
|
|
||||||
|
|
||||||
int32_t GetNotaries(uint8_t pubkeys[64][33], int32_t height, uint32_t timestamp) const
|
|
||||||
{
|
{
|
||||||
memcpy(pubkeys, notaries, sizeof(notaries));
|
public:
|
||||||
return nNotaries;
|
uint32_t nNotaries;
|
||||||
}
|
uint8_t notaries[64][33];
|
||||||
|
std::map<uint256, CTransaction> txs;
|
||||||
|
std::map<uint256, CBlockIndex> blocks;
|
||||||
|
|
||||||
bool GetTxUnconfirmed(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock) const
|
int32_t GetNotaries(uint8_t pubkeys[64][33], int32_t height, uint32_t timestamp) const
|
||||||
{
|
{
|
||||||
auto r = txs.find(hash);
|
memcpy(pubkeys, notaries, sizeof(notaries));
|
||||||
if (r != txs.end()) {
|
return nNotaries;
|
||||||
txOut = r->second;
|
}
|
||||||
if (blocks.count(hash) > 0)
|
|
||||||
hashBlock = hash;
|
bool GetTxUnconfirmed(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock) const
|
||||||
return true;
|
{
|
||||||
|
auto r = txs.find(hash);
|
||||||
|
if (r != txs.end()) {
|
||||||
|
txOut = r->second;
|
||||||
|
if (blocks.count(hash) > 0)
|
||||||
|
hashBlock = hash;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetBlock(uint256 hash, CBlockIndex& blockIdx) const
|
||||||
|
{
|
||||||
|
auto r = blocks.find(hash);
|
||||||
|
if (r == blocks.end()) return false;
|
||||||
|
blockIdx = r->second;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto noop = [&](CMutableTransaction &mtx){};
|
||||||
|
|
||||||
|
|
||||||
|
template<typename Modifier>
|
||||||
|
void SetupEval(EvalMock &eval, CMutableTransaction ¬ary, Modifier modify)
|
||||||
|
{
|
||||||
|
eval.nNotaries = komodo_notaries(eval.notaries, 780060, 1522946781);
|
||||||
|
|
||||||
|
// make fake notary inputs
|
||||||
|
notary.vin.resize(11);
|
||||||
|
for (int i=0; i<notary.vin.size(); i++) {
|
||||||
|
CMutableTransaction txIn;
|
||||||
|
txIn.vout.resize(1);
|
||||||
|
txIn.vout[0].scriptPubKey << VCH(eval.notaries[i*2], 33) << OP_CHECKSIG;
|
||||||
|
notary.vin[i].prevout = COutPoint(txIn.GetHash(), 0);
|
||||||
|
eval.txs[txIn.GetHash()] = CTransaction(txIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
modify(notary);
|
||||||
|
|
||||||
|
eval.txs[notary.GetHash()] = CTransaction(notary);
|
||||||
|
eval.blocks[notary.GetHash()].nHeight = 780060;
|
||||||
|
eval.blocks[notary.GetHash()].nTime = 1522946781;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetBlock(uint256 hash, CBlockIndex& blockIdx) const
|
|
||||||
{
|
|
||||||
auto r = blocks.find(hash);
|
|
||||||
if (r == blocks.end()) return false;
|
|
||||||
blockIdx = r->second;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static auto noop = [&](CMutableTransaction &mtx){};
|
|
||||||
|
|
||||||
|
|
||||||
template<typename Modifier>
|
// https://kmd.explorer.supernet.org/tx/5b8055d37cff745a404d1ae45e21ffdba62da7b28ed6533c67468d7379b20bae
|
||||||
void SetEval(EvalMock &eval, CMutableTransaction ¬ary, Modifier modify)
|
// inputs have been dropped
|
||||||
{
|
static auto rawNotaryTx = "01000000000290460100000000002321020e46e79a2a8d12b9b5d12c7a91adb4e454edfae43c0a0cb805427d2ac7613fd9ac0000000000000000506a4c4dae8e0f3e6e5de498a072f5967f3c418c4faba5d56ac8ce17f472d029ef3000008f2e0100424f545300050ba773f0bc31da5839fc7cb9bd7b87f3b765ca608e5cf66785a466659b28880500000000000000";
|
||||||
eval.nNotaries = komodo_notaries(eval.notaries, 780060, 1522946781);
|
CTransaction notaryTx;
|
||||||
|
static bool init = DecodeHexTx(notaryTx, rawNotaryTx);
|
||||||
// make fake notary inputs
|
|
||||||
notary.vin.resize(11);
|
|
||||||
for (int i=0; i<notary.vin.size(); i++) {
|
|
||||||
CMutableTransaction txIn;
|
|
||||||
txIn.vout.resize(1);
|
|
||||||
txIn.vout[0].scriptPubKey << VCH(eval.notaries[i*2], 33) << OP_CHECKSIG;
|
|
||||||
notary.vin[i].prevout = COutPoint(txIn.GetHash(), 0);
|
|
||||||
eval.txs[txIn.GetHash()] = CTransaction(txIn);
|
|
||||||
}
|
|
||||||
|
|
||||||
modify(notary);
|
static uint256 proofTxHash = uint256S("37f76551a16093fbb0a92ee635bbd45b3460da8fd00cf7d5a6b20d93e727fe4c");
|
||||||
|
static auto vMomProof = ParseHex("0303faecbdd4b3da128c2cd2701bb143820a967069375b2ec5b612f39bbfe78a8611978871c193457ab1e21b9520f4139f113b8d75892eb93ee247c18bccfd067efed7eacbfcdc8946cf22de45ad536ec0719034fb9bc825048fe6ab61fee5bd6e9aae0bb279738d46673c53d68eb2a72da6dbff215ee41a4d405a74ff7cd355805b"); // $ fiat/bots txMoMproof $proofTxHash
|
||||||
|
|
||||||
eval.txs[notary.GetHash()] = CTransaction(notary);
|
/*
|
||||||
eval.blocks[notary.GetHash()].nHeight = 780060;
|
TEST(TestEvalNotarisation, testGetNotarisation)
|
||||||
eval.blocks[notary.GetHash()].nTime = 1522946781;
|
{
|
||||||
|
EvalMock eval;
|
||||||
|
CMutableTransaction notary(notaryTx);
|
||||||
|
SetupEval(eval, notary, noop);
|
||||||
|
|
||||||
EVAL_TEST = &eval;
|
NotarisationData data;
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// https://kmd.explorer.supernet.org/tx/5b8055d37cff745a404d1ae45e21ffdba62da7b28ed6533c67468d7379b20bae
|
|
||||||
// inputs have been dropped
|
|
||||||
static auto rawNotaryTx = "01000000000290460100000000002321020e46e79a2a8d12b9b5d12c7a91adb4e454edfae43c0a0cb805427d2ac7613fd9ac0000000000000000506a4c4dae8e0f3e6e5de498a072f5967f3c418c4faba5d56ac8ce17f472d029ef3000008f2e0100424f545300050ba773f0bc31da5839fc7cb9bd7b87f3b765ca608e5cf66785a466659b28880500000000000000";
|
|
||||||
CTransaction notaryTx;
|
|
||||||
static bool init = DecodeHexTx(notaryTx, rawNotaryTx);
|
|
||||||
|
|
||||||
static uint256 proofTxHash = uint256S("37f76551a16093fbb0a92ee635bbd45b3460da8fd00cf7d5a6b20d93e727fe4c");
|
|
||||||
static auto vMomProof = ParseHex("0303faecbdd4b3da128c2cd2701bb143820a967069375b2ec5b612f39bbfe78a8611978871c193457ab1e21b9520f4139f113b8d75892eb93ee247c18bccfd067efed7eacbfcdc8946cf22de45ad536ec0719034fb9bc825048fe6ab61fee5bd6e9aae0bb279738d46673c53d68eb2a72da6dbff215ee41a4d405a74ff7cd355805b"); // $ fiat/bots txMoMproof $proofTxHash
|
|
||||||
|
|
||||||
|
|
||||||
TEST(TestEvalNotarisation, testGetNotarisation)
|
|
||||||
{
|
|
||||||
EvalMock eval;
|
|
||||||
CMutableTransaction notary(notaryTx);
|
|
||||||
SetEval(eval, notary, noop);
|
|
||||||
|
|
||||||
NotarisationData data;
|
|
||||||
ASSERT_TRUE(eval.GetNotarisationData(notary.GetHash(), data));
|
ASSERT_TRUE(eval.GetNotarisationData(notary.GetHash(), data));
|
||||||
EXPECT_EQ(data.height, 77455);
|
EXPECT_EQ(data.height, 77455);
|
||||||
EXPECT_EQ(data.blockHash.GetHex(), "000030ef29d072f417cec86ad5a5ab4f8c413c7f96f572a098e45d6e3e0f8eae");
|
EXPECT_EQ(data.blockHash.GetHex(), "000030ef29d072f417cec86ad5a5ab4f8c413c7f96f572a098e45d6e3e0f8eae");
|
||||||
@@ -111,7 +108,7 @@ TEST(TestEvalNotarisation, testGetNotarisation)
|
|||||||
|
|
||||||
MoMProof proof;
|
MoMProof proof;
|
||||||
E_UNMARSHAL(vMomProof, ss >> proof);
|
E_UNMARSHAL(vMomProof, ss >> proof);
|
||||||
EXPECT_EQ(data.MoM, proof.Exec(proofTxHash));
|
EXPECT_EQ(data.MoM, proof.branch.Exec(proofTxHash));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -119,13 +116,14 @@ TEST(TestEvalNotarisation, testInvalidNotaryPubkey)
|
|||||||
{
|
{
|
||||||
EvalMock eval;
|
EvalMock eval;
|
||||||
CMutableTransaction notary(notaryTx);
|
CMutableTransaction notary(notaryTx);
|
||||||
SetEval(eval, notary, noop);
|
SetupEval(eval, notary, noop);
|
||||||
|
|
||||||
memset(eval.notaries[10], 0, 33);
|
memset(eval.notaries[10], 0, 33);
|
||||||
|
|
||||||
NotarisationData data;
|
NotarisationData data;
|
||||||
ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data));
|
ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data));
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
TEST(TestEvalNotarisation, testInvalidNotarisationBadOpReturn)
|
TEST(TestEvalNotarisation, testInvalidNotarisationBadOpReturn)
|
||||||
@@ -134,9 +132,9 @@ TEST(TestEvalNotarisation, testInvalidNotarisationBadOpReturn)
|
|||||||
CMutableTransaction notary(notaryTx);
|
CMutableTransaction notary(notaryTx);
|
||||||
|
|
||||||
notary.vout[1].scriptPubKey = CScript() << OP_RETURN << 0;
|
notary.vout[1].scriptPubKey = CScript() << OP_RETURN << 0;
|
||||||
SetEval(eval, notary, noop);
|
SetupEval(eval, notary, noop);
|
||||||
|
|
||||||
NotarisationData data;
|
NotarisationData data(0);
|
||||||
ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data));
|
ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,11 +144,11 @@ TEST(TestEvalNotarisation, testInvalidNotarisationTxNotEnoughSigs)
|
|||||||
EvalMock eval;
|
EvalMock eval;
|
||||||
CMutableTransaction notary(notaryTx);
|
CMutableTransaction notary(notaryTx);
|
||||||
|
|
||||||
SetEval(eval, notary, [](CMutableTransaction &tx) {
|
SetupEval(eval, notary, [](CMutableTransaction &tx) {
|
||||||
tx.vin.resize(10);
|
tx.vin.resize(10);
|
||||||
});
|
});
|
||||||
|
|
||||||
NotarisationData data;
|
NotarisationData data(0);
|
||||||
ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data));
|
ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,9 +158,9 @@ TEST(TestEvalNotarisation, testInvalidNotarisationTxDoesntExist)
|
|||||||
EvalMock eval;
|
EvalMock eval;
|
||||||
CMutableTransaction notary(notaryTx);
|
CMutableTransaction notary(notaryTx);
|
||||||
|
|
||||||
SetEval(eval, notary, noop);
|
SetupEval(eval, notary, noop);
|
||||||
|
|
||||||
NotarisationData data;
|
NotarisationData data(0);
|
||||||
ASSERT_FALSE(eval.GetNotarisationData(uint256(), data));
|
ASSERT_FALSE(eval.GetNotarisationData(uint256(), data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,11 +170,11 @@ TEST(TestEvalNotarisation, testInvalidNotarisationDupeNotary)
|
|||||||
EvalMock eval;
|
EvalMock eval;
|
||||||
CMutableTransaction notary(notaryTx);
|
CMutableTransaction notary(notaryTx);
|
||||||
|
|
||||||
SetEval(eval, notary, [](CMutableTransaction &tx) {
|
SetupEval(eval, notary, [](CMutableTransaction &tx) {
|
||||||
tx.vin[1] = tx.vin[3];
|
tx.vin[1] = tx.vin[3];
|
||||||
});
|
});
|
||||||
|
|
||||||
NotarisationData data;
|
NotarisationData data(0);
|
||||||
ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data));
|
ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +184,7 @@ TEST(TestEvalNotarisation, testInvalidNotarisationInputNotCheckSig)
|
|||||||
EvalMock eval;
|
EvalMock eval;
|
||||||
CMutableTransaction notary(notaryTx);
|
CMutableTransaction notary(notaryTx);
|
||||||
|
|
||||||
SetEval(eval, notary, [&](CMutableTransaction &tx) {
|
SetupEval(eval, notary, [&](CMutableTransaction &tx) {
|
||||||
int i = 1;
|
int i = 1;
|
||||||
CMutableTransaction txIn;
|
CMutableTransaction txIn;
|
||||||
txIn.vout.resize(1);
|
txIn.vout.resize(1);
|
||||||
@@ -195,7 +193,7 @@ TEST(TestEvalNotarisation, testInvalidNotarisationInputNotCheckSig)
|
|||||||
eval.txs[txIn.GetHash()] = CTransaction(txIn);
|
eval.txs[txIn.GetHash()] = CTransaction(txIn);
|
||||||
});
|
});
|
||||||
|
|
||||||
NotarisationData data;
|
NotarisationData data(0);
|
||||||
ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data));
|
ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
54
src/test-komodo/test_parse_notarisation.cpp
Normal file
54
src/test-komodo/test_parse_notarisation.cpp
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "cc/eval.h"
|
||||||
|
#include "core_io.h"
|
||||||
|
#include "key.h"
|
||||||
|
|
||||||
|
#include "testutils.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace TestParseNotarisation {
|
||||||
|
|
||||||
|
class TestParseNotarisation : public ::testing::Test, public Eval {};
|
||||||
|
|
||||||
|
|
||||||
|
TEST(TestParseNotarisation, test_ee2fa)
|
||||||
|
{
|
||||||
|
// ee2fa47820a31a979f9f21cb3fedbc484bf9a8957cb6c9acd0af28ced29bdfe1
|
||||||
|
std::vector<uint8_t> opret = ParseHex("c349ff90f3bce62c1b7b49d1da0423b1a3d9b733130cce825b95b9e047c729066e020d00743a06fdb95ad5775d032b30bbb3680dac2091a0f800cf54c79fd3461ce9b31d4b4d4400");
|
||||||
|
NotarisationData nd;
|
||||||
|
ASSERT_TRUE(E_UNMARSHAL(opret, ss >> nd));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestParseNotarisation, test__)
|
||||||
|
{
|
||||||
|
// 576e910a1f704207bcbcf724124ff9adc5237f45cb6919589cd0aa152caec424
|
||||||
|
std::vector<uint8_t> opret = ParseHex("b3ed7fbbfbc027caeeeec81e65489ec5d9cd47cda675a5cbb75b4a845e67cf0ef6330300b5a6bd8385feb833f3be961c9d8a46fcecd36dcdfa42ad81a20a892433722f0b4b4d44004125a06024eae24c11f36ea110acd707b041d5355b6e1b42de5e2614357999c6aa02000d26ad0300000000404b4c000000000005130300500d000061f22ba7d19fe29ac3baebd839af8b7127d1f90755534400");
|
||||||
|
NotarisationData nd;
|
||||||
|
// We can't parse this one
|
||||||
|
ASSERT_FALSE(E_UNMARSHAL(opret, ss >> nd));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestParseNotarisation, test__a)
|
||||||
|
{
|
||||||
|
// be55101e6c5a93fb3611a44bd66217ad8714d204275ea4e691cfff9d65dff85c TXSCL
|
||||||
|
std::vector<uint8_t> opret = ParseHex("fb9ea2818eec8b07f8811bab49d64379db074db478997f8114666f239bd79803cc460000d0fac4e715b7e2b917a5d79f85ece0c423d27bd3648fd39ac1dc7db8e1bd4b16545853434c00a69eab9f23d7fb63c4624973e7a9079d6ada2f327040936356d7af5e849f6d670a0003001caf7b7b9e1c9bc59d0c7a619c9683ab1dd0794b6f3ea184a19f8fda031150e700000000");
|
||||||
|
NotarisationData nd(1);
|
||||||
|
bool res = E_UNMARSHAL(opret, ss >> nd);
|
||||||
|
ASSERT_TRUE(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestParseNotarisation, test__b)
|
||||||
|
{
|
||||||
|
// 03085dafed656aaebfda25bf43ffe9d1fb72565bb1fc8b2a12a631659f28f877 TXSCL
|
||||||
|
std::vector<uint8_t> opret = ParseHex("48c71a10aa060eab1a43f52acefac3b81fb2a2ce310186b06141884c0501d403c246000052e6d49afd82d9ab3d97c996dd9b6a78a554ffa1625e8dadf0494bd1f8442e3e545853434c007cc5c07e3b67520fd14e23cd5b49f2aa022f411500fd3326ff91e6dc0544a1c90c0003008b69117bb1376ac8df960f785d8c208c599d3a36248c98728256bb6d4737e59600000000");
|
||||||
|
NotarisationData nd(1);
|
||||||
|
bool res = E_UNMARSHAL(opret, ss >> nd);
|
||||||
|
ASSERT_TRUE(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// for l in `g 'parse notarisation' ~/.komodo/debug.log | pyline 'l.split()[8]'`; do hoek decodeTx '{"hex":"'`src/komodo-cli getrawtransaction "$l"`'"}' | jq '.outputs[1].script.op_return' | pyline 'import base64; print base64.b64decode(l).encode("hex")'; done
|
||||||
|
|
||||||
|
}
|
||||||
147
src/test-komodo/testutils.cpp
Normal file
147
src/test-komodo/testutils.cpp
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
#include <cryptoconditions.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
|
#include "core_io.h"
|
||||||
|
#include "key.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "miner.h"
|
||||||
|
#include "notarisationdb.h"
|
||||||
|
#include "random.h"
|
||||||
|
#include "rpcserver.h"
|
||||||
|
#include "rpcprotocol.h"
|
||||||
|
#include "txdb.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "utilstrencodings.h"
|
||||||
|
#include "utiltime.h"
|
||||||
|
#include "consensus/validation.h"
|
||||||
|
#include "primitives/transaction.h"
|
||||||
|
#include "script/cc.h"
|
||||||
|
#include "script/interpreter.h"
|
||||||
|
|
||||||
|
#include "testutils.h"
|
||||||
|
|
||||||
|
|
||||||
|
std::string notaryPubkey = "0205a8ad0c1dbc515f149af377981aab58b836af008d4d7ab21bd76faf80550b47";
|
||||||
|
std::string notarySecret = "UxFWWxsf1d7w7K5TvAWSkeX4H95XQKwdwGv49DXwWUTzPTTjHBbU";
|
||||||
|
CKey notaryKey;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to have control of clock,
|
||||||
|
* otherwise block production can fail.
|
||||||
|
*/
|
||||||
|
int64_t nMockTime;
|
||||||
|
|
||||||
|
extern uint32_t USE_EXTERNAL_PUBKEY;
|
||||||
|
extern std::string NOTARY_PUBKEY;
|
||||||
|
|
||||||
|
void setupChain()
|
||||||
|
{
|
||||||
|
SelectParams(CBaseChainParams::REGTEST);
|
||||||
|
|
||||||
|
// Settings to get block reward
|
||||||
|
NOTARY_PUBKEY = notaryPubkey;
|
||||||
|
USE_EXTERNAL_PUBKEY = 1;
|
||||||
|
mapArgs["-mineraddress"] = "bogus";
|
||||||
|
COINBASE_MATURITY = 1;
|
||||||
|
// Global mock time
|
||||||
|
nMockTime = GetTime();
|
||||||
|
|
||||||
|
// Unload
|
||||||
|
UnloadBlockIndex();
|
||||||
|
|
||||||
|
// Init blockchain
|
||||||
|
ClearDatadirCache();
|
||||||
|
auto pathTemp = GetTempPath() / strprintf("test_komodo_%li_%i", GetTime(), GetRand(100000));
|
||||||
|
if (ASSETCHAINS_SYMBOL[0])
|
||||||
|
pathTemp = pathTemp / strprintf("_%s", ASSETCHAINS_SYMBOL);
|
||||||
|
boost::filesystem::create_directories(pathTemp);
|
||||||
|
mapArgs["-datadir"] = pathTemp.string();
|
||||||
|
pblocktree = new CBlockTreeDB(1 << 20, true);
|
||||||
|
CCoinsViewDB *pcoinsdbview = new CCoinsViewDB(1 << 23, true);
|
||||||
|
pcoinsTip = new CCoinsViewCache(pcoinsdbview);
|
||||||
|
pnotarisations = new NotarisationDB(1 << 20, true);
|
||||||
|
InitBlockIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void generateBlock(CBlock *block)
|
||||||
|
{
|
||||||
|
UniValue params;
|
||||||
|
params.setArray();
|
||||||
|
params.push_back(1);
|
||||||
|
uint256 blockId;
|
||||||
|
|
||||||
|
SetMockTime(nMockTime+=100); // CreateNewBlock can fail if not enough time passes
|
||||||
|
|
||||||
|
try {
|
||||||
|
UniValue out = generate(params, false);
|
||||||
|
blockId.SetHex(out[0].getValStr());
|
||||||
|
if (block) ASSERT_TRUE(ReadBlockFromDisk(*block, mapBlockIndex[blockId], false));
|
||||||
|
} catch (const UniValue& e) {
|
||||||
|
FAIL() << "failed to create block: " << e.write().data();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void acceptTxFail(const CTransaction tx)
|
||||||
|
{
|
||||||
|
CValidationState state;
|
||||||
|
if (!acceptTx(tx, state)) FAIL() << state.GetRejectReason();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool acceptTx(const CTransaction tx, CValidationState &state)
|
||||||
|
{
|
||||||
|
LOCK(cs_main);
|
||||||
|
return AcceptToMemoryPool(mempool, state, tx, false, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CMutableTransaction spendTx(const CTransaction &txIn, int nOut)
|
||||||
|
{
|
||||||
|
CMutableTransaction mtx;
|
||||||
|
mtx.vin.resize(1);
|
||||||
|
mtx.vin[0].prevout.hash = txIn.GetHash();
|
||||||
|
mtx.vin[0].prevout.n = nOut;
|
||||||
|
mtx.vout.resize(1);
|
||||||
|
mtx.vout[0].nValue = txIn.vout[nOut].nValue - 1000;
|
||||||
|
return mtx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<uint8_t> getSig(const CMutableTransaction mtx, CScript inputPubKey, int nIn)
|
||||||
|
{
|
||||||
|
uint256 hash = SignatureHash(inputPubKey, mtx, nIn, SIGHASH_ALL, 0, 0);
|
||||||
|
std::vector<uint8_t> vchSig;
|
||||||
|
notaryKey.Sign(hash, vchSig);
|
||||||
|
vchSig.push_back((unsigned char)SIGHASH_ALL);
|
||||||
|
return vchSig;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In order to do tests there needs to be inputs to spend.
|
||||||
|
* This method creates a block and returns a transaction that spends the coinbase.
|
||||||
|
*/
|
||||||
|
void getInputTx(CScript scriptPubKey, CTransaction &txIn)
|
||||||
|
{
|
||||||
|
// Get coinbase
|
||||||
|
CBlock block;
|
||||||
|
generateBlock(&block);
|
||||||
|
CTransaction coinbase = block.vtx[0];
|
||||||
|
|
||||||
|
// Create tx
|
||||||
|
auto mtx = spendTx(coinbase);
|
||||||
|
mtx.vout[0].scriptPubKey = scriptPubKey;
|
||||||
|
uint256 hash = SignatureHash(coinbase.vout[0].scriptPubKey, mtx, 0, SIGHASH_ALL, 0, 0);
|
||||||
|
std::vector<unsigned char> vchSig;
|
||||||
|
notaryKey.Sign(hash, vchSig);
|
||||||
|
vchSig.push_back((unsigned char)SIGHASH_ALL);
|
||||||
|
mtx.vin[0].scriptSig << vchSig;
|
||||||
|
|
||||||
|
// Accept
|
||||||
|
acceptTxFail(mtx);
|
||||||
|
txIn = CTransaction(mtx);
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef TESTUTILS_H
|
#ifndef TESTUTILS_H
|
||||||
#define TESTUTILS_H
|
#define TESTUTILS_H
|
||||||
|
|
||||||
#include "script/cc.h"
|
#include "main.h"
|
||||||
|
|
||||||
|
|
||||||
#define VCH(a,b) std::vector<unsigned char>(a, a + b)
|
#define VCH(a,b) std::vector<unsigned char>(a, a + b)
|
||||||
@@ -12,4 +12,18 @@ static char ccjsonerr[1000] = "\0";
|
|||||||
if (!o) FAIL() << "bad json: " << ccjsonerr;
|
if (!o) FAIL() << "bad json: " << ccjsonerr;
|
||||||
|
|
||||||
|
|
||||||
|
extern std::string notaryPubkey;
|
||||||
|
extern std::string notarySecret;
|
||||||
|
extern CKey notaryKey;
|
||||||
|
|
||||||
|
|
||||||
|
void setupChain();
|
||||||
|
void generateBlock(CBlock *block=NULL);
|
||||||
|
bool acceptTx(const CTransaction tx, CValidationState &state);
|
||||||
|
void acceptTxFail(const CTransaction tx);
|
||||||
|
void getInputTx(CScript scriptPubKey, CTransaction &txIn);
|
||||||
|
CMutableTransaction spendTx(const CTransaction &txIn, int nOut=0);
|
||||||
|
std::vector<uint8_t> getSig(const CMutableTransaction mtx, CScript inputPubKey, int nIn=0);
|
||||||
|
|
||||||
|
|
||||||
#endif /* TESTUTILS_H */
|
#endif /* TESTUTILS_H */
|
||||||
|
|||||||
@@ -104,8 +104,10 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
|
|||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
mapTx.insert(entry);
|
mapTx.insert(entry);
|
||||||
const CTransaction& tx = mapTx.find(hash)->GetTx();
|
const CTransaction& tx = mapTx.find(hash)->GetTx();
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
if (!tx.IsCoinImport()) {
|
||||||
mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i);
|
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||||
|
mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i);
|
||||||
|
}
|
||||||
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
|
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
|
||||||
BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) {
|
BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) {
|
||||||
mapNullifiers[nf] = &tx;
|
mapNullifiers[nf] = &tx;
|
||||||
@@ -135,13 +137,21 @@ void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewC
|
|||||||
CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
|
CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
|
||||||
mapAddress.insert(make_pair(key, delta));
|
mapAddress.insert(make_pair(key, delta));
|
||||||
inserted.push_back(key);
|
inserted.push_back(key);
|
||||||
} else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) {
|
}
|
||||||
|
else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) {
|
||||||
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23);
|
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23);
|
||||||
CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, j, 1);
|
CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, j, 1);
|
||||||
CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
|
CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
|
||||||
mapAddress.insert(make_pair(key, delta));
|
mapAddress.insert(make_pair(key, delta));
|
||||||
inserted.push_back(key);
|
inserted.push_back(key);
|
||||||
}
|
}
|
||||||
|
else if (prevout.scriptPubKey.IsPayToPublicKey()) {
|
||||||
|
vector<unsigned char> hashBytes(prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34);
|
||||||
|
CMempoolAddressDeltaKey key(1, Hash160(hashBytes), txhash, j, 1);
|
||||||
|
CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n);
|
||||||
|
mapAddress.insert(make_pair(key, delta));
|
||||||
|
inserted.push_back(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned int k = 0; k < tx.vout.size(); k++) {
|
for (unsigned int k = 0; k < tx.vout.size(); k++) {
|
||||||
@@ -151,13 +161,21 @@ void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewC
|
|||||||
CMempoolAddressDeltaKey key(2, uint160(hashBytes), txhash, k, 0);
|
CMempoolAddressDeltaKey key(2, uint160(hashBytes), txhash, k, 0);
|
||||||
mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue)));
|
mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue)));
|
||||||
inserted.push_back(key);
|
inserted.push_back(key);
|
||||||
} else if (out.scriptPubKey.IsPayToPublicKeyHash()) {
|
}
|
||||||
|
else if (out.scriptPubKey.IsPayToPublicKeyHash()) {
|
||||||
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23);
|
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23);
|
||||||
std::pair<addressDeltaMap::iterator,bool> ret;
|
std::pair<addressDeltaMap::iterator,bool> ret;
|
||||||
CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, k, 0);
|
CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, k, 0);
|
||||||
mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue)));
|
mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue)));
|
||||||
inserted.push_back(key);
|
inserted.push_back(key);
|
||||||
}
|
}
|
||||||
|
else if (out.scriptPubKey.IsPayToPublicKey()) {
|
||||||
|
vector<unsigned char> hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34);
|
||||||
|
std::pair<addressDeltaMap::iterator,bool> ret;
|
||||||
|
CMempoolAddressDeltaKey key(1, Hash160(hashBytes), txhash, k, 0);
|
||||||
|
mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue)));
|
||||||
|
inserted.push_back(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mapAddressInserted.insert(make_pair(txhash, inserted));
|
mapAddressInserted.insert(make_pair(txhash, inserted));
|
||||||
@@ -210,10 +228,16 @@ void CTxMemPool::addSpentIndex(const CTxMemPoolEntry &entry, const CCoinsViewCac
|
|||||||
if (prevout.scriptPubKey.IsPayToScriptHash()) {
|
if (prevout.scriptPubKey.IsPayToScriptHash()) {
|
||||||
addressHash = uint160(vector<unsigned char> (prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22));
|
addressHash = uint160(vector<unsigned char> (prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22));
|
||||||
addressType = 2;
|
addressType = 2;
|
||||||
} else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) {
|
}
|
||||||
|
else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) {
|
||||||
addressHash = uint160(vector<unsigned char> (prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23));
|
addressHash = uint160(vector<unsigned char> (prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23));
|
||||||
addressType = 1;
|
addressType = 1;
|
||||||
} else {
|
}
|
||||||
|
else if (prevout.scriptPubKey.IsPayToPublicKey()) {
|
||||||
|
addressHash = Hash160(vector<unsigned char> (prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34));
|
||||||
|
addressType = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
addressHash.SetNull();
|
addressHash.SetNull();
|
||||||
addressType = 0;
|
addressType = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -509,7 +509,31 @@ UniValue kvupdate(const UniValue& params, bool fHelp)
|
|||||||
CWalletTx wtx; UniValue ret(UniValue::VOBJ);
|
CWalletTx wtx; UniValue ret(UniValue::VOBJ);
|
||||||
uint8_t keyvalue[IGUANA_MAXSCRIPTSIZE],opretbuf[IGUANA_MAXSCRIPTSIZE]; int32_t i,coresize,haveprivkey,duration,opretlen,height; uint16_t keylen=0,valuesize=0,refvaluesize=0; uint8_t *key,*value=0; uint32_t flags,tmpflags,n; struct komodo_kv *ptr; uint64_t fee; uint256 privkey,pubkey,refpubkey,sig;
|
uint8_t keyvalue[IGUANA_MAXSCRIPTSIZE],opretbuf[IGUANA_MAXSCRIPTSIZE]; int32_t i,coresize,haveprivkey,duration,opretlen,height; uint16_t keylen=0,valuesize=0,refvaluesize=0; uint8_t *key,*value=0; uint32_t flags,tmpflags,n; struct komodo_kv *ptr; uint64_t fee; uint256 privkey,pubkey,refpubkey,sig;
|
||||||
if (fHelp || params.size() < 3 )
|
if (fHelp || params.size() < 3 )
|
||||||
throw runtime_error("kvupdate key value flags/passphrase");
|
throw runtime_error(
|
||||||
|
"kvupdate key \"value\" days passphrase\n"
|
||||||
|
"\nStore a key value. This feature is only available for asset chains.\n"
|
||||||
|
"\nArguments:\n"
|
||||||
|
"1. key (string, required) key\n"
|
||||||
|
"2. \"value\" (string, required) value\n"
|
||||||
|
"3. days (numeric, required) amount of days(1440 blocks/day) before the key expires. Minimum 1 day\n"
|
||||||
|
"4. passphrase (string, optional) passphrase required to update this key\n"
|
||||||
|
"\nResult:\n"
|
||||||
|
"{\n"
|
||||||
|
" \"coin\": \"xxxxx\", (string) chain the key is stored on\n"
|
||||||
|
" \"height\": xxxxx, (numeric) height the key was stored at\n"
|
||||||
|
" \"expiration\": xxxxx, (numeric) height the key will expire\n"
|
||||||
|
" \"flags\": x, (string) amount of days the key will be stored \n"
|
||||||
|
" \"key\": \"xxxxx\", (numeric) stored key\n"
|
||||||
|
" \"keylen\": xxxxx, (numeric) length of the key\n"
|
||||||
|
" \"value\": \"xxxxx\" (numeric) stored value\n"
|
||||||
|
" \"valuesize\": xxxxx, (string) length of the stored value\n"
|
||||||
|
" \"fee\": xxxxx (string) transaction fee paid to store the key\n"
|
||||||
|
" \"txid\": \"xxxxx\" (string) transaction id\n"
|
||||||
|
"}\n"
|
||||||
|
"\nExamples:\n"
|
||||||
|
+ HelpExampleCli("kvupdate", "examplekey \"examplevalue\" 2 examplepassphrase")
|
||||||
|
+ HelpExampleRpc("kvupdate", "examplekey \"examplevalue\" 2 examplepassphrase")
|
||||||
|
);
|
||||||
if (!EnsureWalletIsAvailable(fHelp))
|
if (!EnsureWalletIsAvailable(fHelp))
|
||||||
return 0;
|
return 0;
|
||||||
if ( ASSETCHAINS_SYMBOL[0] == 0 )
|
if ( ASSETCHAINS_SYMBOL[0] == 0 )
|
||||||
|
|||||||
Reference in New Issue
Block a user