[BROKEN] Introduce CA in data structures

This commit is contained in:
Steven Roose 2019-03-19 20:26:39 +00:00
parent 04d06917d2
commit d53479c9ff
No known key found for this signature in database
GPG key ID: 7FC91380BB4CE800
17 changed files with 336 additions and 135 deletions

View file

@ -48,10 +48,8 @@ static CBlock CreateGenesisBlock(const Consensus::Params& params, const CScript&
CMutableTransaction txNew;
txNew.nVersion = 1;
txNew.vin.resize(1);
txNew.vout.resize(1);
txNew.vin[0].scriptSig = genesisScriptSig;
txNew.vout[0].nValue = genesisReward;
txNew.vout[0].scriptPubKey = genesisOutputScript;
txNew.vout.push_back(CTxOut(CAsset(), genesisReward, genesisOutputScript));
CBlock genesis;
genesis.nTime = nTime;
@ -86,23 +84,6 @@ static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits
return CreateGenesisBlock(params, genesisScriptSig, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward);
}
/** Add an issuance transaction to the genesis block. Typically used to pre-issue
* the policyAsset of a blockchain. The genesis block is not actually validated,
* so this transaction simply has to match issuance structure. */
static void AppendInitialIssuance(CBlock& genesis_block, const COutPoint& prevout, const int64_t asset_values, const CScript& issuance_destination) {
// Note: Genesis block isn't actually validated, outputs are entered into utxo db only
CMutableTransaction txNew;
txNew.nVersion = 1;
txNew.vin.resize(1);
txNew.vin[0].prevout = prevout;
txNew.vout.push_back(CTxOut(asset_values, issuance_destination));
genesis_block.vtx.push_back(MakeTransactionRef(std::move(txNew)));
genesis_block.hashMerkleRoot = BlockMerkleRoot(genesis_block);
}
/**
* Main network
*/
@ -623,8 +604,8 @@ class CCustomParams : public CRegTestParams {
// Intended compatibility with Liquid v1 and elements-0.14.1
std::vector<unsigned char> commit = CommitToArguments(consensus, strNetworkID);
genesis = CreateGenesisBlock(consensus, CScript(commit), CScript(OP_RETURN), 1296688602, 2, 0x207fffff, 1, 0);
if (initialFreeCoins != 0) {
AppendInitialIssuance(genesis, COutPoint(uint256(commit), 0), initialFreeCoins, CScript() << OP_TRUE);
if (initialFreeCoins != 0 || initial_reissuance_tokens != 0) {
AppendInitialIssuance(genesis, COutPoint(uint256(commit), 0), parentGenesisBlockHash, (initialFreeCoins > 0) ? 1 : 0, initialFreeCoins, (initial_reissuance_tokens > 0) ? 1 : 0, initial_reissuance_tokens, CScript() << OP_TRUE);
}
} else {
throw std::runtime_error(strprintf("Invalid -genesis_style (%s)", consensus.genesis_style));

View file

@ -124,7 +124,16 @@ bool CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin* moveout) {
return true;
}
static const Coin coinEmpty;
// ELEMENTS:
// Because g_con_elementsmode is only set after the moment coinEmpty is initialized,
// we have to force set it to an empty coin without the default asset commitment.
Coin generateEmptyCoin() {
Coin coin;
coin.out.nValue.vchCommitment.clear();
coin.out.nAsset.vchCommitment.clear();
return coin;
}
static const Coin coinEmpty = generateEmptyCoin();
const Coin& CCoinsViewCache::AccessCoin(const COutPoint &outpoint) const {
CCoinsMap::const_iterator it = FetchCoin(outpoint);
@ -306,7 +315,8 @@ CAmount CCoinsViewCache::GetValueIn(const CTransaction& tx) const
CAmount nResult = 0;
for (unsigned int i = 0; i < tx.vin.size(); i++)
nResult += AccessCoin(tx.vin[i].prevout).out.nValue;
// ELEMENTS: this method is for tests only, just naively add amounts
nResult += AccessCoin(tx.vin[i].prevout).out.nValue.GetAmount();
return nResult;
}

View file

@ -95,14 +95,42 @@ public:
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
if (!ser_action.ForRead()) {
uint64_t nVal = CompressAmount(txout.nValue);
READWRITE(VARINT(nVal));
if (g_con_elementsmode) {
if (!ser_action.ForRead()) {
if (txout.nValue.IsExplicit()) {
uint8_t b = 0;
READWRITE(b);
uint64_t nVal = CompressAmount(txout.nValue.GetAmount());
READWRITE(VARINT(nVal));
} else {
uint8_t b = 1;
READWRITE(b);
READWRITE(txout.nValue);
}
} else {
uint8_t type = 0;
READWRITE(type);
if (type == 0) {
uint64_t nVal = 0;
READWRITE(VARINT(nVal));
txout.nValue = DecompressAmount(nVal);
} else {
READWRITE(txout.nValue);
}
}
READWRITE(txout.nAsset);
} else {
uint64_t nVal = 0;
READWRITE(VARINT(nVal));
txout.nValue = DecompressAmount(nVal);
if (!ser_action.ForRead()) {
assert(txout.nValue.IsExplicit());
uint64_t nVal = CompressAmount(txout.nValue.GetAmount());
READWRITE(VARINT(nVal));
} else {
uint64_t nVal = 0;
READWRITE(VARINT(nVal));
txout.nValue = DecompressAmount(nVal);
}
}
CScriptCompressor cscript(REF(txout.scriptPubKey));
READWRITE(cscript);
}

View file

@ -77,13 +77,17 @@ uint256 BlockWitnessMerkleRoot(const CBlock& block, bool* mutated)
{
std::vector<uint256> leaves;
leaves.resize(block.vtx.size());
leaves[0].SetNull(); // The witness hash of the coinbase is 0.
for (size_t s = 1; s < block.vtx.size(); s++) {
if (g_con_elementsmode) {
if (g_con_elementsmode) {
// Coinbase witness hash for inputs is just CTxInWitness().GetHash()
for (size_t s = 0; s < block.vtx.size(); s++) {
leaves[s] = block.vtx[s]->GetWitnessOnlyHash();
} else {
}
return ComputeFastMerkleRoot(std::move(leaves));
} else {
leaves[0].SetNull(); // The witness hash of the coinbase is 0.
for (size_t s = 1; s < block.vtx.size(); s++) {
leaves[s] = block.vtx[s]->GetWitnessHash();
}
return ComputeMerkleRoot(std::move(leaves), mutated);
}
return ComputeMerkleRoot(std::move(leaves), mutated);
}

View file

@ -6,6 +6,7 @@
#define BITCOIN_CORE_IO_H
#include <amount.h>
#include <asset.h>
#include <string>
#include <vector>

View file

@ -6,6 +6,7 @@
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <issuance.h>
#include <key_io.h>
#include <script/script.h>
#include <script/standard.h>
@ -16,6 +17,32 @@
#include <utilmoneystr.h>
#include <utilstrencodings.h>
#include <secp256k1_rangeproof.h>
static secp256k1_context* secp256k1_blind_context = NULL;
class RPCRawTransaction_ECC_Init {
public:
RPCRawTransaction_ECC_Init() {
assert(secp256k1_blind_context == NULL);
secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
assert(ctx != NULL);
secp256k1_blind_context = ctx;
}
~RPCRawTransaction_ECC_Init() {
secp256k1_context *ctx = secp256k1_blind_context;
secp256k1_blind_context = NULL;
if (ctx) {
secp256k1_context_destroy(ctx);
}
}
};
static RPCRawTransaction_ECC_Init ecc_init_on_load;
UniValue ValueFromAmount(const CAmount& amount)
{
bool sign = amount < 0;
@ -223,29 +250,65 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
o.pushKV("asm", ScriptToAsmStr(txin.scriptSig, true));
o.pushKV("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
in.pushKV("scriptSig", o);
if (tx.witness.vtxinwit.size() > i) {
const CScriptWitness &scriptWitness = tx.witness.vtxinwit[i].scriptWitness;
if (!scriptWitness.IsNull()) {
UniValue txinwitness(UniValue::VARR);
for (const auto &item : scriptWitness.stack) {
txinwitness.push_back(HexStr(item.begin(), item.end()));
}
in.pushKV("txinwitness", txinwitness);
}
}
// ELEMENTS:
in.pushKV("is_pegin", txin.m_is_pegin);
if (tx.witness.vtxinwit.size() > i && !tx.witness.vtxinwit[i].m_pegin_witness.IsNull()) {
UniValue pegin_witness(UniValue::VARR);
for (const auto& item : tx.witness.vtxinwit[i].m_pegin_witness.stack) {
pegin_witness.push_back(HexStr(item.begin(), item.end()));
}
in.pushKV("pegin_witness", pegin_witness);
}
}
in.pushKV("sequence", (int64_t)txin.nSequence);
if (tx.witness.vtxinwit.size() > i) {
const CScriptWitness &scriptWitness = tx.witness.vtxinwit[i].scriptWitness;
if (!scriptWitness.IsNull()) {
UniValue txinwitness(UniValue::VARR);
for (const auto &item : scriptWitness.stack) {
txinwitness.push_back(HexStr(item.begin(), item.end()));
}
in.pushKV("txinwitness", txinwitness);
}
}
// ELEMENTS:
if (tx.witness.vtxinwit.size() > i && !tx.witness.vtxinwit[i].m_pegin_witness.IsNull()) {
UniValue pegin_witness(UniValue::VARR);
for (const auto& item : tx.witness.vtxinwit[i].m_pegin_witness.stack) {
pegin_witness.push_back(HexStr(item.begin(), item.end()));
}
in.pushKV("pegin_witness", pegin_witness);
}
const CAssetIssuance& issuance = txin.assetIssuance;
if (!issuance.IsNull()) {
UniValue issue(UniValue::VOBJ);
issue.pushKV("assetBlindingNonce", issuance.assetBlindingNonce.GetHex());
CAsset asset;
CAsset token;
uint256 entropy;
if (issuance.assetBlindingNonce.IsNull()) {
GenerateAssetEntropy(entropy, txin.prevout, issuance.assetEntropy);
issue.pushKV("assetEntropy", entropy.GetHex());
CalculateAsset(asset, entropy);
CalculateReissuanceToken(token, entropy, issuance.nAmount.IsCommitment());
issue.pushKV("isreissuance", false);
issue.pushKV("token", token.GetHex());
}
else {
issue.pushKV("assetEntropy", issuance.assetEntropy.GetHex());
issue.pushKV("isreissuance", true);
CalculateAsset(asset, issuance.assetEntropy);
}
issue.pushKV("asset", asset.GetHex());
if (issuance.nAmount.IsExplicit()) {
issue.pushKV("assetamount", ValueFromAmount(issuance.nAmount.GetAmount()));
} else if (issuance.nAmount.IsCommitment()) {
issue.pushKV("assetamountcommitment", HexStr(issuance.nAmount.vchCommitment));
}
if (issuance.nInflationKeys.IsExplicit()) {
issue.pushKV("tokenamount", ValueFromAmount(issuance.nInflationKeys.GetAmount()));
} else if (issuance.nInflationKeys.IsCommitment()) {
issue.pushKV("tokenamountcommitment", HexStr(issuance.nInflationKeys.vchCommitment));
}
in.pushKV("issuance", issue);
}
// END ELEMENTS
vin.push_back(in);
}
entry.pushKV("vin", vin);
@ -256,7 +319,37 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
UniValue out(UniValue::VOBJ);
out.pushKV("value", ValueFromAmount(txout.nValue));
if (txout.nValue.IsExplicit()) {
out.pushKV("value", ValueFromAmount(txout.nValue.GetAmount()));
} else {
int exp;
int mantissa;
uint64_t minv;
uint64_t maxv;
const CTxOutWitness* ptxoutwit = tx.witness.vtxoutwit.size() <= i? NULL: &tx.witness.vtxoutwit[i];
if (ptxoutwit && secp256k1_rangeproof_info(secp256k1_blind_context, &exp, &mantissa, &minv, &maxv, &ptxoutwit->vchRangeproof[0], ptxoutwit->vchRangeproof.size())) {
if (exp == -1) {
out.pushKV("value", ValueFromAmount((CAmount)minv));
} else {
out.pushKV("value-minimum", ValueFromAmount((CAmount)minv));
out.pushKV("value-maximum", ValueFromAmount((CAmount)maxv));
}
out.pushKV("ct-exponent", exp);
out.pushKV("ct-bits", mantissa);
}
out.pushKV("valuecommitment", txout.nValue.GetHex());
}
if (g_con_elementsmode) {
if (txout.nAsset.IsExplicit()) {
out.pushKV("asset", txout.nAsset.GetAsset().GetHex());
} else {
out.pushKV("assetcommitment", txout.nAsset.GetHex());
}
out.pushKV("commitmentnonce", txout.nNonce.GetHex());
CPubKey pubkey(txout.nNonce.vchCommitment);
out.pushKV("commitmentnonce_fully_valid", pubkey.IsFullyValid());
}
out.pushKV("n", (int64_t)i);
UniValue o(UniValue::VOBJ);

View file

@ -355,8 +355,8 @@ public:
num_blocks = ::chainActive.Height();
return true;
}
CAmount getBalance() override { return m_wallet.GetBalance(); }
CAmount getAvailableBalance(const CCoinControl& coin_control) override
CAmountMap getBalance() override { return m_wallet.GetBalance(); }
CAmountMap getAvailableBalance(const CCoinControl& coin_control) override
{
return m_wallet.GetAvailableBalance(&coin_control);
}
@ -370,12 +370,12 @@ public:
LOCK2(::cs_main, m_wallet.cs_wallet);
return m_wallet.IsMine(txout);
}
CAmount getDebit(const CTxIn& txin, isminefilter filter) override
CAmountMap getDebit(const CTxIn& txin, isminefilter filter) override
{
LOCK2(::cs_main, m_wallet.cs_wallet);
return m_wallet.GetDebit(txin, filter);
}
CAmount getCredit(const CTxOut& txout, isminefilter filter) override
CAmountMap getCredit(const CTxOut& txout, isminefilter filter) override
{
LOCK2(::cs_main, m_wallet.cs_wallet);
return m_wallet.GetCredit(txout, filter);

View file

@ -6,6 +6,7 @@
#define BITCOIN_INTERFACES_WALLET_H
#include <amount.h> // For CAmount
#include <asset.h> // For CAmountMap
#include <pubkey.h> // For CKeyID and CScriptID (definitions needed in CTxDestination instantiation)
#include <script/ismine.h> // For isminefilter, isminetype
#include <script/standard.h> // For CTxDestination
@ -196,10 +197,10 @@ public:
virtual bool tryGetBalances(WalletBalances& balances, int& num_blocks) = 0;
//! Get balance.
virtual CAmount getBalance() = 0;
virtual CAmountMap getBalance() = 0;
//! Get available balance.
virtual CAmount getAvailableBalance(const CCoinControl& coin_control) = 0;
virtual CAmountMap getAvailableBalance(const CCoinControl& coin_control) = 0;
//! Return whether transaction input belongs to wallet.
virtual isminetype txinIsMine(const CTxIn& txin) = 0;
@ -208,10 +209,10 @@ public:
virtual isminetype txoutIsMine(const CTxOut& txout) = 0;
//! Return debit amount if transaction input belongs to wallet.
virtual CAmount getDebit(const CTxIn& txin, isminefilter filter) = 0;
virtual CAmountMap getDebit(const CTxIn& txin, isminefilter filter) = 0;
//! Return credit amount if transaction input belongs to wallet.
virtual CAmount getCredit(const CTxOut& txout, isminefilter filter) = 0;
virtual CAmountMap getCredit(const CTxOut& txout, isminefilter filter) = 0;
//! Return AvailableCoins + LockedCoins grouped by wallet address.
//! (put change in one group with wallet address)
@ -309,13 +310,13 @@ struct WalletAddress
//! Collection of wallet balances.
struct WalletBalances
{
CAmount balance = 0;
CAmount unconfirmed_balance = 0;
CAmount immature_balance = 0;
CAmountMap balance = CAmountMap();
CAmountMap unconfirmed_balance = CAmountMap();
CAmountMap immature_balance = CAmountMap();
bool have_watch_only = false;
CAmount watch_only_balance = 0;
CAmount unconfirmed_watch_only_balance = 0;
CAmount immature_watch_only_balance = 0;
CAmountMap watch_only_balance = CAmountMap();
CAmountMap unconfirmed_watch_only_balance = CAmountMap();
CAmountMap immature_watch_only_balance = CAmountMap();
bool balanceChanged(const WalletBalances& prev) const
{
@ -334,9 +335,9 @@ struct WalletTx
std::vector<isminetype> txout_is_mine;
std::vector<CTxDestination> txout_address;
std::vector<isminetype> txout_address_is_mine;
CAmount credit;
CAmount debit;
CAmount change;
CAmountMap credit;
CAmountMap debit;
CAmountMap change;
int64_t time;
std::map<std::string, std::string> value_map;
bool is_coinbase;

View file

@ -46,3 +46,37 @@ void CalculateReissuanceToken(CAsset& reissuanceToken, const uint256& entropy, b
leaves.push_back(fConfidential ? kTwo : kOne);
reissuanceToken = CAsset(ComputeFastMerkleRoot(leaves));
}
/** Add an issuance transaction to the genesis block. Typically used to pre-issue
* the policyAsset of a blockchain. The genesis block is not actually validated,
* so this transaction simply has to match issuance structure. */
void AppendInitialIssuance(CBlock& genesis_block, const COutPoint& prevout, const uint256& contract, const int64_t asset_outputs, const int64_t asset_values, const int64_t reissuance_outputs, const int64_t reissuance_values, const CScript& issuance_destination) {
uint256 entropy;
GenerateAssetEntropy(entropy, prevout, contract);
CAsset asset;
CalculateAsset(asset, entropy);
// Re-issuance of policyAsset is always unblinded
CAsset reissuance;
CalculateReissuanceToken(reissuance, entropy, false);
// Note: Genesis block isn't actually validated, outputs are entered into utxo db only
CMutableTransaction txNew;
txNew.nVersion = 1;
txNew.vin.resize(1);
txNew.vin[0].prevout = prevout;
txNew.vin[0].assetIssuance.assetEntropy = contract;
txNew.vin[0].assetIssuance.nAmount = CConfidentialValue(asset_values * asset_outputs);
txNew.vin[0].assetIssuance.nInflationKeys = CConfidentialValue(reissuance_values * reissuance_outputs);
for (unsigned int i = 0; i < asset_outputs; i++) {
txNew.vout.push_back(CTxOut(asset, CConfidentialValue(asset_values), issuance_destination));
}
for (unsigned int i = 0; i < reissuance_outputs; i++) {
txNew.vout.push_back(CTxOut(reissuance, CConfidentialValue(reissuance_values), issuance_destination));
}
genesis_block.vtx.push_back(MakeTransactionRef(std::move(txNew)));
genesis_block.hashMerkleRoot = BlockMerkleRoot(genesis_block);
}

View file

@ -43,4 +43,6 @@ void CalculateAsset(CAsset& asset, const uint256& entropy);
*/
void CalculateReissuanceToken(CAsset& reissuanceToken, const uint256& entropy, bool fConfidential);
void AppendInitialIssuance(CBlock& genesis_block, const COutPoint& prevout, const uint256& contract, const int64_t asset_outputs, const int64_t asset_values, const int64_t reissuance_outputs, const int64_t reissuance_values, const CScript& issuance_destination);
#endif // BITCOIN_ISSUANCE_H

View file

@ -124,7 +124,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
// Pad block weight to account for OP_RETURN commitments with two compressed pubkeys
for (const auto& commitment : commitments) {
CTxOut output(0, commitment);
CTxOut output(CAsset(), 0, commitment);
nBlockWeight += ::GetSerializeSize(output, PROTOCOL_VERSION)*WITNESS_SCALE_FACTOR;
}
// END PAK
@ -187,12 +187,23 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
coinbaseTx.vin[0].prevout.SetNull();
coinbaseTx.vout.resize(1);
coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn;
coinbaseTx.vout[0].nAsset = policyAsset;
coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus());
if (g_con_elementsmode) {
if(chainparams.GetConsensus().subsidy_asset != policyAsset) {
// Only claim the subsidy if it's the same as the policy asset.
coinbaseTx.vout[0].nValue = nFees;
}
// 0-value outputs must be unspendable
if (coinbaseTx.vout[0].nValue.GetAmount() == 0) {
coinbaseTx.vout[0].scriptPubKey = CScript() << OP_RETURN;
}
}
coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0;
// ELEMENTS: PAK
// Add PAK transition commitments
for (unsigned int i = 0; i < commitments.size(); i++) {
coinbaseTx.vout.push_back(CTxOut(0, commitments[i]));
coinbaseTx.vout.push_back(CTxOut(CAsset(), 0, commitments[i]));
}
// END PAK
pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx));

View file

@ -54,18 +54,16 @@ bool GetAmountFromParentChainPegin(CAmount& amount, const Sidechain::Bitcoin::CT
bool GetAmountFromParentChainPegin(CAmount& amount, const CTransaction& txBTC, unsigned int nOut)
{
//if (!txBTC.vout[nOut].nValue.IsExplicit()) {
// return false;
//}
//if (!txBTC.vout[nOut].nAsset.IsExplicit()) {
// return false;
//}
//if (txBTC.vout[nOut].nAsset.GetAsset() != Params().GetConsensus().parent_pegged_asset) {
// return false;
//}
//amount = txBTC.vout[nOut].nValue.GetAmount();
//TODO(rebase) re-enable above for CA/CT
amount = txBTC.vout[nOut].nValue;
if (!txBTC.vout[nOut].nValue.IsExplicit()) {
return false;
}
if (!txBTC.vout[nOut].nAsset.IsExplicit()) {
return false;
}
if (txBTC.vout[nOut].nAsset.GetAsset() != Params().GetConsensus().parent_pegged_asset) {
return false;
}
amount = txBTC.vout[nOut].nValue.GetAmount();
return true;
}
@ -278,8 +276,7 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& p
err_msg = "Asset type was not 32 bytes.";
return false;
}
//TODO(rebase) CA
//CAsset asset(stack[1]);
CAsset asset(stack[1]);
// Get genesis blockhash
if (stack[2].size() != 32) {
@ -350,11 +347,10 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& p
return false;
}
//TODO(rebase) CA
//// Check the asset type corresponds to a valid pegged asset (only one for now)
//if (asset != Params().GetConsensus().pegged_asset) {
// return false;
//}
// Check the asset type corresponds to a valid pegged asset (only one for now)
if (asset != Params().GetConsensus().pegged_asset) {
return false;
}
// Finally, validate peg-in via rpc call
if (check_depth && gArgs.GetBoolArg("-validatepegin", DEFAULT_VALIDATE_PEGIN)) {
@ -372,9 +368,7 @@ CTxOut GetPeginOutputFromWitness(const CScriptWitness& pegin_witness) {
CAmount value;
stream >> value;
//TODO(rebase) CA
//return CTxOut(CAsset(pegin_witness.stack[1]), value, CScript(pegin_witness.stack[3].begin(), pegin_witness.stack[3].end()));
return CTxOut(value, CScript(pegin_witness.stack[3].begin(), pegin_witness.stack[3].end()));
return CTxOut(CAsset(pegin_witness.stack[1]), CConfidentialValue(value), CScript(pegin_witness.stack[3].begin(), pegin_witness.stack[3].end()));
}
bool MatchLiquidWatchman(const CScript& script)

View file

@ -55,7 +55,13 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
{
return (txout.nValue < GetDustThreshold(txout, dustRelayFeeIn));
if (!txout.nValue.IsExplicit())
return false; // FIXME
if (!txout.nAsset.IsExplicit())
return false;
if (txout.IsFee())
return false;
return (txout.nValue.GetAmount() < GetDustThreshold(txout, dustRelayFeeIn));
}
bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType)
@ -131,21 +137,19 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason)
return false;
}
if (whichType == TX_NULL_DATA) {
}
if (whichType == TX_NULL_DATA) {
nDataOut++;
if (params.GetEnforcePak() &&
txout.scriptPubKey.IsPegoutScript(params.ParentGenesisBlockHash()) &&
(!ScriptHasValidPAKProof(txout.scriptPubKey, params.ParentGenesisBlockHash()))) {
// TODO(rebase) CT/CA check for asset type
txout.nAsset.IsExplicit() && txout.nAsset.GetAsset() == Params().GetConsensus().pegged_asset &&
(!ScriptHasValidPAKProof(txout.scriptPubKey, params.ParentGenesisBlockHash()))) {
reason = "invalid-pegout-proof";
return false;
}
} else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) {
reason = "bare-multisig";
return false;
} else if (IsDust(txout, ::dustRelayFee)) {
} else if ((txout.nAsset.IsExplicit() && txout.nAsset.GetAsset() == policyAsset) && IsDust(txout, ::dustRelayFee)) {
reason = "dust";
return false;
}

