Browse Source

fix all tests

pull/4/head
Scott Sadler 6 years ago
parent
commit
0df96a2f06
  1. 19
      src/cc/eval.cpp
  2. 3
      src/cc/eval.h
  3. 27
      src/cc/import.cpp
  4. 53
      src/importcoin.cpp
  5. 9
      src/importcoin.h
  6. 125
      src/rpccrosschain.cpp
  7. 33
      src/test-komodo/test_coinimport.cpp
  8. 3
      src/test-komodo/testutils.cpp

19
src/cc/eval.cpp

@ -147,12 +147,6 @@ 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 (on KMD)
*/
@ -171,6 +165,19 @@ bool Eval::GetNotarisationData(const uint256 notaryHash, NotarisationData &data)
*/
bool Eval::GetProofRoot(uint256 kmdNotarisationHash, uint256 &momom) const
{
return false; // TODO
}
uint32_t Eval::GetAssetchainsCC() const
{
return ASSETCHAINS_CC;
}
std::string Eval::GetAssetchainsSymbol() const
{
return std::string(ASSETCHAINS_SYMBOL);
}

3
src/cc/eval.h

@ -74,7 +74,8 @@ public:
virtual bool GetNotarisationData(uint256 notarisationHash, NotarisationData &data) const;
virtual bool GetProofRoot(uint256 kmdNotarisationHash, uint256 &momom) const;
virtual bool CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const;
virtual uint32_t GetCurrentLedgerID() const;
virtual uint32_t GetAssetchainsCC() const;
virtual std::string GetAssetchainsSymbol() const;
};

27
src/cc/import.cpp

@ -12,30 +12,31 @@
*/
bool Eval::ImportCoin(const std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn)
{
if (importTx.vout.size() == 0) return Invalid("no-vouts");
if (importTx.vout.size() < 2)
return Invalid("too-few-vouts");
// params
TxProof proof;
CTransaction burnTx;
if (!E_UNMARSHAL(params, ss >> proof; ss >> burnTx))
std::vector<CTxOut> payouts;
if (!UnmarshalImportTx(importTx, proof, burnTx, payouts))
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())
// It should not be at all malleable
if (MakeImportCoinTransaction(proof, burnTx, payouts).GetHash() != importTx.GetHash())
return Invalid("non-canonical");
// burn params
uint32_t chain; // todo
uint32_t targetCcid;
std::string targetSymbol;
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())
if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCcid, payoutsHash))
return Invalid("invalid-burn-tx");
if (targetCcid != GetAssetchainsCC() || targetSymbol != GetAssetchainsSymbol())
return Invalid("importcoin-wrong-chain");
// check burn amount
@ -51,7 +52,7 @@ bool Eval::ImportCoin(const std::vector<uint8_t> params, const CTransaction &imp
}
// Check burntx shows correct outputs hash
if (payoutsHash != SerializeHash(importTx.vout))
if (payoutsHash != SerializeHash(payouts))
return Invalid("wrong-payouts");
// Check proof confirms existance of burnTx

53
src/importcoin.cpp

