1
.gitignore
vendored
1
.gitignore
vendored
@@ -120,3 +120,4 @@ src/komodo-cli
|
||||
src/komodod
|
||||
src/komodo-tx
|
||||
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 \
|
||||
bloom.cpp \
|
||||
cc/eval.cpp \
|
||||
cc/importpayout.cpp \
|
||||
cc/disputepayout.cpp \
|
||||
cc/import.cpp \
|
||||
cc/betprotocol.cpp \
|
||||
chain.cpp \
|
||||
checkpoints.cpp \
|
||||
crosschain.cpp \
|
||||
deprecation.cpp \
|
||||
httprpc.cpp \
|
||||
httpserver.cpp \
|
||||
@@ -272,12 +272,14 @@ libbitcoin_server_a_SOURCES = \
|
||||
miner.cpp \
|
||||
net.cpp \
|
||||
noui.cpp \
|
||||
notarisationdb.cpp \
|
||||
paymentdisclosure.cpp \
|
||||
paymentdisclosuredb.cpp \
|
||||
policy/fees.cpp \
|
||||
pow.cpp \
|
||||
rest.cpp \
|
||||
rpcblockchain.cpp \
|
||||
rpccrosschain.cpp \
|
||||
rpcmining.cpp \
|
||||
rpcmisc.cpp \
|
||||
rpcnet.cpp \
|
||||
@@ -385,6 +387,7 @@ libbitcoin_common_a_SOURCES = \
|
||||
core_read.cpp \
|
||||
core_write.cpp \
|
||||
hash.cpp \
|
||||
importcoin.cpp \
|
||||
key.cpp \
|
||||
keystore.cpp \
|
||||
netbase.cpp \
|
||||
@@ -605,7 +608,7 @@ endif
|
||||
|
||||
libzcashconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS)
|
||||
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)
|
||||
|
||||
endif
|
||||
|
||||
@@ -5,9 +5,13 @@ bin_PROGRAMS += komodo-test
|
||||
# tool for generating our public parameters
|
||||
komodo_test_SOURCES = \
|
||||
test-komodo/main.cpp \
|
||||
test-komodo/testutils.cpp \
|
||||
test-komodo/test_cryptoconditions.cpp \
|
||||
test-komodo/test_coinimport.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)
|
||||
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#include <cryptoconditions.h>
|
||||
|
||||
#include "hash.h"
|
||||
#include "main.h"
|
||||
#include "chain.h"
|
||||
#include "streams.h"
|
||||
#include "script/cc.h"
|
||||
#include "cc/eval.h"
|
||||
#include "cc/betprotocol.h"
|
||||
#include "cc/eval.h"
|
||||
#include "cc/utils.h"
|
||||
#include "primitives/transaction.h"
|
||||
|
||||
|
||||
@@ -137,3 +141,146 @@ bool GetOpReturnHash(CScript script, uint256 &hash)
|
||||
hash = uint256(vHash);
|
||||
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 "pubkey.h"
|
||||
#include "primitives/block.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "cryptoconditions/include/cryptoconditions.h"
|
||||
|
||||
@@ -11,25 +10,18 @@
|
||||
class MoMProof
|
||||
{
|
||||
public:
|
||||
int nIndex;
|
||||
std::vector<uint256> branch;
|
||||
MerkleBranch branch;
|
||||
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;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
READWRITE(VARINT(nIndex));
|
||||
READWRITE(branch);
|
||||
READWRITE(notarisationHash);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class BetProtocol
|
||||
{
|
||||
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 <cryptoconditions.h>
|
||||
|
||||
#include "primitives/block.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "script/cc.h"
|
||||
#include "cc/eval.h"
|
||||
#include "cc/utils.h"
|
||||
#include "main.h"
|
||||
#include "chain.h"
|
||||
#include "core_io.h"
|
||||
#include "crosschain.h"
|
||||
|
||||
|
||||
Eval* EVAL_TEST = 0;
|
||||
@@ -14,9 +17,7 @@ Eval* EVAL_TEST = 0;
|
||||
|
||||
bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn)
|
||||
{
|
||||
Eval eval_;
|
||||
Eval *eval = EVAL_TEST;
|
||||
if (!eval) eval = &eval_;
|
||||
EvalRef eval;
|
||||
|
||||
bool out = eval->Dispatch(cond, tx, nIn);
|
||||
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);
|
||||
}
|
||||
|
||||
if (ecode == EVAL_IMPORTCOIN) {
|
||||
return ImportCoin(vparams, txTo, nIn);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
@@ -152,54 +157,84 @@ bool Eval::GetNotarisationData(const uint256 notaryHash, NotarisationData &data)
|
||||
CBlockIndex block;
|
||||
if (!GetTxConfirmed(notaryHash, notarisationTx, block)) return false;
|
||||
if (!CheckNotaryInputs(notarisationTx, block.nHeight, block.nTime)) return false;
|
||||
if (notarisationTx.vout.size() < 2) return false;
|
||||
if (!data.Parse(notarisationTx.vout[1].scriptPubKey)) return false;
|
||||
if (!ParseNotarisationOpReturn(notarisationTx, data)) return false;
|
||||
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
|
||||
*/
|
||||
extern char ASSETCHAINS_SYMBOL[16];
|
||||
|
||||
bool NotarisationData::Parse(const CScript scriptPK)
|
||||
bool ParseNotarisationOpReturn(const CTransaction &tx, NotarisationData &data)
|
||||
{
|
||||
*this = NotarisationData();
|
||||
|
||||
if (tx.vout.size() < 2) return false;
|
||||
std::vector<unsigned char> vdata;
|
||||
if (!GetOpReturnData(scriptPK, vdata)) return false;
|
||||
|
||||
CDataStream ss(vdata, SER_NETWORK, PROTOCOL_VERSION);
|
||||
|
||||
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;
|
||||
if (!GetOpReturnData(tx.vout[1].scriptPubKey, vdata)) return false;
|
||||
bool out = E_UNMARSHAL(vdata, ss >> data);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Misc
|
||||
*/
|
||||
|
||||
std::string EvalToStr(EvalCode c)
|
||||
{
|
||||
FOREACH_EVAL(EVAL_GENERATE_STRING);
|
||||
char s[10];
|
||||
sprintf(s, "0x%x", c);
|
||||
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 "cc/utils.h"
|
||||
#include "chain.h"
|
||||
#include "streams.h"
|
||||
#include "version.h"
|
||||
@@ -20,8 +21,10 @@
|
||||
* a possible code is EVAL_BITCOIN_SCRIPT, where the entire binary
|
||||
* after the code is interpreted as a bitcoin script.
|
||||
*/
|
||||
#define FOREACH_EVAL(EVAL) \
|
||||
EVAL(EVAL_IMPORTPAYOUT, 0xe1)
|
||||
#define FOREACH_EVAL(EVAL) \
|
||||
EVAL(EVAL_IMPORTPAYOUT, 0xe1) \
|
||||
EVAL(EVAL_IMPORTCOIN, 0xe2)
|
||||
|
||||
|
||||
typedef uint8_t EvalCode;
|
||||
|
||||
@@ -54,6 +57,11 @@ public:
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@@ -64,10 +72,30 @@ public:
|
||||
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 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 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);
|
||||
|
||||
|
||||
@@ -88,22 +116,89 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* 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;
|
||||
extern char ASSETCHAINS_SYMBOL[65];
|
||||
|
||||
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.
|
||||
*/
|
||||
@@ -116,28 +211,42 @@ std::string EvalToStr(EvalCode c);
|
||||
|
||||
|
||||
/*
|
||||
* Serialisation boilerplate
|
||||
* Merkle stuff
|
||||
*/
|
||||
#define E_MARSHAL(body) SerializeF([&] (CDataStream &ss) {body;})
|
||||
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());
|
||||
}
|
||||
uint256 SafeCheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex);
|
||||
|
||||
#define E_UNMARSHAL(params, body) DeserializeF(params, [&] (CDataStream &ss) {body;})
|
||||
template <class T>
|
||||
bool DeserializeF(const std::vector<unsigned char> vIn, T f)
|
||||
|
||||
class MerkleBranch
|
||||
{
|
||||
CDataStream ss(vIn, SER_NETWORK, PROTOCOL_VERSION);
|
||||
try {
|
||||
f(ss);
|
||||
if (ss.eof()) return true;
|
||||
} catch(...) {}
|
||||
return false;
|
||||
}
|
||||
public:
|
||||
int nIndex;
|
||||
std::vector<uint256> branch;
|
||||
|
||||
MerkleBranch() {}
|
||||
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 */
|
||||
|
||||
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));
|
||||
nEquihashN = N;
|
||||
nEquihashK = K;
|
||||
|
||||
|
||||
genesis = CreateGenesisBlock(
|
||||
1296688602,
|
||||
uint256S("0x0000000000000000000000000000000000000000000000000000000000000009"),
|
||||
ParseHex("01936b7db1eb4ac39f151b8704642d0a8bda13ec547d54cd5e43ba142fc6d8877cab07b3"),
|
||||
|
||||
|
||||
KOMODO_MINDIFF_NBITS, 4, 0);
|
||||
consensus.hashGenesisBlock = genesis.GetHash();
|
||||
assert(consensus.hashGenesisBlock == uint256S("0x029f11d80ef9765602235e1bc9727e3eb6ba20839319f761fee920d63401e327"));
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "version.h"
|
||||
#include "policy/fees.h"
|
||||
#include "komodo_defs.h"
|
||||
#include "importcoin.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 value,nResult = 0;
|
||||
if ( interestp != 0 )
|
||||
*interestp = 0;
|
||||
if ( tx.IsCoinImport() )
|
||||
return GetCoinImportValue(tx);
|
||||
if ( tx.IsCoinBase() != 0 )
|
||||
return 0;
|
||||
CAmount value,nResult = 0;
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
{
|
||||
value = GetOutputFor(tx.vin[i]).nValue;
|
||||
@@ -421,6 +424,7 @@ CAmount CCoinsViewCache::GetValueIn(int32_t nHeight,int64_t *interestp,const CTr
|
||||
return nResult;
|
||||
}
|
||||
|
||||
|
||||
bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const
|
||||
{
|
||||
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
|
||||
{
|
||||
if (!tx.IsCoinBase()) {
|
||||
if (!tx.IsMint()) {
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
||||
const COutPoint &prevout = tx.vin[i].prevout;
|
||||
const CCoins* coins = AccessCoins(prevout.hash);
|
||||
@@ -483,6 +487,9 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const
|
||||
if (tx.vjoinsplit.size() > 0) {
|
||||
return MAX_PRIORITY;
|
||||
}
|
||||
if (tx.IsCoinImport()) {
|
||||
return MAX_PRIORITY;
|
||||
}
|
||||
|
||||
double dResult = 0.0;
|
||||
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 */
|
||||
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 "httprpc.h"
|
||||
#include "key.h"
|
||||
#include "notarisationdb.h"
|
||||
#include "main.h"
|
||||
#include "metrics.h"
|
||||
#include "miner.h"
|
||||
@@ -1413,11 +1414,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
delete pcoinsdbview;
|
||||
delete pcoinscatcher;
|
||||
delete pblocktree;
|
||||
delete pnotarisations;
|
||||
|
||||
pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex, dbCompression, dbMaxOpenFiles);
|
||||
pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex);
|
||||
pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview);
|
||||
pcoinsTip = new CCoinsViewCache(pcoinscatcher);
|
||||
pnotarisations = new NotarisationDB(100*1024*1024, false, fReindex);
|
||||
|
||||
|
||||
if (fReindex) {
|
||||
pblocktree->WriteReindexing(true);
|
||||
|
||||
@@ -1296,6 +1296,8 @@ int64_t komodo_checkcommission(CBlock *pblock,int32_t height)
|
||||
return(checktoshis);
|
||||
}
|
||||
|
||||
bool KOMODO_TEST_ASSETCHAIN_SKIP_POW = 0;
|
||||
|
||||
int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height)
|
||||
{
|
||||
uint256 hash; arith_uint256 bnTarget,bhash; bool fNegative,fOverflow; uint8_t *script,pubkey33[33],pubkeys[64][33]; int32_t i,possible,PoSperc,is_PoSblock=0,n,failed = 0,notaryid = -1; int64_t checktoshis,value; CBlockIndex *pprev;
|
||||
@@ -1335,6 +1337,7 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height)
|
||||
}
|
||||
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());
|
||||
return(-1);
|
||||
}
|
||||
|
||||
@@ -19,54 +19,29 @@
|
||||
struct komodo_ccdata *CC_data;
|
||||
int32_t CC_firstheight;
|
||||
|
||||
bits256 iguana_merkle(bits256 *tree,int32_t txn_count)
|
||||
{
|
||||
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 BuildMerkleTree(bool* fMutated, const std::vector<uint256> leaves, std::vector<uint256> &vMerkleTree);
|
||||
|
||||
uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth)
|
||||
{
|
||||
static uint256 zero; bits256 MoM,*tree; CBlockIndex *pindex; int32_t i;
|
||||
MoMdepth &= 0xffff;
|
||||
static uint256 zero; CBlockIndex *pindex; int32_t i; std::vector<uint256> tree, leaves;
|
||||
bool fMutated;
|
||||
MoMdepth &= 0xffff; // In case it includes the ccid
|
||||
if ( MoMdepth >= height )
|
||||
return(zero);
|
||||
tree = (bits256 *)calloc(MoMdepth * 3,sizeof(*tree));
|
||||
for (i=0; i<MoMdepth; i++)
|
||||
{
|
||||
if ( (pindex= komodo_chainactive(height - i)) != 0 )
|
||||
memcpy(&tree[i],&pindex->hashMerkleRoot,sizeof(bits256));
|
||||
leaves.push_back(pindex->hashMerkleRoot);
|
||||
else
|
||||
{
|
||||
free(tree);
|
||||
return(zero);
|
||||
}
|
||||
}
|
||||
MoM = iguana_merkle(tree,MoMdepth);
|
||||
free(tree);
|
||||
return(*(uint256 *)&MoM);
|
||||
return BuildMerkleTree(&fMutated, leaves, tree);
|
||||
}
|
||||
|
||||
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;
|
||||
portable_mutex_lock(&KOMODO_CC_mutex);
|
||||
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);
|
||||
if ( (*nump= num) > 0 )
|
||||
{
|
||||
tree = (bits256 *)calloc(sizeof(bits256),num*3);
|
||||
for (i=0; i<num; i++)
|
||||
memcpy(&tree[i],&allMoMs[i].MoM,sizeof(tree[i]));
|
||||
tmp = iguana_merkle(tree,num);
|
||||
memcpy(MoMoMp,&tmp,sizeof(*MoMoMp));
|
||||
leaves.push_back(allMoMs[i].MoM);
|
||||
*MoMoMp = BuildMerkleTree(&fMutated, leaves, tree);
|
||||
}
|
||||
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 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;
|
||||
if ( (sp= komodo_stateptr(symbol,dest)) != 0 )
|
||||
{
|
||||
for (i=sp->NUM_NPOINTS-1; i>=0; i--)
|
||||
{
|
||||
*idx = i;
|
||||
np = &sp->NPOINTS[i];
|
||||
if ( np->MoMdepth != 0 && height > np->notarized_height-(np->MoMdepth&0xffff) && height <= np->notarized_height )
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
147
src/main.cpp
147
src/main.cpp
@@ -10,6 +10,7 @@
|
||||
#include "addrman.h"
|
||||
#include "alert.h"
|
||||
#include "arith_uint256.h"
|
||||
#include "importcoin.h"
|
||||
#include "chainparams.h"
|
||||
#include "checkpoints.h"
|
||||
#include "checkqueue.h"
|
||||
@@ -19,8 +20,10 @@
|
||||
#include "init.h"
|
||||
#include "merkleblock.h"
|
||||
#include "metrics.h"
|
||||
#include "notarisationdb.h"
|
||||
#include "net.h"
|
||||
#include "pow.h"
|
||||
#include "script/interpreter.h"
|
||||
#include "txdb.h"
|
||||
#include "txmempool.h"
|
||||
#include "ui_interface.h"
|
||||
@@ -826,7 +829,10 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs,
|
||||
{
|
||||
if (tx.IsCoinBase())
|
||||
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++)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (tx.IsCoinBase())
|
||||
if (tx.IsCoinBase() || tx.IsCoinImport())
|
||||
return 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");
|
||||
}
|
||||
}
|
||||
|
||||
if (!(tx.IsCoinBase() || tx.vjoinsplit.empty())) {
|
||||
|
||||
if (!(tx.IsMint() || tx.vjoinsplit.empty())) {
|
||||
auto consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus());
|
||||
// Empty output script.
|
||||
CScript scriptCode;
|
||||
@@ -1010,7 +1016,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state,
|
||||
if (!CheckTransactionWithoutProofVerification(tx, state)) {
|
||||
return false;
|
||||
} else {
|
||||
// Ensure that zk-SNARKs verify
|
||||
// Ensure that zk-SNARKs v|| y
|
||||
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
|
||||
if (!joinsplit.Verify(*pzcashParams, verifier, tx.joinSplitPubKey)) {
|
||||
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
|
||||
// `vjoinsplit` is non-empty.
|
||||
// Migrations may also have empty `vin`
|
||||
if (tx.vin.empty() && tx.vjoinsplit.empty())
|
||||
return state.DoS(10, error("CheckTransaction(): 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
|
||||
if (tx.vjoinsplit.size() > 0)
|
||||
@@ -1293,7 +1300,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
||||
if (pool.exists(hash))
|
||||
{
|
||||
fprintf(stderr,"already in mempool\n");
|
||||
return false;
|
||||
return state.Invalid(false, REJECT_DUPLICATE, "already in mempool");
|
||||
}
|
||||
|
||||
// Check for conflicts with in-memory transactions
|
||||
@@ -1338,28 +1345,37 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
||||
if (view.HaveCoins(hash))
|
||||
{
|
||||
fprintf(stderr,"view.HaveCoins(hash) error\n");
|
||||
return false;
|
||||
return state.Invalid(false, REJECT_DUPLICATE, "already have coins");
|
||||
}
|
||||
|
||||
// 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 (tx.IsCoinImport())
|
||||
{
|
||||
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)
|
||||
*pfMissingInputs = true;
|
||||
//fprintf(stderr,"missing inputs\n");
|
||||
return false;
|
||||
if (!view.HaveCoins(txin.prevout.hash))
|
||||
{
|
||||
if (pfMissingInputs)
|
||||
*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?
|
||||
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
|
||||
// during reorgs to ensure COINBASE_MATURITY is still met.
|
||||
bool fSpendsCoinbase = false;
|
||||
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
|
||||
const CCoins *coins = view.AccessCoins(txin.prevout.hash);
|
||||
if (coins->IsCoinBase()) {
|
||||
fSpendsCoinbase = true;
|
||||
break;
|
||||
if (!tx.IsCoinImport()) {
|
||||
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
|
||||
const CCoins *coins = view.AccessCoins(txin.prevout.hash);
|
||||
if (coins->IsCoinBase()) {
|
||||
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
|
||||
// invalid blocks, however allowing such transactions into the mempool
|
||||
// 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))
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (!tx.IsCoinBase()) // mark inputs spent
|
||||
if (!tx.IsMint()) // mark inputs spent
|
||||
{
|
||||
txundo.vprevout.reserve(tx.vin.size());
|
||||
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
|
||||
|
||||
// Unorthodox state
|
||||
if (tx.IsCoinImport()) {
|
||||
// add a tombstone for the burnTx
|
||||
AddImportTombstone(tx, inputs, 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()() {
|
||||
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 true;
|
||||
@@ -2122,7 +2149,7 @@ bool ContextualCheckInputs(
|
||||
uint32_t consensusBranchId,
|
||||
std::vector<CScriptCheck> *pvChecks)
|
||||
{
|
||||
if (!tx.IsCoinBase())
|
||||
if (!tx.IsMint())
|
||||
{
|
||||
if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs), consensusParams)) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -2325,6 +2358,37 @@ static bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const CO
|
||||
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)
|
||||
{
|
||||
assert(pindex->GetBlockHash() == view.GetBestBlock());
|
||||
@@ -2409,7 +2473,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
|
||||
}
|
||||
|
||||
// restore inputs
|
||||
if (i > 0) { // not coinbases
|
||||
if (!tx.IsMint()) {
|
||||
const CTxUndo &txundo = blockUndo.vtxundo[i-1];
|
||||
if (txundo.vprevout.size() != tx.vin.size())
|
||||
return error("DisconnectBlock(): transaction and undo data inconsistent");
|
||||
@@ -2453,8 +2517,12 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (tx.IsCoinImport())
|
||||
{
|
||||
RemoveImportTombstone(tx, view);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// set the old best anchor back
|
||||
view.PopAnchor(blockUndo.old_tree_root);
|
||||
|
||||
@@ -2474,6 +2542,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
|
||||
return AbortNode(state, "Failed to write address unspent index");
|
||||
}
|
||||
}
|
||||
|
||||
return fClean;
|
||||
}
|
||||
|
||||
@@ -2571,6 +2640,7 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int64_t nTimeVerify = 0;
|
||||
static int64_t nTimeConnect = 0;
|
||||
static int64_t nTimeIndex = 0;
|
||||
@@ -2690,7 +2760,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
return state.DoS(100, error("ConnectBlock(): too many sigops"),
|
||||
REJECT_INVALID, "bad-blk-sigops");
|
||||
//fprintf(stderr,"ht.%d vout0 t%u\n",pindex->nHeight,tx.nLockTime);
|
||||
if (!tx.IsCoinBase())
|
||||
if (!tx.IsMint())
|
||||
{
|
||||
if (!view.HaveInputs(tx))
|
||||
{
|
||||
@@ -2807,7 +2877,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
vPos.push_back(std::make_pair(tx.GetHash(), pos));
|
||||
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
|
||||
}
|
||||
|
||||
|
||||
view.PushAnchor(tree);
|
||||
if (!fJustCheck) {
|
||||
pindex->hashAnchorEnd = tree.root();
|
||||
@@ -2876,6 +2946,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
pindex->RaiseValidity(BLOCK_VALID_SCRIPTS);
|
||||
setDirtyBlockIndex.insert(pindex);
|
||||
}
|
||||
|
||||
ConnectNotarisations(block, pindex->nHeight);
|
||||
|
||||
if (fTxIndex)
|
||||
if (!pblocktree->WriteTxIndex(vPos))
|
||||
@@ -3113,6 +3185,7 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) {
|
||||
if (!DisconnectBlock(block, state, pindexDelete, view))
|
||||
return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString());
|
||||
assert(view.Flush());
|
||||
DisconnectNotarisations(block);
|
||||
}
|
||||
LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001);
|
||||
uint256 anchorAfterDisconnect = pcoinsTip->GetBestAnchor();
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "amount.h"
|
||||
#include "base58.h"
|
||||
#include "chainparams.h"
|
||||
#include "importcoin.h"
|
||||
#include "consensus/consensus.h"
|
||||
#include "consensus/upgrades.h"
|
||||
#include "consensus/validation.h"
|
||||
@@ -230,48 +231,55 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn,int32_t gpucount)
|
||||
double dPriority = 0;
|
||||
CAmount nTotalIn = 0;
|
||||
bool fMissingInputs = false;
|
||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
||||
if (tx.IsCoinImport())
|
||||
{
|
||||
// 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;
|
||||
CAmount nValueIn = GetCoinImportValue(tx);
|
||||
nTotalIn += nValueIn;
|
||||
|
||||
int nConf = nHeight - coins->nHeight;
|
||||
|
||||
dPriority += (double)nValueIn * nConf;
|
||||
dPriority += (double)nValueIn * 1000; // flat multiplier
|
||||
} else {
|
||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
||||
{
|
||||
// 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;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
trees:
|
||||
|
||||
A A
|
||||
/ \ / \
|
||||
B C B C
|
||||
/ \ | / \ / \
|
||||
A A
|
||||
/ \ / \
|
||||
B C B C
|
||||
/ \ \ / \ / \
|
||||
D E F D E F F
|
||||
/ \ / \ / \ / \ / \ / \ / \
|
||||
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
|
||||
root.
|
||||
*/
|
||||
|
||||
vMerkleTree.clear();
|
||||
vMerkleTree.reserve(vtx.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)
|
||||
vMerkleTree.push_back(it->GetHash());
|
||||
vMerkleTree.reserve(leaves.size() * 2 + 16); // Safe upper bound for the number of total nodes.
|
||||
for (std::vector<uint256>::const_iterator it(leaves.begin()); it != leaves.end(); ++it)
|
||||
vMerkleTree.push_back(*it);
|
||||
int j = 0;
|
||||
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)
|
||||
{
|
||||
@@ -78,13 +81,20 @@ uint256 CBlock::BuildMerkleTree(bool* fMutated) const
|
||||
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;
|
||||
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);
|
||||
vMerkleBranch.push_back(vMerkleTree[j+i]);
|
||||
@@ -94,6 +104,15 @@ std::vector<uint256> CBlock::GetMerkleBranch(int nIndex) const
|
||||
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)
|
||||
{
|
||||
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
|
||||
* as input to Equihash.
|
||||
|
||||
@@ -452,11 +452,21 @@ public:
|
||||
// Compute modified tx size for priority calculation (optionally given tx size)
|
||||
unsigned int CalculateModifiedSize(unsigned int nTxSize=0) const;
|
||||
|
||||
bool IsMint() const
|
||||
{
|
||||
return IsCoinImport() || IsCoinBase();
|
||||
}
|
||||
|
||||
bool IsCoinBase() const
|
||||
{
|
||||
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)
|
||||
{
|
||||
return a.hash == b.hash;
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
#include "chain.h"
|
||||
#include "chainparams.h"
|
||||
#include "checkpoints.h"
|
||||
#include "crosschain.h"
|
||||
#include "base58.h"
|
||||
#include "consensus/validation.h"
|
||||
#include "cc/betprotocol.h"
|
||||
#include "cc/eval.h"
|
||||
#include "main.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "rpcserver.h"
|
||||
@@ -758,10 +759,6 @@ 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);
|
||||
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_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)
|
||||
{
|
||||
@@ -798,221 +795,6 @@ UniValue kvsearch(const UniValue& params, bool fHelp)
|
||||
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)
|
||||
{
|
||||
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 },
|
||||
{ "paxpending", 0 },
|
||||
{ "notaries", 2 },
|
||||
{ "height_MoM", 1 },
|
||||
{ "MoMoMdata", 3 },
|
||||
{ "allMoMs", 2 },
|
||||
{ "txMoMproof", 1 },
|
||||
{ "minerids", 1 },
|
||||
{ "kvsearch", 1 },
|
||||
{ "kvupdate", 4 },
|
||||
{ "z_importkey", 2 },
|
||||
{ "z_importviewingkey", 2 },
|
||||
{ "z_getpaymentdisclosure", 1},
|
||||
{ "z_getpaymentdisclosure", 2}
|
||||
{ "z_getpaymentdisclosure", 2},
|
||||
|
||||
// crosschain
|
||||
{ "assetchainproof", 1},
|
||||
{ "crosschainproof", 1},
|
||||
{ "getproofroot", 2},
|
||||
{ "height_MoM", 1},
|
||||
{ "calc_MoM", 2},
|
||||
};
|
||||
|
||||
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));
|
||||
}
|
||||
@@ -134,6 +134,9 @@ void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, UniValue&
|
||||
UniValue in(UniValue::VOBJ);
|
||||
if (tx.IsCoinBase())
|
||||
in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
|
||||
else if (tx.IsCoinImport()) {
|
||||
in.push_back(Pair("is_import", "1"));
|
||||
}
|
||||
else {
|
||||
in.push_back(Pair("txid", txin.prevout.hash.GetHex()));
|
||||
in.push_back(Pair("vout", (int64_t)txin.prevout.n));
|
||||
|
||||
@@ -302,15 +302,20 @@ static const CRPCCommand vRPCCommands[] =
|
||||
{ "blockchain", "paxpending", &paxpending, true },
|
||||
{ "blockchain", "paxprices", &paxprices, 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", "kvsearch", &kvsearch, 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", "getblocktemplate", &getblocktemplate, 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_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 calc_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 minerids(const UniValue& params, bool fHelp);
|
||||
extern UniValue kvsearch(const UniValue& params, bool fHelp);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef SCRIPT_CC_H
|
||||
#define SCRIPT_CC_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "pubkey.h"
|
||||
#include "script/script.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);
|
||||
|
||||
|
||||
#endif /* SCRIPT_CC_H */
|
||||
|
||||
@@ -3,17 +3,20 @@
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <cryptoconditions.h>
|
||||
|
||||
#include "interpreter.h"
|
||||
|
||||
#include "consensus/upgrades.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "cc/eval.h"
|
||||
#include "crypto/ripemd160.h"
|
||||
#include "crypto/sha1.h"
|
||||
#include "crypto/sha256.h"
|
||||
#include "pubkey.h"
|
||||
#include "script/script.h"
|
||||
#include "uint256.h"
|
||||
#include "cryptoconditions/include/cryptoconditions.h"
|
||||
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -186,5 +186,4 @@ bool VerifyScript(
|
||||
const BaseSignatureChecker& checker,
|
||||
uint32_t consensusBranchId,
|
||||
ScriptError* serror = NULL);
|
||||
|
||||
#endif // BITCOIN_SCRIPT_INTERPRETER_H
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
#include "tinyformat.h"
|
||||
#include "utilstrencodings.h"
|
||||
#include "script/cc.h"
|
||||
#include "cc/eval.h"
|
||||
#include "cryptoconditions/include/cryptoconditions.h"
|
||||
|
||||
|
||||
namespace {
|
||||
inline std::string ValueString(const std::vector<unsigned char>& vch)
|
||||
{
|
||||
@@ -266,6 +268,17 @@ bool CScript::MayAcceptCryptoCondition() const
|
||||
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
|
||||
{
|
||||
const_iterator pc = begin();
|
||||
|
||||
@@ -570,6 +570,7 @@ public:
|
||||
|
||||
bool IsPayToScriptHash() const;
|
||||
bool IsPayToCryptoCondition() const;
|
||||
bool IsCoinImport() const;
|
||||
bool MayAcceptCryptoCondition() const;
|
||||
|
||||
/** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
#include "key.h"
|
||||
#include "base58.h"
|
||||
#include "chainparams.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "crypto/common.h"
|
||||
#include "testutils.h"
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
assert(init_and_check_sodium() != -1);
|
||||
ECC_Start();
|
||||
ECCVerifyHandle handle; // Inits secp256k1 verify context
|
||||
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);
|
||||
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"
|
||||
|
||||
|
||||
CKey notaryKey;
|
||||
|
||||
std::string pubkey = "0205a8ad0c1dbc515f149af377981aab58b836af008d4d7ab21bd76faf80550b47";
|
||||
std::string secret = "UxFWWxsf1d7w7K5TvAWSkeX4H95XQKwdwGv49DXwWUTzPTTjHBbU";
|
||||
|
||||
|
||||
class CCTest : public ::testing::Test {
|
||||
public:
|
||||
@@ -32,11 +27,6 @@ protected:
|
||||
virtual void SetUp() {
|
||||
// enable CC
|
||||
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;
|
||||
std::vector<uint256> vBranch;
|
||||
vBranch.resize(3);
|
||||
return MoMProof(nIndex, vBranch, EvalMock::NotarisationHash());
|
||||
return {MerkleBranch(nIndex, vBranch), EvalMock::NotarisationHash()};
|
||||
}
|
||||
|
||||
CMutableTransaction ImportPayoutTx()
|
||||
@@ -237,7 +237,7 @@ public:
|
||||
eval.currentHeight = currentHeight;
|
||||
|
||||
MoMProof proof = GetMoMProof();
|
||||
eval.MoM = proof.Exec(DisputeTx(Player2).GetHash());
|
||||
eval.MoM = proof.branch.Exec(DisputeTx(Player2).GetHash());
|
||||
|
||||
EVAL_TEST = &eval;
|
||||
return eval;
|
||||
@@ -585,7 +585,7 @@ TEST_F(TestBet, testImportPayoutMomFail)
|
||||
EvalMock eval = ebet.SetEvalMock(12);
|
||||
|
||||
MoMProof proof = ebet.GetMoMProof();
|
||||
proof.nIndex ^= 1;
|
||||
proof.branch.nIndex ^= 1;
|
||||
CMutableTransaction importTx = ebet.bet.MakeImportPayoutTx(
|
||||
ebet.Payouts(Player2), ebet.DisputeTx(Player2), uint256(), proof);
|
||||
|
||||
|
||||
@@ -15,93 +15,90 @@
|
||||
#include "testutils.h"
|
||||
|
||||
|
||||
extern Eval* EVAL_TEST;
|
||||
extern int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp);
|
||||
|
||||
|
||||
namespace TestEvalNotarisation {
|
||||
|
||||
|
||||
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
|
||||
class EvalMock : public Eval
|
||||
{
|
||||
memcpy(pubkeys, notaries, sizeof(notaries));
|
||||
return nNotaries;
|
||||
}
|
||||
public:
|
||||
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
|
||||
{
|
||||
auto r = txs.find(hash);
|
||||
if (r != txs.end()) {
|
||||
txOut = r->second;
|
||||
if (blocks.count(hash) > 0)
|
||||
hashBlock = hash;
|
||||
return true;
|
||||
int32_t GetNotaries(uint8_t pubkeys[64][33], int32_t height, uint32_t timestamp) const
|
||||
{
|
||||
memcpy(pubkeys, notaries, sizeof(notaries));
|
||||
return nNotaries;
|
||||
}
|
||||
|
||||
bool GetTxUnconfirmed(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock) const
|
||||
{
|
||||
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>
|
||||
void SetEval(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);
|
||||
}
|
||||
// https://kmd.explorer.supernet.org/tx/5b8055d37cff745a404d1ae45e21ffdba62da7b28ed6533c67468d7379b20bae
|
||||
// inputs have been dropped
|
||||
static auto rawNotaryTx = "01000000000290460100000000002321020e46e79a2a8d12b9b5d12c7a91adb4e454edfae43c0a0cb805427d2ac7613fd9ac0000000000000000506a4c4dae8e0f3e6e5de498a072f5967f3c418c4faba5d56ac8ce17f472d029ef3000008f2e0100424f545300050ba773f0bc31da5839fc7cb9bd7b87f3b765ca608e5cf66785a466659b28880500000000000000";
|
||||
CTransaction notaryTx;
|
||||
static bool init = DecodeHexTx(notaryTx, rawNotaryTx);
|
||||
|
||||
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;
|
||||
eval.blocks[notary.GetHash()].nTime = 1522946781;
|
||||
/*
|
||||
TEST(TestEvalNotarisation, testGetNotarisation)
|
||||
{
|
||||
EvalMock eval;
|
||||
CMutableTransaction notary(notaryTx);
|
||||
SetupEval(eval, notary, noop);
|
||||
|
||||
EVAL_TEST = &eval;
|
||||
}
|
||||
|
||||
|
||||
// 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;
|
||||
NotarisationData data;
|
||||
ASSERT_TRUE(eval.GetNotarisationData(notary.GetHash(), data));
|
||||
EXPECT_EQ(data.height, 77455);
|
||||
EXPECT_EQ(data.blockHash.GetHex(), "000030ef29d072f417cec86ad5a5ab4f8c413c7f96f572a098e45d6e3e0f8eae");
|
||||
@@ -111,7 +108,7 @@ TEST(TestEvalNotarisation, testGetNotarisation)
|
||||
|
||||
MoMProof 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;
|
||||
CMutableTransaction notary(notaryTx);
|
||||
SetEval(eval, notary, noop);
|
||||
SetupEval(eval, notary, noop);
|
||||
|
||||
memset(eval.notaries[10], 0, 33);
|
||||
|
||||
NotarisationData data;
|
||||
ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data));
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
TEST(TestEvalNotarisation, testInvalidNotarisationBadOpReturn)
|
||||
@@ -134,9 +132,9 @@ TEST(TestEvalNotarisation, testInvalidNotarisationBadOpReturn)
|
||||
CMutableTransaction notary(notaryTx);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -146,11 +144,11 @@ TEST(TestEvalNotarisation, testInvalidNotarisationTxNotEnoughSigs)
|
||||
EvalMock eval;
|
||||
CMutableTransaction notary(notaryTx);
|
||||
|
||||
SetEval(eval, notary, [](CMutableTransaction &tx) {
|
||||
SetupEval(eval, notary, [](CMutableTransaction &tx) {
|
||||
tx.vin.resize(10);
|
||||
});
|
||||
|
||||
NotarisationData data;
|
||||
NotarisationData data(0);
|
||||
ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data));
|
||||
}
|
||||
|
||||
@@ -160,9 +158,9 @@ TEST(TestEvalNotarisation, testInvalidNotarisationTxDoesntExist)
|
||||
EvalMock eval;
|
||||
CMutableTransaction notary(notaryTx);
|
||||
|
||||
SetEval(eval, notary, noop);
|
||||
SetupEval(eval, notary, noop);
|
||||
|
||||
NotarisationData data;
|
||||
NotarisationData data(0);
|
||||
ASSERT_FALSE(eval.GetNotarisationData(uint256(), data));
|
||||
}
|
||||
|
||||
@@ -172,11 +170,11 @@ TEST(TestEvalNotarisation, testInvalidNotarisationDupeNotary)
|
||||
EvalMock eval;
|
||||
CMutableTransaction notary(notaryTx);
|
||||
|
||||
SetEval(eval, notary, [](CMutableTransaction &tx) {
|
||||
SetupEval(eval, notary, [](CMutableTransaction &tx) {
|
||||
tx.vin[1] = tx.vin[3];
|
||||
});
|
||||
|
||||
NotarisationData data;
|
||||
NotarisationData data(0);
|
||||
ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data));
|
||||
}
|
||||
|
||||
@@ -186,7 +184,7 @@ TEST(TestEvalNotarisation, testInvalidNotarisationInputNotCheckSig)
|
||||
EvalMock eval;
|
||||
CMutableTransaction notary(notaryTx);
|
||||
|
||||
SetEval(eval, notary, [&](CMutableTransaction &tx) {
|
||||
SetupEval(eval, notary, [&](CMutableTransaction &tx) {
|
||||
int i = 1;
|
||||
CMutableTransaction txIn;
|
||||
txIn.vout.resize(1);
|
||||
@@ -195,7 +193,7 @@ TEST(TestEvalNotarisation, testInvalidNotarisationInputNotCheckSig)
|
||||
eval.txs[txIn.GetHash()] = CTransaction(txIn);
|
||||
});
|
||||
|
||||
NotarisationData data;
|
||||
NotarisationData data(0);
|
||||
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
|
||||
#define TESTUTILS_H
|
||||
|
||||
#include "script/cc.h"
|
||||
#include "main.h"
|
||||
|
||||
|
||||
#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;
|
||||
|
||||
|
||||
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 */
|
||||
|
||||
@@ -104,8 +104,10 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
|
||||
LOCK(cs);
|
||||
mapTx.insert(entry);
|
||||
const CTransaction& tx = mapTx.find(hash)->GetTx();
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i);
|
||||
if (!tx.IsCoinImport()) {
|
||||
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 uint256 &nf, joinsplit.nullifiers) {
|
||||
mapNullifiers[nf] = &tx;
|
||||
|
||||
Reference in New Issue
Block a user