Skip to content

Commit

Permalink
Add decimal denomination metadata to assets
Browse files Browse the repository at this point in the history
  • Loading branch information
Mixa84 committed Jul 12, 2024
1 parent 3889d98 commit c4187cc
Show file tree
Hide file tree
Showing 11 changed files with 49 additions and 17 deletions.
1 change: 1 addition & 0 deletions src/issuance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ void AppendInitialIssuance(CBlock& genesis_block, const COutPoint& prevout, cons
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);
txNew.vin[0].assetIssuance.denomination = 8;

for (unsigned int i = 0; i < asset_outputs; i++) {
txNew.vout.push_back(CTxOut(asset, CConfidentialValue(asset_values), issuance_destination));
Expand Down
1 change: 1 addition & 0 deletions src/primitives/confidential.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ std::string CAssetIssuance::ToString() const
str += strprintf(", amount=%s", (nAmount.IsExplicit() ? strprintf("%d.%08d", nAmount.GetAmount() / COIN, nAmount.GetAmount() % COIN) : std::string("CONFIDENTIAL")));
if (!nInflationKeys.IsNull())
str += strprintf(", inflationkeys=%s", (nInflationKeys.IsExplicit() ? strprintf("%d.%08d", nInflationKeys.GetAmount() / COIN, nInflationKeys.GetAmount() % COIN) : std::string("CONFIDENTIAL")));
str += std::to_string(denomination);
str += ")";
return str;
}
5 changes: 4 additions & 1 deletion src/primitives/confidential.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,13 +192,15 @@ class CAssetIssuance
// generating transaction.
CConfidentialValue nInflationKeys;

uint8_t denomination = 8;

public:
CAssetIssuance()
{
SetNull();
}

SERIALIZE_METHODS(CAssetIssuance, obj) { READWRITE(obj.assetBlindingNonce, obj.assetEntropy, obj.nAmount, obj.nInflationKeys); }
SERIALIZE_METHODS(CAssetIssuance, obj) { READWRITE(obj.assetBlindingNonce, obj.assetEntropy, obj.nAmount, obj.nInflationKeys, obj.denomination); }

void SetNull() { nAmount.SetNull(); nInflationKeys.SetNull(); }
bool IsNull() const { return (nAmount.IsNull() && nInflationKeys.IsNull()); }
Expand All @@ -209,6 +211,7 @@ class CAssetIssuance
a.assetEntropy == b.assetEntropy &&
a.nAmount == b.nAmount &&
a.nInflationKeys == b.nInflationKeys;
a.denomination == b.denomination;
}

friend bool operator!=(const CAssetIssuance& a, const CAssetIssuance& b)
Expand Down
1 change: 1 addition & 0 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "issueasset", 0, "assetamount" },
{ "issueasset", 1, "tokenamount" },
{ "issueasset", 2, "blind" },
{ "issueasset", 5, "denomination" },
{ "reissueasset", 1, "assetamount" },
{ "initpegoutwallet", 1, "bip32_counter"},
{ "rawblindrawtransaction", 1, "inputamountblinders" },
Expand Down
13 changes: 11 additions & 2 deletions src/rpc/rawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2886,12 +2886,13 @@ struct RawIssuanceDetails
uint256 entropy;
CAsset asset;
CAsset token;
uint8_t denomination = 8;
};

// Appends a single issuance to the first input that doesn't have one, and includes
// a single output per asset type in shuffled positions. Requires at least one output
// to exist (the fee output, which must be last).
void issueasset_base(CMutableTransaction& mtx, RawIssuanceDetails& issuance_details, const CAmount asset_amount, const CAmount token_amount, const CTxDestination& asset_dest, const CTxDestination& token_dest, const bool blind_issuance, const uint256& contract_hash)
void issueasset_base(CMutableTransaction& mtx, RawIssuanceDetails& issuance_details, const CAmount asset_amount, const CAmount token_amount, const CTxDestination& asset_dest, const CTxDestination& token_dest, const bool blind_issuance, const uint256& contract_hash, const uint8_t denomination)
{
CHECK_NONFATAL(asset_amount > 0 || token_amount > 0);
CHECK_NONFATAL(mtx.vout.size() > 0);
Expand Down Expand Up @@ -2923,6 +2924,8 @@ void issueasset_base(CMutableTransaction& mtx, RawIssuanceDetails& issuance_deta
issuance_details.entropy = entropy;
issuance_details.asset = asset;
issuance_details.token = token;
if (denomination)
issuance_details.denomination = denomination;

mtx.vin[issuance_input_index].assetIssuance.assetEntropy = contract_hash;

Expand Down Expand Up @@ -3007,6 +3010,7 @@ static RPCHelpMan rawissueasset()
{"token_address", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Destination address of generated reissuance tokens. Required if `token_amount` given."},
{"blind", RPCArg::Type::BOOL, RPCArg::Default{true}, "Whether to mark the issuance input for blinding or not. Only affects issuances with re-issuance tokens."},
{"contract_hash", RPCArg::Type::STR_HEX, RPCArg::Default{"0000...0000"}, "Contract hash that is put into issuance definition. Must be 32 bytes worth in hex string form. This will affect the asset id."},
{"denomination", RPCArg::Type::NUM, RPCArg::Default{8}, "Number of decimals to denominate the asset - default: 8\n"},
}
}
}
Expand Down Expand Up @@ -3108,9 +3112,14 @@ static RPCHelpMan rawissueasset()
contract_hash = ParseHashV(issuance_o["contract_hash"], "contract_hash");
}