@ -6,42 +6,48 @@
#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 TxProof proof, const CTransaction burnTx, const std::vector<CTxOut> payouts)
{
std::vector<uint8_t> payload =
E_MARSHAL(ss << EVAL_IMPORTCOIN; ss << proof; ss << burnTx);
std::vector<uint8_t> payload = E_MARSHAL(ss << EVAL_IMPORTCOIN);
CMutableTransaction mtx;
mtx.vin.push_back(CTxIn(COutPoint(burnTx.GetHash(), 10e8), CScript() << payload));
mtx.vout = payouts;
auto importData = E_MARSHAL(ss << proof; ss << burnTx);
mtx.vout.insert(mtx.vout.begin(), CTxOut(0, CScript() << OP_RETURN << importData));
return CTransaction(mtx);
}
CTxOut MakeBurnOutput(CAmount value, int targetChain, const std::vector<CTxOut> payouts)
CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector<CTxOut> payouts)
{
std::vector<uint8_t> opret = E_MARSHAL(ss << VARINT(targetChain); ss << SerializeHash(payouts));
std::vector<uint8_t> opret = E_MARSHAL(ss << VARINT(targetCCid);
ss << targetSymbol;
ss << SerializeHash(payouts));
return CTxOut(value, CScript() << OP_RETURN << opret);
}
static bool UnmarshalImportTx(const CTransaction &importTx, TxProof &proof, CTransaction &burnTx)
bool UnmarshalImportTx(const CTransaction &importTx, TxProof &proof, CTransaction &burnTx,
std::vector<CTxOut> &payouts)
{
CScript scriptSig = importTx.vin[0].scriptSig;
auto pc = scriptSig.begin();
opcodetype opcode;
std::vector<uint8_t> evalScript;
int code;
bool out = false;
if (scriptSig.GetOp(pc, opcode, evalScript))
if (pc == scriptSig.end())
out = E_UNMARSHAL(evalScript, ss >> VARINT(code); ss >> proof; ss >> burnTx);
return code == EVAL_IMPORTCOIN && out;
std::vector<uint8_t> vData;
GetOpReturnData(importTx.vout[0].scriptPubKey, vData);
if (importTx.vout.size() < 1) return false;
payouts = std::vector<CTxOut>(importTx.vout.begin()+1, importTx.vout.end());
return importTx.vin.size() == 1 &&
importTx.vin[0].scriptSig == (CScript() << E_MARSHAL(ss << EVAL_IMPORTCOIN)) &&
E_UNMARSHAL(vData, ss >> proof; ss >> burnTx);
}
bool UnmarshalBurnTx(const CTransaction &burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash)
{
std::vector<uint8_t> burnOpret;
if (burnTx.vout.size() == 0) return false;
GetOpReturnData(burnTx.vout[0].scriptPubKey, burnOpret);
return E_UNMARSHAL(burnOpret, ss >> VARINT(*targetCCid);
ss >> targetSymbol;
ss >> payoutsHash);
}
@ -53,7 +59,8 @@ CAmount GetCoinImportValue(const CTransaction &tx)
{
TxProof proof;
CTransaction burnTx;
if (UnmarshalImportTx(tx, proof, burnTx)) {
std::vector<CTxOut> payouts;
if (UnmarshalImportTx(tx, proof, burnTx, payouts)) {
return burnTx.vout.size() ? burnTx.vout[0].nValue : 0;
}
return 0;

9
src/importcoin.h

@ -13,10 +13,13 @@ CAmount GetCoinImportValue(const CTransaction &tx);
CTransaction MakeImportCoinTransaction(const TxProof proof,
const CTransaction burnTx, const std::vector<CTxOut> payouts);
CTxOut MakeBurnOutput(CAmount value, int targetChain, const std::vector<CTxOut> payouts);
CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector<CTxOut> payouts);
bool VerifyCoinImport(const CScript& scriptSig,
TransactionSignatureChecker& checker, CValidationState &state);
bool UnmarshalBurnTx(const CTransaction &burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash);
bool UnmarshalImportTx(const CTransaction &importTx, TxProof &proof, CTransaction &burnTx,
std::vector<CTxOut> &payouts);
bool VerifyCoinImport(const CScript& scriptSig, TransactionSignatureChecker& checker, CValidationState &state);
void AddImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs, int nHeight);

125
src/rpccrosschain.cpp

