wip
This commit is contained in:
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,8 +256,7 @@ libbitcoin_server_a_SOURCES = \
|
||||
asyncrpcqueue.cpp \
|
||||
bloom.cpp \
|
||||
cc/eval.cpp \
|
||||
cc/importpayout.cpp \
|
||||
cc/disputepayout.cpp \
|
||||
cc/importcoin.cpp \
|
||||
cc/betprotocol.cpp \
|
||||
chain.cpp \
|
||||
checkpoints.cpp \
|
||||
@@ -605,7 +604,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,7 +5,9 @@ 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
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include <cryptoconditions.h>
|
||||
|
||||
#include "hash.h"
|
||||
#include "main.h"
|
||||
#include "chain.h"
|
||||
#include "streams.h"
|
||||
#include "script/cc.h"
|
||||
#include "cc/eval.h"
|
||||
@@ -137,3 +140,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;
|
||||
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");
|
||||
}
|
||||
@@ -49,6 +49,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");
|
||||
}
|
||||
|
||||
@@ -143,6 +147,12 @@ bool Eval::CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t t
|
||||
}
|
||||
|
||||
|
||||
uint32_t Eval::GetCurrentLedgerID() const
|
||||
{
|
||||
return -1; // TODO
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get MoM from a notarisation tx hash
|
||||
*/
|
||||
@@ -157,6 +167,11 @@ bool Eval::GetNotarisationData(const uint256 notaryHash, NotarisationData &data)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Eval::GetNotarisationData(int notarisationHeight, NotarisationData &data, bool verifyCanonical) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Notarisation data, ie, OP_RETURN payload in notarisation transactions
|
||||
@@ -202,4 +217,26 @@ std::string EvalToStr(EvalCode c)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -20,8 +20,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 +56,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,7 +71,10 @@ 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 GetNotarisationData(int notarisationHeight, NotarisationData &data,
|
||||
bool verifyCanonical) const;
|
||||
virtual bool CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const;
|
||||
virtual uint32_t GetCurrentLedgerID() const;
|
||||
};
|
||||
|
||||
|
||||
@@ -87,7 +97,6 @@ public:
|
||||
evaluate(std::vector<unsigned char> header, std::vector<unsigned char> body) = 0;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Data from notarisation OP_RETURN
|
||||
*/
|
||||
@@ -99,6 +108,8 @@ public:
|
||||
char symbol[64];
|
||||
uint256 MoM;
|
||||
uint32_t MoMDepth;
|
||||
uint256 MoMoM;
|
||||
uint32_t MoMoMDepth;
|
||||
|
||||
bool Parse(CScript scriptPubKey);
|
||||
};
|
||||
@@ -140,4 +151,30 @@ bool DeserializeF(const std::vector<unsigned char> vIn, T f)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Merkle stuff
|
||||
*/
|
||||
uint256 SafeCheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex);
|
||||
|
||||
|
||||
class MerkleBranch
|
||||
{
|
||||
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); }
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif /* CC_EVAL_H */
|
||||
|
||||
147
src/cc/importcoin.cpp
Normal file
147
src/cc/importcoin.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
#include "cc/importcoin.h"
|
||||
#include "hash.h"
|
||||
#include "script/cc.h"
|
||||
#include "primitives/transaction.h"
|
||||
|
||||
|
||||
/*
|
||||
* Generate ImportCoin transaction.
|
||||
*
|
||||
* Contains an empty OP_RETURN as first output; this is critical for preventing a double
|
||||
* import. If it doesn't contain this it's invalid. The empty OP_RETURN will hang around
|
||||
* in the UTXO set and the transaction will be detected as a duplicate.
|
||||
*/
|
||||
CTransaction MakeImportCoinTransaction(const MomoProof proof, const CTransaction burnTx, const std::vector<CTxOut> payouts)
|
||||
{
|
||||
std::vector<uint8_t> payload =
|
||||
E_MARSHAL(ss << EVAL_IMPORTCOIN; ss << proof; ss << burnTx);
|
||||
CMutableTransaction mtx;
|
||||
mtx.vin.resize(1);
|
||||
mtx.vin[0].scriptSig << payload;
|
||||
mtx.vin[0].prevout.n = 10e8;
|
||||
mtx.vout = payouts;
|
||||
return CTransaction(mtx);
|
||||
}
|
||||
|
||||
CTxOut MakeBurnOutput(CAmount value, int targetChain, const std::vector<CTxOut> payouts)
|
||||
{
|
||||
std::vector<uint8_t> opret = E_MARSHAL(ss << VARINT(targetChain); ss << SerializeHash(payouts));
|
||||
return CTxOut(value, CScript() << OP_RETURN << opret);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* CC Eval method for import coin.
|
||||
*
|
||||
* This method has to control *every* parameter of the ImportCoin transaction, so that the legal
|
||||
* importTx for a valid burnTx is 1:1. There can be no two legal importTx transactions for a burnTx
|
||||
* on another chain.
|
||||
*/
|
||||
bool Eval::ImportCoin(const std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn)
|
||||
{
|
||||
if (importTx.vout.size() == 0) return Invalid("no-vouts");
|
||||
|
||||
// params
|
||||
MomoProof proof;
|
||||
CTransaction burnTx;
|
||||
if (!E_UNMARSHAL(params, ss >> proof; ss >> burnTx))
|
||||
return Invalid("invalid-params");
|
||||
|
||||
// Control all aspects of this transaction
|
||||
// It must not be at all malleable
|
||||
if (MakeImportCoinTransaction(proof, burnTx, importTx.vout).GetHash() != importTx.GetHash())
|
||||
return Invalid("non-canonical");
|
||||
|
||||
// burn params
|
||||
uint32_t chain; // todo
|
||||
uint256 payoutsHash;
|
||||
std::vector<uint8_t> burnOpret;
|
||||
if (burnTx.vout.size() == 0) return Invalid("invalid-burn-outputs");
|
||||
GetOpReturnData(burnTx.vout[0].scriptPubKey, burnOpret);
|
||||
if (!E_UNMARSHAL(burnOpret, ss >> VARINT(chain); ss >> payoutsHash))
|
||||
return Invalid("invalid-burn-params");
|
||||
|
||||
// check chain
|
||||
if (chain != GetCurrentLedgerID())
|
||||
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(importTx.vout))
|
||||
return Invalid("wrong-payouts");
|
||||
|
||||
// Check proof confirms existance of burnTx
|
||||
{
|
||||
NotarisationData data;
|
||||
if (!GetNotarisationData(proof.notarisationHeight, data, true))
|
||||
return Invalid("coudnt-load-momom");
|
||||
|
||||
if (data.MoMoM != proof.branch.Exec(burnTx.GetHash()))
|
||||
return Invalid("momom-check-fail");
|
||||
}
|
||||
|
||||
return Valid();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Required by main
|
||||
*/
|
||||
CAmount GetCoinImportValue(const CTransaction &tx)
|
||||
{
|
||||
CScript scriptSig = tx.vin[0].scriptSig;
|
||||
auto pc = scriptSig.begin();
|
||||
opcodetype opcode;
|
||||
std::vector<uint8_t> evalScript;
|
||||
if (!scriptSig.GetOp(pc, opcode, evalScript))
|
||||
return false;
|
||||
if (pc != scriptSig.end())
|
||||
return false;
|
||||
EvalCode code;
|
||||
MomoProof proof;
|
||||
CTransaction burnTx;
|
||||
if (!E_UNMARSHAL(evalScript, ss >> proof; ss >> burnTx))
|
||||
return 0;
|
||||
return burnTx.vout.size() ? burnTx.vout[0].nValue : 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");
|
||||
}
|
||||
33
src/cc/importcoin.h
Normal file
33
src/cc/importcoin.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef CC_IMPORTCOIN_H
|
||||
#define CC_IMPORTCOIN_H
|
||||
|
||||
#include "cc/eval.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "script/interpreter.h"
|
||||
#include <cryptoconditions.h>
|
||||
|
||||
|
||||
class MomoProof
|
||||
{
|
||||
public:
|
||||
MerkleBranch branch;
|
||||
int notarisationHeight;
|
||||
ADD_SERIALIZE_METHODS;
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
READWRITE(branch);
|
||||
READWRITE(notarisationHeight);
|
||||
}
|
||||
};
|
||||
|
||||
CAmount GetCoinImportValue(const CTransaction &tx);
|
||||
|
||||
CTransaction MakeImportCoinTransaction(const MomoProof proof,
|
||||
const CTransaction burnTx, const std::vector<CTxOut> payouts);
|
||||
|
||||
CTxOut MakeBurnOutput(CAmount value, int targetChain, const std::vector<CTxOut> payouts);
|
||||
|
||||
bool VerifyCoinImport(const CScript& scriptSig,
|
||||
TransactionSignatureChecker& checker, CValidationState &state);
|
||||
|
||||
#endif /* CC_IMPORTCOIN_H */
|
||||
@@ -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();
|
||||
}
|
||||
@@ -532,11 +532,19 @@ public:
|
||||
BOOST_STATIC_ASSERT(equihash_parameters_acceptable(N, K));
|
||||
nEquihashN = N;
|
||||
nEquihashK = K;
|
||||
genesis.nTime = 1296688602;
|
||||
genesis.nBits = KOMODO_MINDIFF_NBITS;
|
||||
genesis.nNonce = uint256S("0x0000000000000000000000000000000000000000000000000000000000000021");
|
||||
genesis.nSolution = ParseHex("0f2a976db4c4263da10fd5d38eb1790469cf19bdb4bf93450e09a72fdff17a3454326399");
|
||||
|
||||
|
||||
genesis = CreateGenesisBlock(
|
||||
1296688602,
|
||||
uint256S("0x0000000000000000000000000000000000000000000000000000000000000009"),
|
||||
ParseHex("01936b7db1eb4ac39f151b8704642d0a8bda13ec547d54cd5e43ba142fc6d8877cab07b3"),
|
||||
0x200f0f0f, 4, 0);
|
||||
|
||||
consensus.hashGenesisBlock = genesis.GetHash();
|
||||
|
||||
assert(consensus.hashGenesisBlock == uint256S("0x029f11d80ef9765602235e1bc9727e3eb6ba20839319f761fee920d63401e327"));
|
||||
assert(genesis.hashMerkleRoot == uint256S("0xc4eaa58879081de3c24a7b117ed2b28300e7ec4c4c1dff1d3f1268b7857a4ddb"));
|
||||
|
||||
nDefaultPort = 17779;
|
||||
nPruneAfterHeight = 1000;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "version.h"
|
||||
#include "policy/fees.h"
|
||||
#include "komodo_defs.h"
|
||||
#include "cc/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);
|
||||
@@ -482,6 +486,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)
|
||||
|
||||
@@ -97,6 +97,11 @@ public:
|
||||
nVersion = tx.nVersion;
|
||||
//nLockTime = tx.nLockTime;
|
||||
ClearUnspendable();
|
||||
|
||||
// This must live forever
|
||||
if (tx.IsCoinImport()) {
|
||||
vout.insert(vout.begin(), CTxOut(0, CScript() << OP_NOP << OP_RETURN));
|
||||
}
|
||||
}
|
||||
|
||||
//! construct a CCoins from a CTransaction, at a given height
|
||||
|
||||
91
src/main.cpp
91
src/main.cpp
@@ -10,6 +10,7 @@
|
||||
#include "addrman.h"
|
||||
#include "alert.h"
|
||||
#include "arith_uint256.h"
|
||||
#include "cc/importcoin.h"
|
||||
#include "chainparams.h"
|
||||
#include "checkpoints.h"
|
||||
#include "checkqueue.h"
|
||||
@@ -21,6 +22,7 @@
|
||||
#include "metrics.h"
|
||||
#include "net.h"
|
||||
#include "pow.h"
|
||||
#include "script/interpreter.h"
|
||||
#include "txdb.h"
|
||||
#include "txmempool.h"
|
||||
#include "ui_interface.h"
|
||||
@@ -817,7 +819,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]);
|
||||
@@ -888,7 +893,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;
|
||||
@@ -945,8 +950,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;
|
||||
@@ -1001,7 +1006,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"),
|
||||
@@ -1059,6 +1064,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");
|
||||
@@ -1167,7 +1173,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)
|
||||
@@ -1284,7 +1290,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
|
||||
@@ -1326,31 +1332,36 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
||||
view.SetBackend(viewMemPool);
|
||||
|
||||
// do we already have it?
|
||||
// This is what stops coin import transactions from being double spent; they
|
||||
// grow the UTXO set size slightly
|
||||
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))
|
||||
// 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))
|
||||
@@ -1393,11 +1404,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1478,6 +1491,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");
|
||||
@@ -1967,7 +1981,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) {
|
||||
@@ -2003,7 +2017,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;
|
||||
@@ -2113,7 +2128,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;
|
||||
@@ -2165,7 +2180,13 @@ bool ContextualCheckInputs(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (tx.IsCoinImport())
|
||||
{
|
||||
ServerTransactionSignatureChecker checker(&tx, 0, 0, false, txdata);
|
||||
return VerifyCoinImport(tx.vin[0].scriptSig, checker, state);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2400,7 +2421,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");
|
||||
@@ -2681,7 +2702,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))
|
||||
return state.DoS(100, error("ConnectBlock(): inputs missing/spent"),
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "amount.h"
|
||||
#include "base58.h"
|
||||
#include "chainparams.h"
|
||||
#include "cc/importcoin.h"
|
||||
#include "consensus/consensus.h"
|
||||
#include "consensus/upgrades.h"
|
||||
#include "consensus/validation.h"
|
||||
@@ -224,48 +225,55 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
|
||||
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
|
||||
|
||||
@@ -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 (vin.size() == 1 && vin[0].prevout.hash.IsNull());
|
||||
}
|
||||
|
||||
bool IsCoinBase() const
|
||||
{
|
||||
return (vin.size() == 1 && vin[0].prevout.IsNull());
|
||||
}
|
||||
|
||||
bool IsCoinImport() const
|
||||
{
|
||||
return IsMint() && vin[0].prevout.n == 10e8;
|
||||
}
|
||||
|
||||
friend bool operator==(const CTransaction& a, const CTransaction& b)
|
||||
{
|
||||
return a.hash == b.hash;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "checkpoints.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"
|
||||
@@ -1011,7 +1011,7 @@ UniValue txMoMproof(const UniValue& params, bool fHelp)
|
||||
|
||||
// Encode and return
|
||||
CDataStream ssProof(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssProof << MoMProof(nIndex, branch, notarisationHash);
|
||||
ssProof << std::make_pair(notarisationHash, MerkleBranch(nIndex, branch));
|
||||
return HexStr(ssProof.begin(), ssProof.end());
|
||||
}
|
||||
|
||||
|
||||
@@ -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,7 +1,9 @@
|
||||
#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) {
|
||||
@@ -9,6 +11,11 @@ int main(int argc, char **argv) {
|
||||
ECC_Start();
|
||||
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();
|
||||
}
|
||||
|
||||
206
src/test-komodo/test_coinimport.cpp
Normal file
206
src/test-komodo/test_coinimport.cpp
Normal file
@@ -0,0 +1,206 @@
|
||||
|
||||
#include <cryptoconditions.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "cc/importcoin.h"
|
||||
#include "cc/eval.h"
|
||||
#include "base58.h"
|
||||
#include "core_io.h"
|
||||
#include "key.h"
|
||||
#include "main.h"
|
||||
#include "script/cc.h"
|
||||
#include "primitives/transaction.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;
|
||||
MomoProof proof;
|
||||
uint256 MoMoM;
|
||||
CMutableTransaction importTx;
|
||||
uint32_t chainId = 2;
|
||||
CAmount amount = 100;
|
||||
|
||||
void SetImportTx() {
|
||||
CTxOut burnOutput = MakeBurnOutput(amount, chainId, payouts);
|
||||
burnTx.vout.push_back(burnOutput);
|
||||
MoMoM = burnTx.GetHash(); // TODO: an actual branch
|
||||
importTx = CMutableTransaction(MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts));
|
||||
}
|
||||
|
||||
uint32_t GetCurrentLedgerID() const { return chainId; }
|
||||
|
||||
bool GetNotarisationData(int notarisationHeight, NotarisationData &data, bool verifyCanonical) const
|
||||
{
|
||||
if (MoMoM.IsNull()) return false;
|
||||
data.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 fail 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, testNoVouts)
|
||||
{
|
||||
importTx.vout.resize(0);
|
||||
TestRunCCEval(importTx);
|
||||
EXPECT_EQ("no-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-outputs", state.GetRejectReason());
|
||||
}
|
||||
|
||||
|
||||
TEST_F(TestCoinImport, testInvalidBurnParams)
|
||||
{
|
||||
burnTx.vout[0].scriptPubKey = CScript() << OP_RETURN << E_MARSHAL(ss << VARINT(chainId));
|
||||
MoMoM = burnTx.GetHash(); // TODO: an actual branch
|
||||
CTransaction tx = MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts);
|
||||
TestRunCCEval(tx);
|
||||
EXPECT_EQ("invalid-burn-params", state.GetRejectReason());
|
||||
}
|
||||
|
||||
|
||||
TEST_F(TestCoinImport, testWrongChainId)
|
||||
{
|
||||
chainId = 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[0].nValue = 101;
|
||||
TestRunCCEval(importTx);
|
||||
EXPECT_EQ("payout-too-high", state.GetRejectReason());
|
||||
}
|
||||
|
||||
|
||||
TEST_F(TestCoinImport, testInvalidPayouts)
|
||||
{
|
||||
importTx.vout[0].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());
|
||||
}
|
||||
|
||||
} /* namespace TestCoinImport */
|
||||
@@ -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,7 +15,6 @@
|
||||
#include "testutils.h"
|
||||
|
||||
|
||||
extern Eval* EVAL_TEST;
|
||||
extern int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp);
|
||||
|
||||
|
||||
@@ -61,7 +60,7 @@ static auto noop = [&](CMutableTransaction &mtx){};
|
||||
|
||||
|
||||
template<typename Modifier>
|
||||
void SetEval(EvalMock &eval, CMutableTransaction ¬ary, Modifier modify)
|
||||
void SetupEval(EvalMock &eval, CMutableTransaction ¬ary, Modifier modify)
|
||||
{
|
||||
eval.nNotaries = komodo_notaries(eval.notaries, 780060, 1522946781);
|
||||
|
||||
@@ -80,8 +79,6 @@ void SetEval(EvalMock &eval, CMutableTransaction ¬ary, Modifier modify)
|
||||
eval.txs[notary.GetHash()] = CTransaction(notary);
|
||||
eval.blocks[notary.GetHash()].nHeight = 780060;
|
||||
eval.blocks[notary.GetHash()].nTime = 1522946781;
|
||||
|
||||
EVAL_TEST = &eval;
|
||||
}
|
||||
|
||||
|
||||
@@ -94,12 +91,12 @@ 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);
|
||||
SetupEval(eval, notary, noop);
|
||||
|
||||
NotarisationData data;
|
||||
ASSERT_TRUE(eval.GetNotarisationData(notary.GetHash(), data));
|
||||
@@ -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,7 +132,7 @@ TEST(TestEvalNotarisation, testInvalidNotarisationBadOpReturn)
|
||||
CMutableTransaction notary(notaryTx);
|
||||
|
||||
notary.vout[1].scriptPubKey = CScript() << OP_RETURN << 0;
|
||||
SetEval(eval, notary, noop);
|
||||
SetupEval(eval, notary, noop);
|
||||
|
||||
NotarisationData data;
|
||||
ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data));
|
||||
@@ -146,7 +144,7 @@ TEST(TestEvalNotarisation, testInvalidNotarisationTxNotEnoughSigs)
|
||||
EvalMock eval;
|
||||
CMutableTransaction notary(notaryTx);
|
||||
|
||||
SetEval(eval, notary, [](CMutableTransaction &tx) {
|
||||
SetupEval(eval, notary, [](CMutableTransaction &tx) {
|
||||
tx.vin.resize(10);
|
||||
});
|
||||
|
||||
@@ -160,7 +158,7 @@ TEST(TestEvalNotarisation, testInvalidNotarisationTxDoesntExist)
|
||||
EvalMock eval;
|
||||
CMutableTransaction notary(notaryTx);
|
||||
|
||||
SetEval(eval, notary, noop);
|
||||
SetupEval(eval, notary, noop);
|
||||
|
||||
NotarisationData data;
|
||||
ASSERT_FALSE(eval.GetNotarisationData(uint256(), data));
|
||||
@@ -172,7 +170,7 @@ TEST(TestEvalNotarisation, testInvalidNotarisationDupeNotary)
|
||||
EvalMock eval;
|
||||
CMutableTransaction notary(notaryTx);
|
||||
|
||||
SetEval(eval, notary, [](CMutableTransaction &tx) {
|
||||
SetupEval(eval, notary, [](CMutableTransaction &tx) {
|
||||
tx.vin[1] = tx.vin[3];
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
129
src/test-komodo/testutils.cpp
Normal file
129
src/test-komodo/testutils.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
#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 "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;
|
||||
|
||||
void setupChain()
|
||||
{
|
||||
SelectParams(CBaseChainParams::REGTEST);
|
||||
|
||||
// Settings to get block reward
|
||||
//NOTARY_PUBKEY = _NOTARY_PUBKEY;
|
||||
USE_EXTERNAL_PUBKEY = 1;
|
||||
mapArgs["-mineraddress"] = "bogus";
|
||||
COINBASE_MATURITY = 1;
|
||||
// Global mock time
|
||||
nMockTime = GetTime();
|
||||
|
||||
// Init blockchain
|
||||
ClearDatadirCache();
|
||||
auto pathTemp = GetTempPath() / strprintf("test_komodo_%li_%i", GetTime(), GetRand(100000));
|
||||
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);
|
||||
InitBlockIndex();
|
||||
}
|
||||
|
||||
|
||||
void generateBlock(CBlock *block)
|
||||
{
|
||||
UniValue params;
|
||||
params.setArray();
|
||||
params.push_back(1);
|
||||
uint256 blockId;
|
||||
|
||||
SetMockTime(nMockTime++); // CreateNewBlock can fail if not enough time passes
|
||||
|
||||
try {
|
||||
UniValue out = generate(params, false);
|
||||
blockId.SetHex(out[0].getValStr());
|
||||
} catch (const UniValue& e) {
|
||||
FAIL() << "failed to create block: " << e.write().data();
|
||||
}
|
||||
if (block) ASSERT_TRUE(ReadBlockFromDisk(*block, mapBlockIndex[blockId]));
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
static CMutableTransaction spendTx(const CTransaction &txIn, int nOut=0)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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,16 @@ 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);
|
||||
|
||||
|
||||
#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