uint8_t denomination = 0;
if (!issuance_o["denomination"].isNull()) {
denomination = issuance_o["denomination"].get_int();
}

RawIssuanceDetails details;

issueasset_base(mtx, details, asset_amount, token_amount, asset_dest, token_dest, blind_issuance, contract_hash);
issueasset_base(mtx, details, asset_amount, token_amount, asset_dest, token_dest, blind_issuance, contract_hash, denomination);
if (details.input_index == -1) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Failed to find enough blank inputs for listed issuances.");
}
Expand Down
24 changes: 16 additions & 8 deletions src/wallet/rpc/elements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1398,7 +1398,8 @@ RPCHelpMan issueasset()
{"tokenamount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "Amount of reissuance tokens to generate. Note that the amount is BTC-like, with 8 decimal places. These will allow you to reissue the asset if in wallet using `reissueasset`. These tokens are not consumed during reissuance."},
{"blind", RPCArg::Type::BOOL, RPCArg::Default{true}, "Whether to blind the issuances."},
{"contract_hash", RPCArg::Type::STR_HEX, RPCArg::Default{"0000...0000"}, "Contract hash that is put into issuance definition. Must be 32 bytes worth in hex string form. This will affect the asset id."},
{"fee_asset", RPCArg::Type::STR, RPCArg::DefaultHint{"not set, fall back to fee asset in existing transaction"}, "Asset to use to pay fees\n"},
{"fee_asset", RPCArg::Type::STR, RPCArg::DefaultHint{"not set, fall back to fee asset in existing transaction"}, "Asset to use to pay the fees"},
{"denomination", RPCArg::Type::NUM, RPCArg::Default{8}, "Number of decimals to denominate the asset - default: 8\n"},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
Expand Down Expand Up @@ -1469,14 +1470,19 @@ RPCHelpMan issueasset()
issuance_details.blind_issuance = blind_issuances;
issuance_details.contract_hash = contract_hash;
CCoinControl coin_control;
if (g_con_any_asset_fees && request.params.size() > 4) {
CAsset fee_asset = ::policyAsset;
std::string feeAssetString = request.params[4].get_str();
fee_asset = GetAssetFromString(feeAssetString);
if (fee_asset.IsNull()) {
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Unknown label and invalid asset hex for fee: %s", feeAssetString));
if (g_con_any_asset_fees) {
if (request.params.size() >= 5) {
CAsset fee_asset = ::policyAsset;
std::string feeAssetString = request.params[4].get_str();
fee_asset = GetAssetFromString(feeAssetString);
if (fee_asset.IsNull()) {
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Unknown label and invalid asset hex for fee: %s", feeAssetString));
}
coin_control.m_fee_asset = fee_asset;
}
if (request.params.size() >= 6) {
issuance_details.denomination = request.params[5].get_int();
}
coin_control.m_fee_asset = fee_asset;
}

CTransactionRef tx_ref = SendGenerationTransaction(GetScriptForDestination(asset_dest), asset_dest_blindpub, GetScriptForDestination(token_dest), token_dest_blindpub, nAmount, nTokens, &issuance_details, coin_control, pwallet);
Expand Down Expand Up @@ -1624,6 +1630,7 @@ RPCHelpMan listissuances()
{RPCResult::Type::STR_HEX, "token", "Token type for issuancen"},
{RPCResult::Type::NUM, "vin", "The input position of the issuance in the transaction"},
{RPCResult::Type::STR_AMOUNT, "assetamount", "The amount of asset issued. Is -1 if blinded and unknown to wallet"},
{RPCResult::Type::NUM, "denomination", "Asset decimal denomination"},
{RPCResult::Type::STR_AMOUNT, "tokenamount", "The reissuance token amount issued. Is -1 if blinded and unknown to wallet"},
{RPCResult::Type::BOOL, "isreissuance", "Whether this is a reissuance"},
{RPCResult::Type::STR_HEX, "assetblinds", "Blinding factor for asset amounts"},
Expand Down Expand Up @@ -1687,6 +1694,7 @@ RPCHelpMan listissuances()
}
CAmount iaamount = pcoin->GetIssuanceAmount(*pwallet, vinIndex, false);
item.pushKV("assetamount", (iaamount == -1 ) ? -1 : ValueFromAmount(iaamount));
item.pushKV("denomination", issuance.denomination);
item.pushKV("assetblinds", pcoin->GetIssuanceBlindingFactor(*pwallet, vinIndex, false).GetHex());
if (!asset_filter.IsNull() && asset_filter != asset) {
continue;
Expand Down
4 changes: 3 additions & 1 deletion src/wallet/spend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1410,6 +1410,8 @@ static bool CreateTransactionInternal(
}
}
}
// SEQUENTIA: Add denomination in the asset issuance
txNew.vin[0].assetIssuance.denomination = issuance_details->denomination;
// Asset being reissued with explicitly named asset/token
} else if (asset_index != -1) {
assert(reissuance_index != -1);
Expand Down Expand Up @@ -1693,7 +1695,7 @@ static bool CreateTransactionInternal(
}

if (g_con_any_asset_fees) {
CAmount nFeeRetValue = ExchangeRateMap::GetInstance().ConvertAmountToValue(nFeeRet, coin_selection_params.m_fee_asset).GetValue();
CAmount nFeeRetValue = ExchangeRateMap::GetInstance().ConvertAmountToValue(nFeeRet, coin_selection_params.m_fee_asset).GetValue();
if (nFeeRetValue > wallet.m_default_max_tx_fee) {
error = TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED);
return false;
Expand Down
3 changes: 3 additions & 0 deletions src/wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ struct IssuanceDetails {
CAsset reissuance_asset;
CAsset reissuance_token;
uint256 entropy;

// SEQUENTIA: Asset denomination
uint8_t denomination = 8;
};
//end ELEMENTS

Expand Down
4 changes: 2 additions & 2 deletions test/functional/example_elements_code_tutorial.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ def run_test(self):

expected_amt = {
'bitcoin': 0,
'8f1560e209f6bcac318569a935a0b2513c54f326ee4820ccd5b8c1b1b4632373': 0,
'4fa41f2929d4bf6975a55967d9da5b650b6b9bfddeae4d7b54b04394be328f7f': 99
'884071e106da92ff53b432340c8d160066502c781f50dfac5afc67459f946d6f': 0,
'daa8284c0d06cb02ef28b75ffa74c3b512131884d9c72e4f11dac634703d4fc4': 99
}
assert self.nodes[0].gettransaction(reissuance_txid)['amount'] == expected_amt

Expand Down
5 changes: 3 additions & 2 deletions test/functional/feature_txwitness.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,10 @@ def serialize(self):
block_witness_stuffed.vtx[0].vin[0].scriptSig = coinbase_orig.scriptSig
block_witness_stuffed.vtx[0].vin[0].nSequence = coinbase_orig.nSequence
block_witness_stuffed.vtx[0].vin[0].assetIssuance.nAmount.setToAmount(1)
block_witness_stuffed.vtx[0].vin[0].assetIssuance.denomination = 8
bad_coinbase_ser_size = len(block_witness_stuffed.vtx[0].vin[0].serialize())
# 32+32+9+1 should be serialized for each assetIssuance field
assert_equal(bad_coinbase_ser_size, coinbase_ser_size+32+32+9+1)
# 32+32+9+1+1 should be serialized for each assetIssuance field
assert_equal(bad_coinbase_ser_size, coinbase_ser_size+32+32+9+1+1)
assert not block_witness_stuffed.vtx[0].vin[0].assetIssuance.isNull()
assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, block_witness_stuffed.vtx[0].serialize().hex())

Expand Down
5 changes: 4 additions & 1 deletion test/functional/test_framework/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,13 +391,14 @@ def __repr__(self):
OUTPOINT_INDEX_MASK = 0x3fffffff

class CAssetIssuance():
__slots__ = ("assetBlindingNonce", "assetEntropy", "nAmount", "nInflationKeys")
__slots__ = ("assetBlindingNonce", "assetEntropy", "nAmount", "nInflationKeys", "denomination")

def __init__(self):
self.assetBlindingNonce = 0
self.assetEntropy = 0
self.nAmount = CTxOutValue()
self.nInflationKeys = CTxOutValue()
self.denomination = 8

def isNull(self):
return self.nAmount.isNull() and self.nInflationKeys.isNull()
Expand All @@ -409,13 +410,15 @@ def deserialize(self, f):
self.nAmount.deserialize(f)
self.nInflationKeys = CTxOutValue()
self.nInflationKeys.deserialize(f)
self.denomination = deser_compact_size(f)

def serialize(self):
r = b""
r += ser_uint256(self.assetBlindingNonce)
r += ser_uint256(self.assetEntropy)
r += self.nAmount.serialize()
r += self.nInflationKeys.serialize()
r += ser_compact_size(self.denomination)
return r

# serialization of asset issuance used in taproot sighash
Expand Down

0 comments on commit c4187cc

Please sign in to comment.