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 \
|
asyncrpcqueue.cpp \
|
||||||
bloom.cpp \
|
bloom.cpp \
|
||||||
cc/eval.cpp \
|
cc/eval.cpp \
|
||||||
cc/importpayout.cpp \
|
cc/importcoin.cpp \
|
||||||
cc/disputepayout.cpp \
|
|
||||||
cc/betprotocol.cpp \
|
cc/betprotocol.cpp \
|
||||||
chain.cpp \
|
chain.cpp \
|
||||||
checkpoints.cpp \
|
checkpoints.cpp \
|
||||||
@@ -605,7 +604,7 @@ endif
|
|||||||
|
|
||||||
libzcashconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS)
|
libzcashconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS)
|
||||||
libzcashconsensus_la_LIBADD = $(LIBSECP256K1)
|
libzcashconsensus_la_LIBADD = $(LIBSECP256K1)
|
||||||
libzcashconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL
|
libzcashconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -I$(srcdir)/cryptoconditions/include -DBUILD_BITCOIN_INTERNAL
|
||||||
libzcashconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
libzcashconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ bin_PROGRAMS += komodo-test
|
|||||||
# tool for generating our public parameters
|
# tool for generating our public parameters
|
||||||
komodo_test_SOURCES = \
|
komodo_test_SOURCES = \
|
||||||
test-komodo/main.cpp \
|
test-komodo/main.cpp \
|
||||||
|
test-komodo/testutils.cpp \
|
||||||
test-komodo/test_cryptoconditions.cpp \
|
test-komodo/test_cryptoconditions.cpp \
|
||||||
|
test-komodo/test_coinimport.cpp \
|
||||||
test-komodo/test_eval_bet.cpp \
|
test-komodo/test_eval_bet.cpp \
|
||||||
test-komodo/test_eval_notarisation.cpp
|
test-komodo/test_eval_notarisation.cpp
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#include <cryptoconditions.h>
|
#include <cryptoconditions.h>
|
||||||
|
|
||||||
|
#include "hash.h"
|
||||||
|
#include "main.h"
|
||||||
|
#include "chain.h"
|
||||||
#include "streams.h"
|
#include "streams.h"
|
||||||
#include "script/cc.h"
|
#include "script/cc.h"
|
||||||
#include "cc/eval.h"
|
#include "cc/eval.h"
|
||||||
@@ -137,3 +140,146 @@ bool GetOpReturnHash(CScript script, uint256 &hash)
|
|||||||
hash = uint256(vHash);
|
hash = uint256(vHash);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Crypto-Condition EVAL method that verifies a payout against a transaction
|
||||||
|
* notarised on another chain.
|
||||||
|
*
|
||||||
|
* IN: params - condition params
|
||||||
|
* IN: importTx - Payout transaction on value chain (KMD)
|
||||||
|
* IN: nIn - index of input of stake
|
||||||
|
*
|
||||||
|
* importTx: Spends stakeTx with payouts from asset chain
|
||||||
|
*
|
||||||
|
* in 0: Spends Stake TX and contains ImportPayout CC
|
||||||
|
* out 0: OP_RETURN MomProof, disputeTx
|
||||||
|
* out 1-: arbitrary payouts
|
||||||
|
*
|
||||||
|
* disputeTx: Spends sessionTx.0 (opener on asset chain)
|
||||||
|
*
|
||||||
|
* in 0: spends sessionTx.0
|
||||||
|
* in 1-: anything
|
||||||
|
* out 0: OP_RETURN hash of payouts
|
||||||
|
* out 1-: anything
|
||||||
|
*/
|
||||||
|
bool Eval::ImportPayout(const std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn)
|
||||||
|
{
|
||||||
|
if (importTx.vout.size() == 0) return Invalid("no-vouts");
|
||||||
|
|
||||||
|
// load data from vout[0]
|
||||||
|
MoMProof proof;
|
||||||
|
CTransaction disputeTx;
|
||||||
|
{
|
||||||
|
std::vector<unsigned char> vopret;
|
||||||
|
GetOpReturnData(importTx.vout[0].scriptPubKey, vopret);
|
||||||
|
if (!E_UNMARSHAL(vopret, ss >> proof; ss >> disputeTx))
|
||||||
|
return Invalid("invalid-payload");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check disputeTx.0 shows correct payouts
|
||||||
|
{
|
||||||
|
uint256 givenPayoutsHash;
|
||||||
|
GetOpReturnHash(disputeTx.vout[0].scriptPubKey, givenPayoutsHash);
|
||||||
|
std::vector<CTxOut> payouts(importTx.vout.begin() + 1, importTx.vout.end());
|
||||||
|
if (givenPayoutsHash != SerializeHash(payouts))
|
||||||
|
return Invalid("wrong-payouts");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check disputeTx spends sessionTx.0
|
||||||
|
// condition ImportPayout params is session ID from other chain
|
||||||
|
{
|
||||||
|
uint256 sessionHash;
|
||||||
|
if (!E_UNMARSHAL(params, ss >> sessionHash))
|
||||||
|
return Invalid("malformed-params");
|
||||||
|
if (disputeTx.vin[0].prevout != COutPoint(sessionHash, 0))
|
||||||
|
return Invalid("wrong-session");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check disputeTx solves momproof from vout[0]
|
||||||
|
{
|
||||||
|
NotarisationData data;
|
||||||
|
if (!GetNotarisationData(proof.notarisationHash, data))
|
||||||
|
return Invalid("coudnt-load-mom");
|
||||||
|
|
||||||
|
if (data.MoM != proof.branch.Exec(disputeTx.GetHash()))
|
||||||
|
return Invalid("mom-check-fail");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Crypto-Condition EVAL method that resolves a dispute of a session
|
||||||
|
*
|
||||||
|
* IN: vm - AppVM virtual machine to verify states
|
||||||
|
* IN: params - condition params
|
||||||
|
* IN: disputeTx - transaction attempting to resolve dispute
|
||||||
|
* IN: nIn - index of input of dispute tx
|
||||||
|
*
|
||||||
|
* disputeTx: attempt to resolve a dispute
|
||||||
|
*
|
||||||
|
* in 0: Spends Session TX first output, reveals DisputeHeader
|
||||||
|
* out 0: OP_RETURN hash of payouts
|
||||||
|
*/
|
||||||
|
bool Eval::DisputePayout(AppVM &vm, std::vector<uint8_t> params, const CTransaction &disputeTx, unsigned int nIn)
|
||||||
|
{
|
||||||
|
if (disputeTx.vout.size() == 0) return Invalid("no-vouts");
|
||||||
|
|
||||||
|
// get payouts hash
|
||||||
|
uint256 payoutHash;
|
||||||
|
if (!GetOpReturnHash(disputeTx.vout[0].scriptPubKey, payoutHash))
|
||||||
|
return Invalid("invalid-payout-hash");
|
||||||
|
|
||||||
|
// load params
|
||||||
|
uint16_t waitBlocks;
|
||||||
|
std::vector<uint8_t> vmParams;
|
||||||
|
if (!E_UNMARSHAL(params, ss >> VARINT(waitBlocks); ss >> vmParams))
|
||||||
|
return Invalid("malformed-params");
|
||||||
|
|
||||||
|
// ensure that enough time has passed
|
||||||
|
{
|
||||||
|
CTransaction sessionTx;
|
||||||
|
CBlockIndex sessionBlock;
|
||||||
|
|
||||||
|
// if unconformed its too soon
|
||||||
|
if (!GetTxConfirmed(disputeTx.vin[0].prevout.hash, sessionTx, sessionBlock))
|
||||||
|
return Error("couldnt-get-parent");
|
||||||
|
|
||||||
|
if (GetCurrentHeight() < sessionBlock.nHeight + waitBlocks)
|
||||||
|
return Invalid("dispute-too-soon"); // Not yet
|
||||||
|
}
|
||||||
|
|
||||||
|
// get spends
|
||||||
|
std::vector<CTransaction> spends;
|
||||||
|
if (!GetSpendsConfirmed(disputeTx.vin[0].prevout.hash, spends))
|
||||||
|
return Error("couldnt-get-spends");
|
||||||
|
|
||||||
|
// verify result from VM
|
||||||
|
int maxLength = -1;
|
||||||
|
uint256 bestPayout;
|
||||||
|
for (int i=1; i<spends.size(); i++)
|
||||||
|
{
|
||||||
|
std::vector<unsigned char> vmState;
|
||||||
|
if (spends[i].vout.size() == 0) continue;
|
||||||
|
if (!GetOpReturnData(spends[i].vout[0].scriptPubKey, vmState)) continue;
|
||||||
|
auto out = vm.evaluate(vmParams, vmState);
|
||||||
|
uint256 resultHash = SerializeHash(out.second);
|
||||||
|
if (out.first > maxLength) {
|
||||||
|
maxLength = out.first;
|
||||||
|
bestPayout = resultHash;
|
||||||
|
}
|
||||||
|
// The below means that if for any reason there is a draw, the first dispute wins
|
||||||
|
else if (out.first == maxLength) {
|
||||||
|
if (bestPayout != payoutHash) {
|
||||||
|
fprintf(stderr, "WARNING: VM has multiple solutions of same length\n");
|
||||||
|
bestPayout = resultHash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxLength == -1) return Invalid("no-evidence");
|
||||||
|
|
||||||
|
return bestPayout == payoutHash ? Valid() : Invalid("wrong-payout");
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include "cc/eval.h"
|
#include "cc/eval.h"
|
||||||
#include "pubkey.h"
|
#include "pubkey.h"
|
||||||
#include "primitives/block.h"
|
|
||||||
#include "primitives/transaction.h"
|
#include "primitives/transaction.h"
|
||||||
#include "cryptoconditions/include/cryptoconditions.h"
|
#include "cryptoconditions/include/cryptoconditions.h"
|
||||||
|
|
||||||
@@ -11,25 +10,18 @@
|
|||||||
class MoMProof
|
class MoMProof
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
int nIndex;
|
MerkleBranch branch;
|
||||||
std::vector<uint256> branch;
|
|
||||||
uint256 notarisationHash;
|
uint256 notarisationHash;
|
||||||
|
|
||||||
MoMProof() {}
|
|
||||||
MoMProof(int i, std::vector<uint256> b, uint256 n) : notarisationHash(n), nIndex(i), branch(b) {}
|
|
||||||
uint256 Exec(uint256 hash) const { return CBlock::CheckMerkleBranch(hash, branch, nIndex); }
|
|
||||||
|
|
||||||
ADD_SERIALIZE_METHODS;
|
ADD_SERIALIZE_METHODS;
|
||||||
|
|
||||||
template <typename Stream, typename Operation>
|
template <typename Stream, typename Operation>
|
||||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||||
READWRITE(VARINT(nIndex));
|
|
||||||
READWRITE(branch);
|
READWRITE(branch);
|
||||||
READWRITE(notarisationHash);
|
READWRITE(notarisationHash);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BetProtocol
|
class BetProtocol
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
#include <cryptoconditions.h>
|
|
||||||
|
|
||||||
#include "hash.h"
|
|
||||||
#include "chain.h"
|
|
||||||
#include "version.h"
|
|
||||||
#include "script/cc.h"
|
|
||||||
#include "cc/eval.h"
|
|
||||||
#include "cc/betprotocol.h"
|
|
||||||
#include "primitives/transaction.h"
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Crypto-Condition EVAL method that resolves a dispute of a session
|
|
||||||
*
|
|
||||||
* IN: vm - AppVM virtual machine to verify states
|
|
||||||
* IN: params - condition params
|
|
||||||
* IN: disputeTx - transaction attempting to resolve dispute
|
|
||||||
* IN: nIn - index of input of dispute tx
|
|
||||||
*
|
|
||||||
* disputeTx: attempt to resolve a dispute
|
|
||||||
*
|
|
||||||
* in 0: Spends Session TX first output, reveals DisputeHeader
|
|
||||||
* out 0: OP_RETURN hash of payouts
|
|
||||||
*/
|
|
||||||
bool Eval::DisputePayout(AppVM &vm, std::vector<uint8_t> params, const CTransaction &disputeTx, unsigned int nIn)
|
|
||||||
{
|
|
||||||
if (disputeTx.vout.size() == 0) return Invalid("no-vouts");
|
|
||||||
|
|
||||||
// get payouts hash
|
|
||||||
uint256 payoutHash;
|
|
||||||
if (!GetOpReturnHash(disputeTx.vout[0].scriptPubKey, payoutHash))
|
|
||||||
return Invalid("invalid-payout-hash");
|
|
||||||
|
|
||||||
// load params
|
|
||||||
uint16_t waitBlocks;
|
|
||||||
std::vector<uint8_t> vmParams;
|
|
||||||
if (!E_UNMARSHAL(params, ss >> VARINT(waitBlocks); ss >> vmParams))
|
|
||||||
return Invalid("malformed-params");
|
|
||||||
|
|
||||||
// ensure that enough time has passed
|
|
||||||
{
|
|
||||||
CTransaction sessionTx;
|
|
||||||
CBlockIndex sessionBlock;
|
|
||||||
|
|
||||||
// if unconformed its too soon
|
|
||||||
if (!GetTxConfirmed(disputeTx.vin[0].prevout.hash, sessionTx, sessionBlock))
|
|
||||||
return Error("couldnt-get-parent");
|
|
||||||
|
|
||||||
if (GetCurrentHeight() < sessionBlock.nHeight + waitBlocks)
|
|
||||||
return Invalid("dispute-too-soon"); // Not yet
|
|
||||||
}
|
|
||||||
|
|
||||||
// get spends
|
|
||||||
std::vector<CTransaction> spends;
|
|
||||||
if (!GetSpendsConfirmed(disputeTx.vin[0].prevout.hash, spends))
|
|
||||||
return Error("couldnt-get-spends");
|
|
||||||
|
|
||||||
// verify result from VM
|
|
||||||
int maxLength = -1;
|
|
||||||
uint256 bestPayout;
|
|
||||||
for (int i=1; i<spends.size(); i++)
|
|
||||||
{
|
|
||||||
std::vector<unsigned char> vmState;
|
|
||||||
if (spends[i].vout.size() == 0) continue;
|
|
||||||
if (!GetOpReturnData(spends[i].vout[0].scriptPubKey, vmState)) continue;
|
|
||||||
auto out = vm.evaluate(vmParams, vmState);
|
|
||||||
uint256 resultHash = SerializeHash(out.second);
|
|
||||||
if (out.first > maxLength) {
|
|
||||||
maxLength = out.first;
|
|
||||||
bestPayout = resultHash;
|
|
||||||
}
|
|
||||||
// The below means that if for any reason there is a draw, the first dispute wins
|
|
||||||
else if (out.first == maxLength) {
|
|
||||||
if (bestPayout != payoutHash) {
|
|
||||||
fprintf(stderr, "WARNING: VM has multiple solutions of same length\n");
|
|
||||||
bestPayout = resultHash;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maxLength == -1) return Invalid("no-evidence");
|
|
||||||
|
|
||||||
return bestPayout == payoutHash ? Valid() : Invalid("wrong-payout");
|
|
||||||
}
|
|
||||||
@@ -49,6 +49,10 @@ bool Eval::Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn)
|
|||||||
return ImportPayout(vparams, txTo, nIn);
|
return ImportPayout(vparams, txTo, nIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ecode == EVAL_IMPORTCOIN) {
|
||||||
|
return ImportCoin(vparams, txTo, nIn);
|
||||||
|
}
|
||||||
|
|
||||||
return Invalid("invalid-code");
|
return Invalid("invalid-code");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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
|
* Get MoM from a notarisation tx hash
|
||||||
*/
|
*/
|
||||||
@@ -157,6 +167,11 @@ bool Eval::GetNotarisationData(const uint256 notaryHash, NotarisationData &data)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Eval::GetNotarisationData(int notarisationHeight, NotarisationData &data, bool verifyCanonical) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Notarisation data, ie, OP_RETURN payload in notarisation transactions
|
* Notarisation data, ie, OP_RETURN payload in notarisation transactions
|
||||||
@@ -202,4 +217,26 @@ std::string EvalToStr(EvalCode c)
|
|||||||
char s[10];
|
char s[10];
|
||||||
sprintf(s, "0x%x", c);
|
sprintf(s, "0x%x", c);
|
||||||
return std::string(s);
|
return std::string(s);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint256 SafeCheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex)
|
||||||
|
{
|
||||||
|
if (nIndex == -1)
|
||||||
|
return uint256();
|
||||||
|
for (auto it(vMerkleBranch.begin()); it != vMerkleBranch.end(); ++it)
|
||||||
|
{
|
||||||
|
if (nIndex & 1) {
|
||||||
|
if (*it == hash) {
|
||||||
|
// non canonical. hash may be equal to node but never on the right.
|
||||||
|
return uint256();
|
||||||
|
}
|
||||||
|
hash = Hash(BEGIN(*it), END(*it), BEGIN(hash), END(hash));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
hash = Hash(BEGIN(hash), END(hash), BEGIN(*it), END(*it));
|
||||||
|
nIndex >>= 1;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,10 @@
|
|||||||
* a possible code is EVAL_BITCOIN_SCRIPT, where the entire binary
|
* a possible code is EVAL_BITCOIN_SCRIPT, where the entire binary
|
||||||
* after the code is interpreted as a bitcoin script.
|
* after the code is interpreted as a bitcoin script.
|
||||||
*/
|
*/
|
||||||
#define FOREACH_EVAL(EVAL) \
|
#define FOREACH_EVAL(EVAL) \
|
||||||
EVAL(EVAL_IMPORTPAYOUT, 0xe1)
|
EVAL(EVAL_IMPORTPAYOUT, 0xe1) \
|
||||||
|
EVAL(EVAL_IMPORTCOIN, 0xe2)
|
||||||
|
|
||||||
|
|
||||||
typedef uint8_t EvalCode;
|
typedef uint8_t EvalCode;
|
||||||
|
|
||||||
@@ -54,6 +56,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool ImportPayout(std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn);
|
bool ImportPayout(std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Import coin from another chain with same symbol
|
||||||
|
*/
|
||||||
|
bool ImportCoin(std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* IO functions
|
* IO functions
|
||||||
*/
|
*/
|
||||||
@@ -64,7 +71,10 @@ public:
|
|||||||
virtual bool GetBlock(uint256 hash, CBlockIndex& blockIdx) const;
|
virtual bool GetBlock(uint256 hash, CBlockIndex& blockIdx) const;
|
||||||
virtual int32_t GetNotaries(uint8_t pubkeys[64][33], int32_t height, uint32_t timestamp) const;
|
virtual int32_t GetNotaries(uint8_t pubkeys[64][33], int32_t height, uint32_t timestamp) const;
|
||||||
virtual bool GetNotarisationData(uint256 notarisationHash, NotarisationData &data) const;
|
virtual bool GetNotarisationData(uint256 notarisationHash, NotarisationData &data) const;
|
||||||
|
virtual bool GetNotarisationData(int notarisationHeight, NotarisationData &data,
|
||||||
|
bool verifyCanonical) const;
|
||||||
virtual bool CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const;
|
virtual bool CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const;
|
||||||
|
virtual uint32_t GetCurrentLedgerID() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -87,7 +97,6 @@ public:
|
|||||||
evaluate(std::vector<unsigned char> header, std::vector<unsigned char> body) = 0;
|
evaluate(std::vector<unsigned char> header, std::vector<unsigned char> body) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Data from notarisation OP_RETURN
|
* Data from notarisation OP_RETURN
|
||||||
*/
|
*/
|
||||||
@@ -99,6 +108,8 @@ public:
|
|||||||
char symbol[64];
|
char symbol[64];
|
||||||
uint256 MoM;
|
uint256 MoM;
|
||||||
uint32_t MoMDepth;
|
uint32_t MoMDepth;
|
||||||
|
uint256 MoMoM;
|
||||||
|
uint32_t MoMoMDepth;
|
||||||
|
|
||||||
bool Parse(CScript scriptPubKey);
|
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 */
|
#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));
|
BOOST_STATIC_ASSERT(equihash_parameters_acceptable(N, K));
|
||||||
nEquihashN = N;
|
nEquihashN = N;
|
||||||
nEquihashK = K;
|
nEquihashK = K;
|
||||||
genesis.nTime = 1296688602;
|
|
||||||
genesis.nBits = KOMODO_MINDIFF_NBITS;
|
|
||||||
genesis.nNonce = uint256S("0x0000000000000000000000000000000000000000000000000000000000000021");
|
genesis = CreateGenesisBlock(
|
||||||
genesis.nSolution = ParseHex("0f2a976db4c4263da10fd5d38eb1790469cf19bdb4bf93450e09a72fdff17a3454326399");
|
1296688602,
|
||||||
|
uint256S("0x0000000000000000000000000000000000000000000000000000000000000009"),
|
||||||
|
ParseHex("01936b7db1eb4ac39f151b8704642d0a8bda13ec547d54cd5e43ba142fc6d8877cab07b3"),
|
||||||
|
0x200f0f0f, 4, 0);
|
||||||
|
|
||||||
consensus.hashGenesisBlock = genesis.GetHash();
|
consensus.hashGenesisBlock = genesis.GetHash();
|
||||||
|
|
||||||
|
assert(consensus.hashGenesisBlock == uint256S("0x029f11d80ef9765602235e1bc9727e3eb6ba20839319f761fee920d63401e327"));
|
||||||
|
assert(genesis.hashMerkleRoot == uint256S("0xc4eaa58879081de3c24a7b117ed2b28300e7ec4c4c1dff1d3f1268b7857a4ddb"));
|
||||||
|
|
||||||
nDefaultPort = 17779;
|
nDefaultPort = 17779;
|
||||||
nPruneAfterHeight = 1000;
|
nPruneAfterHeight = 1000;
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "policy/fees.h"
|
#include "policy/fees.h"
|
||||||
#include "komodo_defs.h"
|
#include "komodo_defs.h"
|
||||||
|
#include "cc/importcoin.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
@@ -392,11 +393,13 @@ extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN];
|
|||||||
|
|
||||||
CAmount CCoinsViewCache::GetValueIn(int32_t nHeight,int64_t *interestp,const CTransaction& tx,uint32_t tiptime) const
|
CAmount CCoinsViewCache::GetValueIn(int32_t nHeight,int64_t *interestp,const CTransaction& tx,uint32_t tiptime) const
|
||||||
{
|
{
|
||||||
|
CAmount value,nResult = 0;
|
||||||
if ( interestp != 0 )
|
if ( interestp != 0 )
|
||||||
*interestp = 0;
|
*interestp = 0;
|
||||||
|
if ( tx.IsCoinImport() )
|
||||||
|
return GetCoinImportValue(tx);
|
||||||
if ( tx.IsCoinBase() != 0 )
|
if ( tx.IsCoinBase() != 0 )
|
||||||
return 0;
|
return 0;
|
||||||
CAmount value,nResult = 0;
|
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||||
{
|
{
|
||||||
value = GetOutputFor(tx.vin[i]).nValue;
|
value = GetOutputFor(tx.vin[i]).nValue;
|
||||||
@@ -421,6 +424,7 @@ CAmount CCoinsViewCache::GetValueIn(int32_t nHeight,int64_t *interestp,const CTr
|
|||||||
return nResult;
|
return nResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const
|
bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const
|
||||||
{
|
{
|
||||||
boost::unordered_map<uint256, ZCIncrementalMerkleTree, CCoinsKeyHasher> intermediates;
|
boost::unordered_map<uint256, ZCIncrementalMerkleTree, CCoinsKeyHasher> intermediates;
|
||||||
@@ -457,7 +461,7 @@ bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const
|
|||||||
|
|
||||||
bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
|
bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
|
||||||
{
|
{
|
||||||
if (!tx.IsCoinBase()) {
|
if (!tx.IsMint()) {
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
||||||
const COutPoint &prevout = tx.vin[i].prevout;
|
const COutPoint &prevout = tx.vin[i].prevout;
|
||||||
const CCoins* coins = AccessCoins(prevout.hash);
|
const CCoins* coins = AccessCoins(prevout.hash);
|
||||||
@@ -482,6 +486,9 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const
|
|||||||
if (tx.vjoinsplit.size() > 0) {
|
if (tx.vjoinsplit.size() > 0) {
|
||||||
return MAX_PRIORITY;
|
return MAX_PRIORITY;
|
||||||
}
|
}
|
||||||
|
if (tx.IsCoinImport()) {
|
||||||
|
return MAX_PRIORITY;
|
||||||
|
}
|
||||||
|
|
||||||
double dResult = 0.0;
|
double dResult = 0.0;
|
||||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
||||||
|
|||||||
@@ -97,6 +97,11 @@ public:
|
|||||||
nVersion = tx.nVersion;
|
nVersion = tx.nVersion;
|
||||||
//nLockTime = tx.nLockTime;
|
//nLockTime = tx.nLockTime;
|
||||||
ClearUnspendable();
|
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
|
//! construct a CCoins from a CTransaction, at a given height
|
||||||
|
|||||||
83
src/main.cpp
83
src/main.cpp
@@ -10,6 +10,7 @@
|
|||||||
#include "addrman.h"
|
#include "addrman.h"
|
||||||
#include "alert.h"
|
#include "alert.h"
|
||||||
#include "arith_uint256.h"
|
#include "arith_uint256.h"
|
||||||
|
#include "cc/importcoin.h"
|
||||||
#include "chainparams.h"
|
#include "chainparams.h"
|
||||||
#include "checkpoints.h"
|
#include "checkpoints.h"
|
||||||
#include "checkqueue.h"
|
#include "checkqueue.h"
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
#include "metrics.h"
|
#include "metrics.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
#include "pow.h"
|
#include "pow.h"
|
||||||
|
#include "script/interpreter.h"
|
||||||
#include "txdb.h"
|
#include "txdb.h"
|
||||||
#include "txmempool.h"
|
#include "txmempool.h"
|
||||||
#include "ui_interface.h"
|
#include "ui_interface.h"
|
||||||
@@ -818,6 +820,9 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs,
|
|||||||
if (tx.IsCoinBase())
|
if (tx.IsCoinBase())
|
||||||
return true; // Coinbases don't use vin normally
|
return true; // Coinbases don't use vin normally
|
||||||
|
|
||||||
|
if (tx.IsCoinImport())
|
||||||
|
return tx.vin[0].scriptSig.IsCoinImport();
|
||||||
|
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||||
{
|
{
|
||||||
const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]);
|
const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]);
|
||||||
@@ -888,7 +893,7 @@ unsigned int GetLegacySigOpCount(const CTransaction& tx)
|
|||||||
|
|
||||||
unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& inputs)
|
unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& inputs)
|
||||||
{
|
{
|
||||||
if (tx.IsCoinBase())
|
if (tx.IsCoinBase() || tx.IsCoinImport())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
unsigned int nSigOps = 0;
|
unsigned int nSigOps = 0;
|
||||||
@@ -946,7 +951,7 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(tx.IsCoinBase() || tx.vjoinsplit.empty())) {
|
if (!(tx.IsMint() || tx.vjoinsplit.empty())) {
|
||||||
auto consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus());
|
auto consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus());
|
||||||
// Empty output script.
|
// Empty output script.
|
||||||
CScript scriptCode;
|
CScript scriptCode;
|
||||||
@@ -1001,7 +1006,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state,
|
|||||||
if (!CheckTransactionWithoutProofVerification(tx, state)) {
|
if (!CheckTransactionWithoutProofVerification(tx, state)) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// Ensure that zk-SNARKs verify
|
// Ensure that zk-SNARKs v|| y
|
||||||
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
|
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
|
||||||
if (!joinsplit.Verify(*pzcashParams, verifier, tx.joinSplitPubKey)) {
|
if (!joinsplit.Verify(*pzcashParams, verifier, tx.joinSplitPubKey)) {
|
||||||
return state.DoS(100, error("CheckTransaction(): joinsplit does not verify"),
|
return state.DoS(100, error("CheckTransaction(): joinsplit does not verify"),
|
||||||
@@ -1059,6 +1064,7 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio
|
|||||||
|
|
||||||
// Transactions can contain empty `vin` and `vout` so long as
|
// Transactions can contain empty `vin` and `vout` so long as
|
||||||
// `vjoinsplit` is non-empty.
|
// `vjoinsplit` is non-empty.
|
||||||
|
// Migrations may also have empty `vin`
|
||||||
if (tx.vin.empty() && tx.vjoinsplit.empty())
|
if (tx.vin.empty() && tx.vjoinsplit.empty())
|
||||||
return state.DoS(10, error("CheckTransaction(): vin empty"),
|
return state.DoS(10, error("CheckTransaction(): vin empty"),
|
||||||
REJECT_INVALID, "bad-txns-vin-empty");
|
REJECT_INVALID, "bad-txns-vin-empty");
|
||||||
@@ -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
|
// There should be no joinsplits in a coinbase transaction
|
||||||
if (tx.vjoinsplit.size() > 0)
|
if (tx.vjoinsplit.size() > 0)
|
||||||
@@ -1284,7 +1290,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||||||
if (pool.exists(hash))
|
if (pool.exists(hash))
|
||||||
{
|
{
|
||||||
fprintf(stderr,"already in mempool\n");
|
fprintf(stderr,"already in mempool\n");
|
||||||
return false;
|
return state.Invalid(false, REJECT_DUPLICATE, "already in mempool");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for conflicts with in-memory transactions
|
// Check for conflicts with in-memory transactions
|
||||||
@@ -1326,31 +1332,36 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||||||
view.SetBackend(viewMemPool);
|
view.SetBackend(viewMemPool);
|
||||||
|
|
||||||
// do we already have it?
|
// 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))
|
if (view.HaveCoins(hash))
|
||||||
{
|
{
|
||||||
fprintf(stderr,"view.HaveCoins(hash) error\n");
|
fprintf(stderr,"view.HaveCoins(hash) error\n");
|
||||||
return false;
|
return state.Invalid(false, REJECT_DUPLICATE, "already have coins");
|
||||||
}
|
}
|
||||||
|
|
||||||
// do all inputs exist?
|
if (!tx.IsCoinImport())
|
||||||
// Note that this does not check for the presence of actual outputs (see the next check for that),
|
|
||||||
// and only helps with filling in pfMissingInputs (to determine missing vs spent).
|
|
||||||
BOOST_FOREACH(const CTxIn txin, tx.vin)
|
|
||||||
{
|
{
|
||||||
if (!view.HaveCoins(txin.prevout.hash))
|
// do all inputs exist?
|
||||||
|
// Note that this does not check for the presence of actual outputs (see the next check for that),
|
||||||
|
// and only helps with filling in pfMissingInputs (to determine missing vs spent).
|
||||||
|
BOOST_FOREACH(const CTxIn txin, tx.vin)
|
||||||
{
|
{
|
||||||
if (pfMissingInputs)
|
if (!view.HaveCoins(txin.prevout.hash))
|
||||||
*pfMissingInputs = true;
|
{
|
||||||
//fprintf(stderr,"missing inputs\n");
|
if (pfMissingInputs)
|
||||||
return false;
|
*pfMissingInputs = true;
|
||||||
|
//fprintf(stderr,"missing inputs\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// are the actual inputs available?
|
// are the actual inputs available?
|
||||||
if (!view.HaveInputs(tx))
|
if (!view.HaveInputs(tx))
|
||||||
{
|
{
|
||||||
//fprintf(stderr,"accept failure.1\n");
|
//fprintf(stderr,"accept failure.1\n");
|
||||||
return state.Invalid(error("AcceptToMemoryPool: inputs already spent"),REJECT_DUPLICATE, "bad-txns-inputs-spent");
|
return state.Invalid(error("AcceptToMemoryPool: inputs already spent"),REJECT_DUPLICATE, "bad-txns-inputs-spent");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// are the joinsplit's requirements met?
|
// are the joinsplit's requirements met?
|
||||||
if (!view.HaveJoinSplitRequirements(tx))
|
if (!view.HaveJoinSplitRequirements(tx))
|
||||||
@@ -1393,11 +1404,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||||||
// Keep track of transactions that spend a coinbase, which we re-scan
|
// Keep track of transactions that spend a coinbase, which we re-scan
|
||||||
// during reorgs to ensure COINBASE_MATURITY is still met.
|
// during reorgs to ensure COINBASE_MATURITY is still met.
|
||||||
bool fSpendsCoinbase = false;
|
bool fSpendsCoinbase = false;
|
||||||
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
|
if (!tx.IsCoinImport()) {
|
||||||
const CCoins *coins = view.AccessCoins(txin.prevout.hash);
|
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
|
||||||
if (coins->IsCoinBase()) {
|
const CCoins *coins = view.AccessCoins(txin.prevout.hash);
|
||||||
fSpendsCoinbase = true;
|
if (coins->IsCoinBase()) {
|
||||||
break;
|
fSpendsCoinbase = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1478,6 +1491,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||||||
// There is a similar check in CreateNewBlock() to prevent creating
|
// There is a similar check in CreateNewBlock() to prevent creating
|
||||||
// invalid blocks, however allowing such transactions into the mempool
|
// invalid blocks, however allowing such transactions into the mempool
|
||||||
// can be exploited as a DoS attack.
|
// can be exploited as a DoS attack.
|
||||||
|
// XXX: is this neccesary for CryptoConditions?
|
||||||
if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId))
|
if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId))
|
||||||
{
|
{
|
||||||
fprintf(stderr,"accept failure.10\n");
|
fprintf(stderr,"accept failure.10\n");
|
||||||
@@ -1967,7 +1981,7 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state
|
|||||||
|
|
||||||
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight)
|
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight)
|
||||||
{
|
{
|
||||||
if (!tx.IsCoinBase()) // mark inputs spent
|
if (!tx.IsMint()) // mark inputs spent
|
||||||
{
|
{
|
||||||
txundo.vprevout.reserve(tx.vin.size());
|
txundo.vprevout.reserve(tx.vin.size());
|
||||||
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
|
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
|
||||||
@@ -2003,7 +2017,8 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
|
|||||||
|
|
||||||
bool CScriptCheck::operator()() {
|
bool CScriptCheck::operator()() {
|
||||||
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
|
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
|
||||||
if (!VerifyScript(scriptSig, scriptPubKey, nFlags, ServerTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), consensusBranchId, &error)) {
|
ServerTransactionSignatureChecker checker(ptxTo, nIn, amount, cacheStore, *txdata);
|
||||||
|
if (!VerifyScript(scriptSig, scriptPubKey, nFlags, checker, consensusBranchId, &error)) {
|
||||||
return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error));
|
return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -2113,7 +2128,7 @@ bool ContextualCheckInputs(
|
|||||||
uint32_t consensusBranchId,
|
uint32_t consensusBranchId,
|
||||||
std::vector<CScriptCheck> *pvChecks)
|
std::vector<CScriptCheck> *pvChecks)
|
||||||
{
|
{
|
||||||
if (!tx.IsCoinBase())
|
if (!tx.IsMint())
|
||||||
{
|
{
|
||||||
if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs), consensusParams)) {
|
if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs), consensusParams)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -2166,6 +2181,12 @@ bool ContextualCheckInputs(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tx.IsCoinImport())
|
||||||
|
{
|
||||||
|
ServerTransactionSignatureChecker checker(&tx, 0, 0, false, txdata);
|
||||||
|
return VerifyCoinImport(tx.vin[0].scriptSig, checker, state);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2400,7 +2421,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
|
|||||||
}
|
}
|
||||||
|
|
||||||
// restore inputs
|
// restore inputs
|
||||||
if (i > 0) { // not coinbases
|
if (!tx.IsMint()) {
|
||||||
const CTxUndo &txundo = blockUndo.vtxundo[i-1];
|
const CTxUndo &txundo = blockUndo.vtxundo[i-1];
|
||||||
if (txundo.vprevout.size() != tx.vin.size())
|
if (txundo.vprevout.size() != tx.vin.size())
|
||||||
return error("DisconnectBlock(): transaction and undo data inconsistent");
|
return error("DisconnectBlock(): transaction and undo data inconsistent");
|
||||||
@@ -2681,7 +2702,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||||||
return state.DoS(100, error("ConnectBlock(): too many sigops"),
|
return state.DoS(100, error("ConnectBlock(): too many sigops"),
|
||||||
REJECT_INVALID, "bad-blk-sigops");
|
REJECT_INVALID, "bad-blk-sigops");
|
||||||
//fprintf(stderr,"ht.%d vout0 t%u\n",pindex->nHeight,tx.nLockTime);
|
//fprintf(stderr,"ht.%d vout0 t%u\n",pindex->nHeight,tx.nLockTime);
|
||||||
if (!tx.IsCoinBase())
|
if (!tx.IsMint())
|
||||||
{
|
{
|
||||||
if (!view.HaveInputs(tx))
|
if (!view.HaveInputs(tx))
|
||||||
return state.DoS(100, error("ConnectBlock(): inputs missing/spent"),
|
return state.DoS(100, error("ConnectBlock(): inputs missing/spent"),
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "amount.h"
|
#include "amount.h"
|
||||||
#include "base58.h"
|
#include "base58.h"
|
||||||
#include "chainparams.h"
|
#include "chainparams.h"
|
||||||
|
#include "cc/importcoin.h"
|
||||||
#include "consensus/consensus.h"
|
#include "consensus/consensus.h"
|
||||||
#include "consensus/upgrades.h"
|
#include "consensus/upgrades.h"
|
||||||
#include "consensus/validation.h"
|
#include "consensus/validation.h"
|
||||||
@@ -224,47 +225,54 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
|
|||||||
double dPriority = 0;
|
double dPriority = 0;
|
||||||
CAmount nTotalIn = 0;
|
CAmount nTotalIn = 0;
|
||||||
bool fMissingInputs = false;
|
bool fMissingInputs = false;
|
||||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
if (tx.IsCoinImport())
|
||||||
{
|
{
|
||||||
// Read prev transaction
|
CAmount nValueIn = GetCoinImportValue(tx);
|
||||||
if (!view.HaveCoins(txin.prevout.hash))
|
|
||||||
{
|
|
||||||
// This should never happen; all transactions in the memory
|
|
||||||
// pool should connect to either transactions in the chain
|
|
||||||
// or other transactions in the memory pool.
|
|
||||||
if (!mempool.mapTx.count(txin.prevout.hash))
|
|
||||||
{
|
|
||||||
LogPrintf("ERROR: mempool transaction missing input\n");
|
|
||||||
if (fDebug) assert("mempool transaction missing input" == 0);
|
|
||||||
fMissingInputs = true;
|
|
||||||
if (porphan)
|
|
||||||
vOrphan.pop_back();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Has to wait for dependencies
|
|
||||||
if (!porphan)
|
|
||||||
{
|
|
||||||
// Use list for automatic deletion
|
|
||||||
vOrphan.push_back(COrphan(&tx));
|
|
||||||
porphan = &vOrphan.back();
|
|
||||||
}
|
|
||||||
mapDependers[txin.prevout.hash].push_back(porphan);
|
|
||||||
porphan->setDependsOn.insert(txin.prevout.hash);
|
|
||||||
nTotalIn += mempool.mapTx.find(txin.prevout.hash)->GetTx().vout[txin.prevout.n].nValue;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
|
|
||||||
assert(coins);
|
|
||||||
|
|
||||||
CAmount nValueIn = coins->vout[txin.prevout.n].nValue;
|
|
||||||
nTotalIn += nValueIn;
|
nTotalIn += nValueIn;
|
||||||
|
dPriority += (double)nValueIn * 1000; // flat multiplier
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
|
||||||
int nConf = nHeight - coins->nHeight;
|
// 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);
|
||||||
|
|
||||||
dPriority += (double)nValueIn * nConf;
|
CAmount nValueIn = coins->vout[txin.prevout.n].nValue;
|
||||||
|
nTotalIn += nValueIn;
|
||||||
|
|
||||||
|
int nConf = nHeight - coins->nHeight;
|
||||||
|
|
||||||
|
dPriority += (double)nValueIn * nConf;
|
||||||
|
}
|
||||||
|
nTotalIn += tx.GetJoinSplitValueIn();
|
||||||
}
|
}
|
||||||
nTotalIn += tx.GetJoinSplitValueIn();
|
|
||||||
|
|
||||||
if (fMissingInputs) continue;
|
if (fMissingInputs) continue;
|
||||||
|
|
||||||
|
|||||||
@@ -452,11 +452,21 @@ public:
|
|||||||
// Compute modified tx size for priority calculation (optionally given tx size)
|
// Compute modified tx size for priority calculation (optionally given tx size)
|
||||||
unsigned int CalculateModifiedSize(unsigned int nTxSize=0) const;
|
unsigned int CalculateModifiedSize(unsigned int nTxSize=0) const;
|
||||||
|
|
||||||
|
bool IsMint() const
|
||||||
|
{
|
||||||
|
return (vin.size() == 1 && vin[0].prevout.hash.IsNull());
|
||||||
|
}
|
||||||
|
|
||||||
bool IsCoinBase() const
|
bool IsCoinBase() const
|
||||||
{
|
{
|
||||||
return (vin.size() == 1 && vin[0].prevout.IsNull());
|
return (vin.size() == 1 && vin[0].prevout.IsNull());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsCoinImport() const
|
||||||
|
{
|
||||||
|
return IsMint() && vin[0].prevout.n == 10e8;
|
||||||
|
}
|
||||||
|
|
||||||
friend bool operator==(const CTransaction& a, const CTransaction& b)
|
friend bool operator==(const CTransaction& a, const CTransaction& b)
|
||||||
{
|
{
|
||||||
return a.hash == b.hash;
|
return a.hash == b.hash;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#include "checkpoints.h"
|
#include "checkpoints.h"
|
||||||
#include "base58.h"
|
#include "base58.h"
|
||||||
#include "consensus/validation.h"
|
#include "consensus/validation.h"
|
||||||
#include "cc/betprotocol.h"
|
#include "cc/eval.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "primitives/transaction.h"
|
#include "primitives/transaction.h"
|
||||||
#include "rpcserver.h"
|
#include "rpcserver.h"
|
||||||
@@ -1011,7 +1011,7 @@ UniValue txMoMproof(const UniValue& params, bool fHelp)
|
|||||||
|
|
||||||
// Encode and return
|
// Encode and return
|
||||||
CDataStream ssProof(SER_NETWORK, PROTOCOL_VERSION);
|
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());
|
return HexStr(ssProof.begin(), ssProof.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,17 +3,20 @@
|
|||||||
// Distributed under the MIT software license, see the accompanying
|
// Distributed under the MIT software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include <cryptoconditions.h>
|
||||||
|
|
||||||
#include "interpreter.h"
|
#include "interpreter.h"
|
||||||
|
|
||||||
#include "consensus/upgrades.h"
|
#include "consensus/upgrades.h"
|
||||||
#include "primitives/transaction.h"
|
#include "primitives/transaction.h"
|
||||||
|
#include "cc/eval.h"
|
||||||
#include "crypto/ripemd160.h"
|
#include "crypto/ripemd160.h"
|
||||||
#include "crypto/sha1.h"
|
#include "crypto/sha1.h"
|
||||||
#include "crypto/sha256.h"
|
#include "crypto/sha256.h"
|
||||||
#include "pubkey.h"
|
#include "pubkey.h"
|
||||||
#include "script/script.h"
|
#include "script/script.h"
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
#include "cryptoconditions/include/cryptoconditions.h"
|
|
||||||
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|||||||
@@ -186,5 +186,4 @@ bool VerifyScript(
|
|||||||
const BaseSignatureChecker& checker,
|
const BaseSignatureChecker& checker,
|
||||||
uint32_t consensusBranchId,
|
uint32_t consensusBranchId,
|
||||||
ScriptError* serror = NULL);
|
ScriptError* serror = NULL);
|
||||||
|
|
||||||
#endif // BITCOIN_SCRIPT_INTERPRETER_H
|
#endif // BITCOIN_SCRIPT_INTERPRETER_H
|
||||||
|
|||||||
@@ -8,8 +8,10 @@
|
|||||||
#include "tinyformat.h"
|
#include "tinyformat.h"
|
||||||
#include "utilstrencodings.h"
|
#include "utilstrencodings.h"
|
||||||
#include "script/cc.h"
|
#include "script/cc.h"
|
||||||
|
#include "cc/eval.h"
|
||||||
#include "cryptoconditions/include/cryptoconditions.h"
|
#include "cryptoconditions/include/cryptoconditions.h"
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
inline std::string ValueString(const std::vector<unsigned char>& vch)
|
inline std::string ValueString(const std::vector<unsigned char>& vch)
|
||||||
{
|
{
|
||||||
@@ -266,6 +268,17 @@ bool CScript::MayAcceptCryptoCondition() const
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CScript::IsCoinImport() const
|
||||||
|
{
|
||||||
|
const_iterator pc = this->begin();
|
||||||
|
vector<unsigned char> data;
|
||||||
|
opcodetype opcode;
|
||||||
|
if (this->GetOp(pc, opcode, data))
|
||||||
|
if (opcode > OP_0 && opcode <= OP_PUSHDATA4)
|
||||||
|
return data.begin()[0] == EVAL_IMPORTCOIN;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool CScript::IsPushOnly() const
|
bool CScript::IsPushOnly() const
|
||||||
{
|
{
|
||||||
const_iterator pc = begin();
|
const_iterator pc = begin();
|
||||||
|
|||||||
@@ -570,6 +570,7 @@ public:
|
|||||||
|
|
||||||
bool IsPayToScriptHash() const;
|
bool IsPayToScriptHash() const;
|
||||||
bool IsPayToCryptoCondition() const;
|
bool IsPayToCryptoCondition() const;
|
||||||
|
bool IsCoinImport() const;
|
||||||
bool MayAcceptCryptoCondition() const;
|
bool MayAcceptCryptoCondition() const;
|
||||||
|
|
||||||
/** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */
|
/** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
#include "key.h"
|
#include "key.h"
|
||||||
|
#include "base58.h"
|
||||||
#include "chainparams.h"
|
#include "chainparams.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "crypto/common.h"
|
#include "crypto/common.h"
|
||||||
|
#include "testutils.h"
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
@@ -9,6 +11,11 @@ int main(int argc, char **argv) {
|
|||||||
ECC_Start();
|
ECC_Start();
|
||||||
SelectParams(CBaseChainParams::REGTEST);
|
SelectParams(CBaseChainParams::REGTEST);
|
||||||
|
|
||||||
|
CBitcoinSecret vchSecret;
|
||||||
|
// this returns false due to network prefix mismatch but works anyway
|
||||||
|
vchSecret.SetString(notarySecret);
|
||||||
|
notaryKey = vchSecret.GetKey();
|
||||||
|
|
||||||
testing::InitGoogleTest(&argc, argv);
|
testing::InitGoogleTest(&argc, argv);
|
||||||
return RUN_ALL_TESTS();
|
return RUN_ALL_TESTS();
|
||||||
}
|
}
|
||||||
|
|||||||
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"
|
#include "testutils.h"
|
||||||
|
|
||||||
|
|
||||||
CKey notaryKey;
|
|
||||||
|
|
||||||
std::string pubkey = "0205a8ad0c1dbc515f149af377981aab58b836af008d4d7ab21bd76faf80550b47";
|
|
||||||
std::string secret = "UxFWWxsf1d7w7K5TvAWSkeX4H95XQKwdwGv49DXwWUTzPTTjHBbU";
|
|
||||||
|
|
||||||
|
|
||||||
class CCTest : public ::testing::Test {
|
class CCTest : public ::testing::Test {
|
||||||
public:
|
public:
|
||||||
@@ -32,11 +27,6 @@ protected:
|
|||||||
virtual void SetUp() {
|
virtual void SetUp() {
|
||||||
// enable CC
|
// enable CC
|
||||||
ASSETCHAINS_CC = 1;
|
ASSETCHAINS_CC = 1;
|
||||||
// Notary key
|
|
||||||
CBitcoinSecret vchSecret;
|
|
||||||
// this returns false due to network prefix mismatch but works anyway
|
|
||||||
vchSecret.SetString(secret);
|
|
||||||
notaryKey = vchSecret.GetKey();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ public:
|
|||||||
int nIndex = 5;
|
int nIndex = 5;
|
||||||
std::vector<uint256> vBranch;
|
std::vector<uint256> vBranch;
|
||||||
vBranch.resize(3);
|
vBranch.resize(3);
|
||||||
return MoMProof(nIndex, vBranch, EvalMock::NotarisationHash());
|
return {MerkleBranch(nIndex, vBranch), EvalMock::NotarisationHash()};
|
||||||
}
|
}
|
||||||
|
|
||||||
CMutableTransaction ImportPayoutTx()
|
CMutableTransaction ImportPayoutTx()
|
||||||
@@ -237,7 +237,7 @@ public:
|
|||||||
eval.currentHeight = currentHeight;
|
eval.currentHeight = currentHeight;
|
||||||
|
|
||||||
MoMProof proof = GetMoMProof();
|
MoMProof proof = GetMoMProof();
|
||||||
eval.MoM = proof.Exec(DisputeTx(Player2).GetHash());
|
eval.MoM = proof.branch.Exec(DisputeTx(Player2).GetHash());
|
||||||
|
|
||||||
EVAL_TEST = &eval;
|
EVAL_TEST = &eval;
|
||||||
return eval;
|
return eval;
|
||||||
@@ -585,7 +585,7 @@ TEST_F(TestBet, testImportPayoutMomFail)
|
|||||||
EvalMock eval = ebet.SetEvalMock(12);
|
EvalMock eval = ebet.SetEvalMock(12);
|
||||||
|
|
||||||
MoMProof proof = ebet.GetMoMProof();
|
MoMProof proof = ebet.GetMoMProof();
|
||||||
proof.nIndex ^= 1;
|
proof.branch.nIndex ^= 1;
|
||||||
CMutableTransaction importTx = ebet.bet.MakeImportPayoutTx(
|
CMutableTransaction importTx = ebet.bet.MakeImportPayoutTx(
|
||||||
ebet.Payouts(Player2), ebet.DisputeTx(Player2), uint256(), proof);
|
ebet.Payouts(Player2), ebet.DisputeTx(Player2), uint256(), proof);
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
#include "testutils.h"
|
#include "testutils.h"
|
||||||
|
|
||||||
|
|
||||||
extern Eval* EVAL_TEST;
|
|
||||||
extern int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp);
|
extern int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp);
|
||||||
|
|
||||||
|
|
||||||
@@ -61,7 +60,7 @@ static auto noop = [&](CMutableTransaction &mtx){};
|
|||||||
|
|
||||||
|
|
||||||
template<typename Modifier>
|
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);
|
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.txs[notary.GetHash()] = CTransaction(notary);
|
||||||
eval.blocks[notary.GetHash()].nHeight = 780060;
|
eval.blocks[notary.GetHash()].nHeight = 780060;
|
||||||
eval.blocks[notary.GetHash()].nTime = 1522946781;
|
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 uint256 proofTxHash = uint256S("37f76551a16093fbb0a92ee635bbd45b3460da8fd00cf7d5a6b20d93e727fe4c");
|
||||||
static auto vMomProof = ParseHex("0303faecbdd4b3da128c2cd2701bb143820a967069375b2ec5b612f39bbfe78a8611978871c193457ab1e21b9520f4139f113b8d75892eb93ee247c18bccfd067efed7eacbfcdc8946cf22de45ad536ec0719034fb9bc825048fe6ab61fee5bd6e9aae0bb279738d46673c53d68eb2a72da6dbff215ee41a4d405a74ff7cd355805b"); // $ fiat/bots txMoMproof $proofTxHash
|
static auto vMomProof = ParseHex("0303faecbdd4b3da128c2cd2701bb143820a967069375b2ec5b612f39bbfe78a8611978871c193457ab1e21b9520f4139f113b8d75892eb93ee247c18bccfd067efed7eacbfcdc8946cf22de45ad536ec0719034fb9bc825048fe6ab61fee5bd6e9aae0bb279738d46673c53d68eb2a72da6dbff215ee41a4d405a74ff7cd355805b"); // $ fiat/bots txMoMproof $proofTxHash
|
||||||
|
|
||||||
|
/*
|
||||||
TEST(TestEvalNotarisation, testGetNotarisation)
|
TEST(TestEvalNotarisation, testGetNotarisation)
|
||||||
{
|
{
|
||||||
EvalMock eval;
|
EvalMock eval;
|
||||||
CMutableTransaction notary(notaryTx);
|
CMutableTransaction notary(notaryTx);
|
||||||
SetEval(eval, notary, noop);
|
SetupEval(eval, notary, noop);
|
||||||
|
|
||||||
NotarisationData data;
|
NotarisationData data;
|
||||||
ASSERT_TRUE(eval.GetNotarisationData(notary.GetHash(), data));
|
ASSERT_TRUE(eval.GetNotarisationData(notary.GetHash(), data));
|
||||||
@@ -111,7 +108,7 @@ TEST(TestEvalNotarisation, testGetNotarisation)
|
|||||||
|
|
||||||
MoMProof proof;
|
MoMProof proof;
|
||||||
E_UNMARSHAL(vMomProof, ss >> proof);
|
E_UNMARSHAL(vMomProof, ss >> proof);
|
||||||
EXPECT_EQ(data.MoM, proof.Exec(proofTxHash));
|
EXPECT_EQ(data.MoM, proof.branch.Exec(proofTxHash));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -119,13 +116,14 @@ TEST(TestEvalNotarisation, testInvalidNotaryPubkey)
|
|||||||
{
|
{
|
||||||
EvalMock eval;
|
EvalMock eval;
|
||||||
CMutableTransaction notary(notaryTx);
|
CMutableTransaction notary(notaryTx);
|
||||||
SetEval(eval, notary, noop);
|
SetupEval(eval, notary, noop);
|
||||||
|
|
||||||
memset(eval.notaries[10], 0, 33);
|
memset(eval.notaries[10], 0, 33);
|
||||||
|
|
||||||
NotarisationData data;
|
NotarisationData data;
|
||||||
ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data));
|
ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data));
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
TEST(TestEvalNotarisation, testInvalidNotarisationBadOpReturn)
|
TEST(TestEvalNotarisation, testInvalidNotarisationBadOpReturn)
|
||||||
@@ -134,7 +132,7 @@ TEST(TestEvalNotarisation, testInvalidNotarisationBadOpReturn)
|
|||||||
CMutableTransaction notary(notaryTx);
|
CMutableTransaction notary(notaryTx);
|
||||||
|
|
||||||
notary.vout[1].scriptPubKey = CScript() << OP_RETURN << 0;
|
notary.vout[1].scriptPubKey = CScript() << OP_RETURN << 0;
|
||||||
SetEval(eval, notary, noop);
|
SetupEval(eval, notary, noop);
|
||||||
|
|
||||||
NotarisationData data;
|
NotarisationData data;
|
||||||
ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data));
|
ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data));
|
||||||
@@ -146,7 +144,7 @@ TEST(TestEvalNotarisation, testInvalidNotarisationTxNotEnoughSigs)
|
|||||||
EvalMock eval;
|
EvalMock eval;
|
||||||
CMutableTransaction notary(notaryTx);
|
CMutableTransaction notary(notaryTx);
|
||||||
|
|
||||||
SetEval(eval, notary, [](CMutableTransaction &tx) {
|
SetupEval(eval, notary, [](CMutableTransaction &tx) {
|
||||||
tx.vin.resize(10);
|
tx.vin.resize(10);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -160,7 +158,7 @@ TEST(TestEvalNotarisation, testInvalidNotarisationTxDoesntExist)
|
|||||||
EvalMock eval;
|
EvalMock eval;
|
||||||
CMutableTransaction notary(notaryTx);
|
CMutableTransaction notary(notaryTx);
|
||||||
|
|
||||||
SetEval(eval, notary, noop);
|
SetupEval(eval, notary, noop);
|
||||||
|
|
||||||
NotarisationData data;
|
NotarisationData data;
|
||||||
ASSERT_FALSE(eval.GetNotarisationData(uint256(), data));
|
ASSERT_FALSE(eval.GetNotarisationData(uint256(), data));
|
||||||
@@ -172,7 +170,7 @@ TEST(TestEvalNotarisation, testInvalidNotarisationDupeNotary)
|
|||||||
EvalMock eval;
|
EvalMock eval;
|
||||||
CMutableTransaction notary(notaryTx);
|
CMutableTransaction notary(notaryTx);
|
||||||
|
|
||||||
SetEval(eval, notary, [](CMutableTransaction &tx) {
|
SetupEval(eval, notary, [](CMutableTransaction &tx) {
|
||||||
tx.vin[1] = tx.vin[3];
|
tx.vin[1] = tx.vin[3];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -186,7 +184,7 @@ TEST(TestEvalNotarisation, testInvalidNotarisationInputNotCheckSig)
|
|||||||
EvalMock eval;
|
EvalMock eval;
|
||||||
CMutableTransaction notary(notaryTx);
|
CMutableTransaction notary(notaryTx);
|
||||||
|
|
||||||
SetEval(eval, notary, [&](CMutableTransaction &tx) {
|
SetupEval(eval, notary, [&](CMutableTransaction &tx) {
|
||||||
int i = 1;
|
int i = 1;
|
||||||
CMutableTransaction txIn;
|
CMutableTransaction txIn;
|
||||||
txIn.vout.resize(1);
|
txIn.vout.resize(1);
|
||||||
|
|||||||
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
|
#ifndef TESTUTILS_H
|
||||||
#define TESTUTILS_H
|
#define TESTUTILS_H
|
||||||
|
|
||||||
#include "script/cc.h"
|
#include "main.h"
|
||||||
|
|
||||||
|
|
||||||
#define VCH(a,b) std::vector<unsigned char>(a, a + b)
|
#define VCH(a,b) std::vector<unsigned char>(a, a + b)
|
||||||
@@ -12,4 +12,16 @@ static char ccjsonerr[1000] = "\0";
|
|||||||
if (!o) FAIL() << "bad json: " << ccjsonerr;
|
if (!o) FAIL() << "bad json: " << ccjsonerr;
|
||||||
|
|
||||||
|
|
||||||
|
extern std::string notaryPubkey;
|
||||||
|
extern std::string notarySecret;
|
||||||
|
extern CKey notaryKey;
|
||||||
|
|
||||||
|
|
||||||
|
void setupChain();
|
||||||
|
void generateBlock(CBlock *block=NULL);
|
||||||
|
bool acceptTx(const CTransaction tx, CValidationState &state);
|
||||||
|
void acceptTxFail(const CTransaction tx);
|
||||||
|
void getInputTx(CScript scriptPubKey, CTransaction &txIn);
|
||||||
|
|
||||||
|
|
||||||
#endif /* TESTUTILS_H */
|
#endif /* TESTUTILS_H */
|
||||||
|
|||||||
@@ -104,8 +104,10 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
|
|||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
mapTx.insert(entry);
|
mapTx.insert(entry);
|
||||||
const CTransaction& tx = mapTx.find(hash)->GetTx();
|
const CTransaction& tx = mapTx.find(hash)->GetTx();
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
if (!tx.IsCoinImport()) {
|
||||||
mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i);
|
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||||
|
mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i);
|
||||||
|
}
|
||||||
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
|
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
|
||||||
BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) {
|
BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) {
|
||||||
mapNullifiers[nf] = &tx;
|
mapNullifiers[nf] = &tx;
|
||||||
|
|||||||
Reference in New Issue
Block a user