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

[WIP] Add computeissuanceasset RPC to compute asset id, tests #578

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendmany", 9 , "ignoreblindfail" },
{ "sendtoaddress", 9 , "ignoreblindfail" },
{ "createrawtransaction", 4, "output_assets" },
{ "computeissuanceasset", 1, "prevout_n" },
{ "computeissuanceasset", 2, "blinded" },

};
// clang-format on
Expand Down
48 changes: 48 additions & 0 deletions src/rpc/rawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2264,6 +2264,53 @@ UniValue rawreissueasset(const JSONRPCRequest& request)
return ret;
}

UniValue computeissuanceasset(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
throw std::runtime_error(
"computeissuanceasset prevout_hash prevout_n ( contract_hash blinded )\n"
"\nComputes asset id from previous transaction information as well as the contract hash.\n"
"\nArguments:\n"
"1. \"prevout_hash\" (string, required) Transaction prevout hash for issuance input.\n"
"2. \"prevout_n\" (numeric, required) Transaction prevout index for issaunce input.\n"
"3. \"contract_hash\" (string, optional, default=00..00) Contract hash that is put into issuance definition. Must be 32 bytes worth in hex string form."
"4. \"blinded\" (bool, optional, default=true) Whether or not the issuance is blinded. Only effects `token` derivation.\n"
"\nResult:\n"
" { (json object)\n"
" \"entropy\":\"<entropy>\" (string) Entropy of the asset type.\n"
" \"asset\":\"<asset>\", (string) Asset type for issuance if known.\n"
" \"token\":\"<token>\", (string) Token type for issuance.\n"
" }\n"
);

uint256 prevout_hash = ParseHashV(request.params[0], "prevout_hash");

int32_t prevout_n = request.params[1].get_int();

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

const bool blind_issuance = request.params[3].isNull() ? true : request.params[3].get_bool();

uint256 entropy;
CAsset asset;
CAsset token;
COutPoint prevout;
prevout.hash = prevout_hash;
prevout.n = prevout_n;
GenerateAssetEntropy(entropy, prevout, contract_hash);
CalculateAsset(asset, entropy);
CalculateReissuanceToken(token, entropy, blind_issuance);
UniValue obj(UniValue::VOBJ);
obj.pushKV("entropy", entropy.GetHex());
obj.pushKV("asset", asset.GetHex());
obj.pushKV("token", token.GetHex());
return obj;
}


// clang-format off
static const CRPCCommand commands[] =
Expand All @@ -2289,6 +2336,7 @@ static const CRPCCommand commands[] =
{ "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} },
{ "rawtransactions", "rawissueasset", &rawissueasset, {"transaction", "issuances"}},
{ "rawtransactions", "rawreissueasset", &rawreissueasset, {"transaction", "reissuances"}},
{ "rawtransactions", "computeissuanceasset", &computeissuanceasset, {"prevout_hash", "prevout_n", "contract_hash", "blinded"}},
};
// clang-format on

Expand Down
39 changes: 31 additions & 8 deletions test/functional/feature_issuance.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,26 +297,49 @@ def run_test(self):
id_set = set()

# First issue an asset with no argument
issued_tx = self.nodes[2].rawissueasset(funded_tx, [{"asset_amount":1, "asset_address":nonblind_addr}])[0]["hex"]
decode_tx = self.nodes[0].decoderawtransaction(issued_tx)
issued_tx = self.nodes[2].rawissueasset(funded_tx, [{"asset_amount":1, "asset_address":nonblind_addr}])[0]
decode_tx = self.nodes[0].decoderawtransaction(issued_tx["hex"])
id_set.add(decode_tx["vin"][0]["issuance"]["asset"])

# Recompute details minimal helper API
computed_asset = self.nodes[0].computeissuanceasset(decode_tx["vin"][0]["txid"], 0)
assert_equal(computed_asset["entropy"], issued_tx["entropy"])
assert_equal(computed_asset["asset"], issued_tx["asset"])
assert_equal(computed_asset["token"], issued_tx["token"])

# Again with 00..00 argument, which match the no-argument case
issued_tx = self.nodes[2].rawissueasset(funded_tx, [{"asset_amount":1, "asset_address":nonblind_addr, "contract_hash":"00"*32}])[0]["hex"]
decode_tx = self.nodes[0].decoderawtransaction(issued_tx)
issued_tx = self.nodes[2].rawissueasset(funded_tx, [{"asset_amount":1, "asset_address":nonblind_addr, "contract_hash":"00"*32}])[0]
decode_tx = self.nodes[0].decoderawtransaction(issued_tx["hex"])
id_set.add(decode_tx["vin"][0]["issuance"]["asset"])
assert_equal(len(id_set), 1)

# Test optional blind arg
computed_asset = self.nodes[0].computeissuanceasset(decode_tx["vin"][0]["txid"], 0, "00"*32, True)
assert_equal(computed_asset["entropy"], issued_tx["entropy"])
assert_equal(computed_asset["asset"], issued_tx["asset"])
assert_equal(computed_asset["token"], issued_tx["token"])

# Random contract string should again differ
issued_tx = self.nodes[2].rawissueasset(funded_tx, [{"asset_amount":1, "asset_address":nonblind_addr, "contract_hash":"deadbeef"*8}])[0]["hex"]
decode_tx = self.nodes[0].decoderawtransaction(issued_tx)
issued_tx = self.nodes[2].rawissueasset(funded_tx, [{"asset_amount":1, "asset_address":nonblind_addr, "contract_hash":"deadbeef"*8}])[0]
decode_tx = self.nodes[0].decoderawtransaction(issued_tx["hex"])
id_set.add(decode_tx["vin"][0]["issuance"]["asset"])
assert_equal(len(id_set), 2)
issued_tx = self.nodes[2].rawissueasset(funded_tx, [{"asset_amount":1, "asset_address":nonblind_addr, "contract_hash":"deadbeee"*8}])[0]["hex"]
decode_tx = self.nodes[0].decoderawtransaction(issued_tx)
issued_tx = self.nodes[2].rawissueasset(funded_tx, [{"asset_amount":1, "asset_address":nonblind_addr, "contract_hash":"deadbeee"*8}])[0]
decode_tx = self.nodes[0].decoderawtransaction(issued_tx["hex"])
id_set.add(decode_tx["vin"][0]["issuance"]["asset"])
assert_equal(len(id_set), 3)

computed_asset = self.nodes[0].computeissuanceasset(decode_tx["vin"][0]["txid"], 0, "deadbeee"*8)
assert_equal(computed_asset["entropy"], issued_tx["entropy"])
assert_equal(computed_asset["asset"], issued_tx["asset"])
assert_equal(computed_asset["token"], issued_tx["token"])

# Only change blind arg to false, token should mistmatch
unblind_computed = self.nodes[0].computeissuanceasset(decode_tx["vin"][0]["txid"], 0, "deadbeee"*8, False)
assert_equal(computed_asset["entropy"], issued_tx["entropy"])
assert_equal(computed_asset["asset"], issued_tx["asset"])
assert computed_asset["token"] != issued_tx["token"]

# Finally, append an issuance on top of an already-"issued" raw tx
# Same contract, different utxo being spent results in new asset type
# We also create a reissuance token to test reissuance with contract hash
Expand Down