Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decimal denomination to assets #41

Merged
merged 4 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 += strprintf("%d", 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
21 changes: 16 additions & 5 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 @@ -3005,8 +3008,9 @@ static RPCHelpMan rawissueasset()
{"asset_address", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Destination address of generated asset. Required if `asset_amount` given."},
{"token_amount", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED_NAMED_ARG, "Amount of reissuance token to generate, if any."},
{"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."},
{"blind", RPCArg::Type::BOOL, RPCArg::Default{false}, "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 @@ -3099,18 +3103,25 @@ static RPCHelpMan rawissueasset()
}

// If we have issuances, check if reissuance tokens will be generated via blinding path
const UniValue blind_uni = issuance_o["blind"];
const bool blind_issuance = !blind_uni.isBool() || blind_uni.get_bool();
bool blind_issuance = false;
if (!issuance_o["blind"].isNull()) {
blind_issuance = issuance_o["blind"].get_bool();
}

// Check for optional contract to hash into definition
uint256 contract_hash;
if (!issuance_o["contract_hash"].isNull()) {
contract_hash = ParseHashV(issuance_o["contract_hash"], "contract_hash");
}

uint8_t denomination = 8;
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
33 changes: 22 additions & 11 deletions src/wallet/rpc/elements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1396,9 +1396,10 @@ RPCHelpMan issueasset()
{
{"assetamount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "Amount of asset to generate. Note that the amount is BTC-like, with 8 decimal places."},
{"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."},
{"blind", RPCArg::Type::BOOL, RPCArg::Default{false}, "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 @@ -1432,11 +1433,14 @@ RPCHelpMan issueasset()
throw JSONRPCError(RPC_TYPE_ERROR, "Issuance must have one non-zero component");
}

bool blind_issuances = request.params.size() < 3 || request.params[2].get_bool();
bool blind_issuances = false;
if (!request.params[2].isNull()) {
blind_issuances = request.params[2].get_bool();
}

// Check for optional contract to hash into definition
uint256 contract_hash;
if (request.params.size() >= 4) {
if (!request.params[3].isNull()) {
contract_hash = ParseHashV(request.params[3], "contract_hash");
}

Expand Down Expand Up @@ -1469,14 +1473,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[4].isNull()) {
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[5].isNull()) {
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 +1633,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 +1697,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
21 changes: 13 additions & 8 deletions test/functional/feature_any_asset_fee.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,26 @@ def init(self):
assert self.nodes[0].dumpassetlabels() == {'gasset': 'b2e15d0d7a0c94e4e2ce0fe6e8691b9e451377f6e46e8045a86f7c4b5d4f0f23'}
assert self.nodes[0].getfeeexchangerates() == { 'gasset': 100000000 }

self.node0_address = self.nodes[0].getnewaddress()
self.node1_address = self.nodes[1].getnewaddress()

self.issue_amount = Decimal('100')
self.issuance = self.nodes[0].issueasset(self.issue_amount, 1)
self.issuance = self.nodes[0].issueasset(
assetamount = self.issue_amount,
tokenamount = 1,
blind = False,
denomination = 2)
self.asset = self.issuance['asset']
#token = issuance['token']
self.issuance_txid = self.issuance['txid']
self.issuance_vin = self.issuance['vin']

assert len(self.nodes[0].listissuances()) == 2 # asset & reisuance token
self.generatetoaddress(self.nodes[0], 1, self.node0_address) # confirm the tx

self.nodes[0].generatetoaddress(1, self.nodes[0].getnewaddress(), invalid_call=False) # confirm the tx
assert len(self.nodes[0].listissuances()) == 2 # asset & devcoin
issuances = self.nodes[0].listissuances()
assert (issuances[0]['denomination'] == 2 and issuances[1]['denomination'] == 8) \
or (issuances[0]['denomination'] == 2 and issuances[1]['denomination'] == 8)

self.issuance_addr = self.nodes[0].gettransaction(self.issuance_txid)['details'][0]['address']
self.nodes[1].importaddress(self.issuance_addr)
Expand All @@ -56,11 +66,6 @@ def init(self):
assert (issuances[0]['tokenamount'] == 1 and issuances[0]['assetamount'] == self.issue_amount) \
or (issuances[1]['tokenamount'] == 1 and issuances[1]['assetamount'] == self.issue_amount)

self.node0_address = self.nodes[0].getnewaddress()
self.node0_nonct_address = self.nodes[0].getaddressinfo(self.node0_address)["unconfidential"]
self.node1_address = self.nodes[1].getnewaddress()
self.node1_nonct_address = self.nodes[1].getaddressinfo(self.node1_address)["unconfidential"]

new_rates = { "gasset": 100000000, self.asset: 100000000 }
self.nodes[0].setfeeexchangerates(new_rates)
assert self.nodes[0].getfeeexchangerates() == new_rates
Expand Down
2 changes: 1 addition & 1 deletion test/functional/feature_any_asset_fee_rates.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def init(self):
self.gasset = 'b2e15d0d7a0c94e4e2ce0fe6e8691b9e451377f6e46e8045a86f7c4b5d4f0f23'

self.issue_amount = Decimal('100')
self.issuance = self.nodes[0].issueasset(self.issue_amount, 1)
self.issuance = self.nodes[0].issueasset(self.issue_amount, 1, False)
self.asset = self.issuance['asset']
self.issuance_txid = self.issuance['txid']
self.issuance_vin = self.issuance['vin']
Expand Down
2 changes: 1 addition & 1 deletion test/functional/feature_any_asset_fee_rbf.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def init(self):

self.gasset = self.nodes[0].dumpassetlabels()['gasset']
self.issue_amount = Decimal('100')
self.issuance = self.nodes[0].issueasset(self.issue_amount, 1)
self.issuance = self.nodes[0].issueasset(self.issue_amount, 1, False)
self.asset = self.issuance['asset']
#token = issuance['token']
self.issuance_txid = self.issuance['txid']
Expand Down
3 changes: 1 addition & 2 deletions test/functional/feature_any_asset_fee_scenarios.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def init(self):
self.node1_address = self.nodes[1].getnewaddress()

self.issue_amount1 = Decimal('100')
self.issuance1 = self.nodes[0].issueasset(self.issue_amount1, 1)
self.issuance1 = self.nodes[0].issueasset(self.issue_amount1, 1, False)
self.asset1 = self.issuance1['asset']
self.issuance_txid1 = self.issuance1['txid']
self.issuance_vin1 = self.issuance1['vin']
Expand Down Expand Up @@ -74,7 +74,6 @@ def init(self):
assetamount=self.issue_amount2,
tokenamount=1,
blind=False,
contract_hash=self.asset1,
fee_asset = self.asset1)
self.asset2 = self.issuance2['asset']
self.issuance_txid2 = self.issuance2['txid']
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
Loading