@ -3,9 +3,11 @@
#include "chainparams.h"
#include "checkpoints.h"
#include "crosschain.h"
#include "importcoin.h"
#include "base58.h"
#include "consensus/validation.h"
#include "cc/eval.h"
#include "cc/utils.h"
#include "main.h"
#include "primitives/transaction.h"
#include "rpcserver.h"
@ -156,3 +158,126 @@ UniValue calc_MoM(const UniValue& params, bool fHelp)
ret.push_back(Pair("MoM",MoM.GetHex()));
return ret;
}
UniValue migrate_converttoexport(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 3)
throw runtime_error(
"migrate_converttoexport \"hexstring\" \"dest_symbol\" \"burn_amount\"\n"
"\nConvert a raw transaction to a cross-chain export.\n"
"If neccesary, the transaction should be funded using fundrawtransaction.\n"
"Finally, the transaction should be signed using signrawtransaction\n"
"The finished export transaction, plus the vouts, should be passed to "
"the \"importtransaction\" method on a KMD node to get the corresponding "
"import transaction.\n"
);
if (ASSETCHAINS_CC < 2)
throw runtime_error("-ac_cc < 2");
if (ASSETCHAINS_SYMBOL[0] == 0)
throw runtime_error("Must be called on assetchain");
vector<uint8_t> txData(ParseHexV(params[0], "argument 1"));
CMutableTransaction tx;
if (!E_UNMARSHAL(txData, ss >> tx))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
string targetSymbol = params[1].get_str();
if (targetSymbol.size() == 0 || targetSymbol.size() > 32)
throw runtime_error("targetSymbol length must be >0 and <=32");
CAmount burnAmount = params[2].get_int64();
{
CAmount needed;
for (int i=0; i<tx.vout.size(); i++)
needed += tx.vout[i].nValue;
if (burnAmount < needed)
throw runtime_error("burnAmount too small");
}
CTxOut burnOut = MakeBurnOutput(burnAmount, ASSETCHAINS_CC, targetSymbol, tx.vout);
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("vouts", HexStr(E_MARSHAL(ss << tx.vout))));
tx.vout.clear();
tx.vout.push_back(burnOut);
ret.push_back(Pair("exportTx", HexStr(E_MARSHAL(ss << tx))));
return ret;
}
/*
* The process to migrate funds
*
* Create a transaction on assetchain:
*
* generaterawtransaction
* migrate_converttoexport
* fundrawtransaction
* signrawtransaction
*
* migrate_createimportransaction
* migrate_completeimporttransaction
*/
UniValue migrate_createimporttransaction(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 2)
throw runtime_error("");
if (ASSETCHAINS_CC < 2)
throw runtime_error("-ac_cc < 2");
if (ASSETCHAINS_SYMBOL[0] == 0)
throw runtime_error("Must be called on assetchain");
vector<uint8_t> txData(ParseHexV(params[0], "argument 1"));
CTransaction burnTx;
if (!E_UNMARSHAL(txData, ss >> burnTx))
throw runtime_error("Couldn't parse burnTx");
vector<CTxOut> payouts;
if (!E_UNMARSHAL(ParseHexV(params[0], "argument 2"), ss >> payouts))
throw runtime_error("Couldn't parse payouts");
uint256 txid = burnTx.GetHash();
TxProof proof = GetAssetchainProof(burnTx.GetHash());
CTransaction importTx = MakeImportCoinTransaction(proof, burnTx, payouts);
return HexStr(E_MARSHAL(ss << importTx));
}
UniValue migrate_completeimporttransaction(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error("");
if (ASSETCHAINS_SYMBOL[0] != 0)
throw runtime_error("Must be called on KMD");
CTransaction importTx;
if (!E_UNMARSHAL(ParseHexV(params[0], "argument 2"), ss >> importTx))
throw runtime_error("Couldn't parse importTx");
TxProof proof;
CTransaction burnTx;
vector<CTxOut> payouts;
if (!UnmarshalImportTx(importTx, proof, burnTx, payouts))
throw runtime_error("Couldn't parse importTx data");
std::string targetSymbol;
uint32_t targetCCid;
uint256 payoutsHash;
if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCCid, payoutsHash))
throw runtime_error("Couldn't parse burnTx data");
proof = GetCrossChainProof(burnTx.GetHash(), targetSymbol.data(), targetCCid, proof);
importTx = MakeImportCoinTransaction(proof, burnTx, importTx.vout);
return HexStr(E_MARSHAL(ss << importTx));
}