View file

@ -47,15 +47,21 @@ std::string CTxIn::ToString() const
return str;
}
CTxOut::CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn)
CTxOut::CTxOut(const CConfidentialAsset& nAssetIn, const CConfidentialValue& nValueIn, CScript scriptPubKeyIn)
{
nAsset = nAssetIn;
nValue = nValueIn;
scriptPubKey = scriptPubKeyIn;
}
std::string CTxOut::ToString() const
{
return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, HexStr(scriptPubKey).substr(0, 30));
std::string strAsset;
if (nAsset.IsExplicit())
strAsset = strprintf("nAsset=%s, ", nAsset.GetAsset().GetHex());
if (nAsset.IsCommitment())
strAsset = std::string("nAsset=CONFIDENTIAL, ");
return strprintf("CTxOut(%snValue=%s, scriptPubKey=%s)", strAsset, (nValue.IsExplicit() ? strprintf("%d.%08d", nValue.GetAmount() / COIN, nValue.GetAmount() % COIN) : std::string("CONFIDENTIAL")), HexStr(scriptPubKey).substr(0, 30));
}
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {}
@ -89,7 +95,8 @@ uint256 CTransaction::GetWitnessOnlyHash() const
leaves.reserve(std::max(vin.size(), vout.size()));
/* Inputs */
for (size_t i = 0; i < vin.size(); ++i) {
const CTxInWitness& txinwit = witness.vtxinwit.size() <= i || vin[i].prevout.IsNull() ? CTxInWitness() : witness.vtxinwit[i];
// Input has no witness OR is null input(coinbase)
const CTxInWitness& txinwit = (witness.vtxinwit.size() <= i || vin[i].prevout.IsNull()) ? CTxInWitness() : witness.vtxinwit[i];
leaves.push_back(txinwit.GetHash());
}
uint256 hashIn = ComputeFastMerkleRoot(leaves);
@ -114,15 +121,19 @@ CTransaction::CTransaction(const CMutableTransaction& tx) :
CTransaction::CTransaction(CMutableTransaction&& tx) :
vin(std::move(tx.vin)), vout(std::move(tx.vout)), nVersion(tx.nVersion), nLockTime(tx.nLockTime), witness(std::move(tx.witness)),hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {}
CAmount CTransaction::GetValueOut() const
{
CAmount nValueOut = 0;
CAmountMap CTransaction::GetValueOutMap() const {
CAmountMap values;
for (const auto& tx_out : vout) {
nValueOut += tx_out.nValue;
if (!MoneyRange(tx_out.nValue) || !MoneyRange(nValueOut))
throw std::runtime_error(std::string(__func__) + ": value out of range");
if (tx_out.nValue.IsExplicit() && tx_out.nAsset.IsExplicit()) {
CAmountMap m;
m[tx_out.nAsset.GetAsset()] = tx_out.nValue.GetAmount();
values += m;
if (!MoneyRange(tx_out.nValue.GetAmount()) || !MoneyRange(values[tx_out.nAsset.GetAsset()]))
throw std::runtime_error(std::string(__func__) + ": value out of range");
}
}
return nValueOut;
return values;
}
unsigned int CTransaction::GetTotalSize() const

View file

@ -237,7 +237,9 @@ public:
class CTxOut
{
public:
CAmount nValue;
CConfidentialAsset nAsset;
CConfidentialValue nValue;
CConfidentialNonce nNonce;
CScript scriptPubKey;
CTxOut()
@ -245,30 +247,58 @@ public:
SetNull();
}
CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn);
CTxOut(const CConfidentialAsset& nAssetIn, const CConfidentialValue& nValueIn, CScript scriptPubKeyIn);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(nValue);
READWRITE(scriptPubKey);
if (g_con_elementsmode) {
READWRITE(nAsset);
READWRITE(nValue);
READWRITE(nNonce);
READWRITE(scriptPubKey);
} else {
CAmount value;
if (!ser_action.ForRead()) {
value = nValue.GetAmount();
}
READWRITE(value);
if (ser_action.ForRead()) {
nValue.SetToAmount(value);
}
READWRITE(scriptPubKey);
}
}
void SetNull()
{
nValue = -1;
nAsset.SetNull();
nValue.SetNull();
nNonce.SetNull();
scriptPubKey.clear();
}
bool IsNull() const
{
return (nValue == -1);
if (!g_con_elementsmode) {
// Ignore the asset and the nonce in compatibility mode.
return nValue.IsNull() && scriptPubKey.empty();
}
return nAsset.IsNull() && nValue.IsNull() && nNonce.IsNull() && scriptPubKey.empty();
}
bool IsFee() const {
return g_con_elementsmode && scriptPubKey == CScript()
&& nValue.IsExplicit() && nAsset.IsExplicit();
}
friend bool operator==(const CTxOut& a, const CTxOut& b)
{
return (a.nValue == b.nValue &&
return (a.nAsset == b.nAsset &&
a.nValue == b.nValue &&
a.nNonce == b.nNonce &&
a.scriptPubKey == b.scriptPubKey);
}
@ -482,7 +512,7 @@ public:
uint256 GetWitnessOnlyHash() const;
// Return sum of txouts.
CAmount GetValueOut() const;
CAmountMap GetValueOutMap() const;
// GetValueIn() is a method on CCoinsViewCache, because
// inputs must be known to compute value in.

View file

@ -16,9 +16,8 @@
uint256 CTxInWitness::GetHash() const
{
std::vector<uint256> leaves;
//TODO(rebase) CA/CT
//leaves.push_back(SerializeHash(vchIssuanceAmountRangeproof, SER_GETHASH, 0));
//leaves.push_back(SerializeHash(vchInflationKeysRangeproof, SER_GETHASH, 0));
leaves.push_back(SerializeHash(vchIssuanceAmountRangeproof, SER_GETHASH, 0));
leaves.push_back(SerializeHash(vchInflationKeysRangeproof, SER_GETHASH, 0));
leaves.push_back(SerializeHash(scriptWitness.stack, SER_GETHASH, 0));
leaves.push_back(SerializeHash(m_pegin_witness.stack, SER_GETHASH, 0));
return ComputeFastMerkleRoot(leaves);

View file

@ -23,8 +23,8 @@ public:
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action)
{
//READWRITE(vchIssuanceAmountRangeproof);
//READWRITE(vchInflationKeysRangeproof);
READWRITE(vchIssuanceAmountRangeproof);
READWRITE(vchInflationKeysRangeproof);
READWRITE(scriptWitness.stack);
READWRITE(m_pegin_witness.stack);
}
@ -105,15 +105,13 @@ public:
for (size_t n = 0; n < vtxinwit.size(); n++) {
READWRITE(vtxinwit[n]);
}
//TODO(rebase) CA/CT
//for (size_t n = 0; n < vtxoutwit.size(); n++) {
// READWRITE(vtxoutwit[n]);
//}
//TODO(stevenroose) re-enabled after testing
//if (IsNull()) {
// /* It's illegal to encode a witness when all vtxinwit and vtxoutwit entries are empty. */
// throw std::ios_base::failure("Superfluous witness record");
//}
for (size_t n = 0; n < vtxoutwit.size(); n++) {
READWRITE(vtxoutwit[n]);
}
if (IsNull()) {
/* It's illegal to encode a witness when all vtxinwit and vtxoutwit entries are empty. */
throw std::ios_base::failure("Superfluous witness record");
}
}
bool IsEmpty() const