33
src/test-komodo/test_coinimport.cpp

@ -31,17 +31,19 @@ public:
TxProof proof;
uint256 MoMoM;
CMutableTransaction importTx;
uint32_t chainId = 2;
uint32_t testCcid = 2;
std::string testSymbol = "PIZZA";
CAmount amount = 100;
void SetImportTx() {
burnTx.vout.resize(0);
burnTx.vout.push_back(MakeBurnOutput(amount, chainId, payouts));
MoMoM = burnTx.GetHash(); // TODO: an actual branch
burnTx.vout.push_back(MakeBurnOutput(amount, testCcid, testSymbol, payouts));
importTx = CMutableTransaction(MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts));
MoMoM = burnTx.GetHash(); // TODO: an actual branch
}
uint32_t GetCurrentLedgerID() const { return chainId; }
uint32_t GetAssetchainsCC() const { return testCcid; }
std::string GetAssetchainsSymbol() const { return testSymbol; }
bool GetProofRoot(uint256 hash, uint256 &momom) const
{
@ -145,7 +147,7 @@ TEST_F(TestCoinImport, testNoVouts)
{
importTx.vout.resize(0);
TestRunCCEval(importTx);
EXPECT_EQ("no-vouts", state.GetRejectReason());
EXPECT_EQ("too-few-vouts", state.GetRejectReason());
}
@ -172,23 +174,23 @@ TEST_F(TestCoinImport, testInvalidBurnOutputs)
MoMoM = burnTx.GetHash(); // TODO: an actual branch
CTransaction tx = MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts);
TestRunCCEval(tx);
EXPECT_EQ("invalid-burn-outputs", state.GetRejectReason());
EXPECT_EQ("invalid-burn-tx", state.GetRejectReason());
}
TEST_F(TestCoinImport, testInvalidBurnParams)
{
burnTx.vout[0].scriptPubKey = CScript() << OP_RETURN << E_MARSHAL(ss << VARINT(chainId));
burnTx.vout[0].scriptPubKey = CScript() << OP_RETURN << E_MARSHAL(ss << VARINT(testCcid));
MoMoM = burnTx.GetHash(); // TODO: an actual branch
CTransaction tx = MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts);
TestRunCCEval(tx);
EXPECT_EQ("invalid-burn-params", state.GetRejectReason());
EXPECT_EQ("invalid-burn-tx", state.GetRejectReason());
}
TEST_F(TestCoinImport, testWrongChainId)
{
chainId = 0;
testCcid = 0;
TestRunCCEval(importTx);
EXPECT_EQ("importcoin-wrong-chain", state.GetRejectReason());
}
@ -206,15 +208,24 @@ TEST_F(TestCoinImport, testInvalidBurnAmount)
TEST_F(TestCoinImport, testPayoutTooHigh)
{
importTx.vout[0].nValue = 101;
importTx.vout[1].nValue = 101;
TestRunCCEval(importTx);
EXPECT_EQ("payout-too-high", state.GetRejectReason());
}
TEST_F(TestCoinImport, testAmountInOpret)
{
importTx.vout[0].nValue = 1;
TestRunCCEval(importTx);
EXPECT_EQ("non-canonical", state.GetRejectReason());
}
TEST_F(TestCoinImport, testInvalidPayouts)
{
importTx.vout[0].nValue = 40;
importTx.vout[1].nValue = 40;
importTx.vout.push_back(importTx.vout[0]);
TestRunCCEval(importTx);
EXPECT_EQ("wrong-payouts", state.GetRejectReason());

3
src/test-komodo/testutils.cpp

@ -47,6 +47,9 @@ void setupChain()
COINBASE_MATURITY = 1;
// Global mock time
nMockTime = GetTime();
// Unload
UnloadBlockIndex();
// Init blockchain
ClearDatadirCache();

Loading…
Cancel
Save