Merge #861: Make issuance fail for invalid addresses

425691623 Add test to make sure issuances to wrong addresses fail (Steven Roose)
1b8bf85d8 Make issuance fail for invalid addresses (Steven Roose)

Pull request description:

  Fixes https://github.com/ElementsProject/elements/issues/853.

Tree-SHA512: fd4e8dbede4c08a52c081abd57d2609ee7057b952b6de60ddc296574400f7488dd5ec33ac4874211cd029b8902a64741e52f1ed40b4a1e565274bacfd67e5075
This commit is contained in:
Steven Roose 2020-04-16 22:53:13 +01:00
commit f0d97cdf70
No known key found for this signature in database
GPG key ID: 2F2A88D7F8D68E87
2 changed files with 57 additions and 37 deletions

View file

@ -2808,13 +2808,10 @@ struct RawIssuanceDetails
// Appends a single issuance to the first input that doesn't have one, and includes // Appends a single issuance to the first input that doesn't have one, and includes
// a single output per asset type in shuffled positions. // a single output per asset type in shuffled positions.
void issueasset_base(CMutableTransaction& mtx, RawIssuanceDetails& issuance_details, const CAmount asset_amount, const CAmount token_amount, const std::string& asset_address_str, const std::string& token_address_str, 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)
{ {
CScript asset_script = GetScriptForDestination(asset_dest);
CTxDestination asset_address(DecodeDestination(asset_address_str)); CScript token_script = GetScriptForDestination(token_dest);
CTxDestination token_address(DecodeDestination(token_address_str));
CScript asset_destination = GetScriptForDestination(asset_address);
CScript token_destination = GetScriptForDestination(token_address);
// Find an input with no issuance field // Find an input with no issuance field
size_t issuance_input_index = 0; size_t issuance_input_index = 0;
@ -2848,29 +2845,30 @@ void issueasset_base(CMutableTransaction& mtx, RawIssuanceDetails& issuance_deta
int asset_place = GetRandInt(mtx.vout.size()-1); int asset_place = GetRandInt(mtx.vout.size()-1);
int token_place = GetRandInt(mtx.vout.size()); // Don't bias insertion int token_place = GetRandInt(mtx.vout.size()); // Don't bias insertion
CTxOut asset_out(asset, asset_amount, asset_destination); assert(asset_amount > 0 || token_amount > 0);
// If blinded address, insert the pubkey into the nonce field for later substitution by blinding if (asset_amount > 0) {
if (IsBlindDestination(asset_address)) { CTxOut asset_out(asset, asset_amount, asset_script);
CPubKey asset_blind = GetDestinationBlindingKey(asset_address); // If blinded address, insert the pubkey into the nonce field for later substitution by blinding
asset_out.nNonce.vchCommitment = std::vector<unsigned char>(asset_blind.begin(), asset_blind.end()); if (IsBlindDestination(asset_dest)) {
CPubKey asset_blind = GetDestinationBlindingKey(asset_dest);
asset_out.nNonce.vchCommitment = std::vector<unsigned char>(asset_blind.begin(), asset_blind.end());
}
mtx.vout.insert(mtx.vout.begin()+asset_place, asset_out);
} }
// Explicit 0 is represented by a null value, don't set to non-null in that case // Explicit 0 is represented by a null value, don't set to non-null in that case
if (blind_issuance || asset_amount != 0) { if (blind_issuance || asset_amount != 0) {
mtx.vin[issuance_input_index].assetIssuance.nAmount = asset_amount; mtx.vin[issuance_input_index].assetIssuance.nAmount = asset_amount;
} }
// Don't make zero value output(impossible by consensus)
if (asset_amount > 0) {
mtx.vout.insert(mtx.vout.begin()+asset_place, asset_out);
}
CTxOut token_out(token, token_amount, token_destination);
// If blinded address, insert the pubkey into the nonce field for later substitution by blinding
if (IsBlindDestination(token_address)) {
CPubKey token_blind = GetDestinationBlindingKey(token_address);
token_out.nNonce.vchCommitment = std::vector<unsigned char>(token_blind.begin(), token_blind.end());
}
// Explicit 0 is represented by a null value, don't set to non-null in that case
if (token_amount > 0) { if (token_amount > 0) {
CTxOut token_out(token, token_amount, token_script);
// If blinded address, insert the pubkey into the nonce field for later substitution by blinding
if (IsBlindDestination(token_dest)) {
CPubKey token_blind = GetDestinationBlindingKey(token_dest);
token_out.nNonce.vchCommitment = std::vector<unsigned char>(token_blind.begin(), token_blind.end());
}
mtx.vin[issuance_input_index].assetIssuance.nInflationKeys = token_amount; mtx.vin[issuance_input_index].assetIssuance.nInflationKeys = token_amount;
mtx.vout.insert(mtx.vout.begin()+token_place, token_out); mtx.vout.insert(mtx.vout.begin()+token_place, token_out);
} }
@ -2878,11 +2876,9 @@ void issueasset_base(CMutableTransaction& mtx, RawIssuanceDetails& issuance_deta
// Appends a single reissuance to the specified input if none exists, // Appends a single reissuance to the specified input if none exists,
// and the corresponding output in a shuffled position. Errors otherwise. // and the corresponding output in a shuffled position. Errors otherwise.
void reissueasset_base(CMutableTransaction& mtx, int& issuance_input_index, const CAmount asset_amount, const std::string& asset_address_str, const uint256& asset_blinder, const uint256& entropy) void reissueasset_base(CMutableTransaction& mtx, int& issuance_input_index, const CAmount asset_amount, const CTxDestination& asset_dest, const uint256& asset_blinder, const uint256& entropy)
{ {
CScript asset_script = GetScriptForDestination(asset_dest);
CTxDestination asset_address(DecodeDestination(asset_address_str));
CScript asset_destination = GetScriptForDestination(asset_address);
// Check if issuance already exists, error if already exists // Check if issuance already exists, error if already exists
if ((size_t)issuance_input_index >= mtx.vin.size() || !mtx.vin[issuance_input_index].assetIssuance.IsNull()) { if ((size_t)issuance_input_index >= mtx.vin.size() || !mtx.vin[issuance_input_index].assetIssuance.IsNull()) {
@ -2901,10 +2897,10 @@ void reissueasset_base(CMutableTransaction& mtx, int& issuance_input_index, cons
assert(mtx.vout.size() >= 1); assert(mtx.vout.size() >= 1);
int asset_place = GetRandInt(mtx.vout.size()-1); int asset_place = GetRandInt(mtx.vout.size()-1);
CTxOut asset_out(asset, asset_amount, asset_destination); CTxOut asset_out(asset, asset_amount, asset_script);
// If blinded address, insert the pubkey into the nonce field for later substitution by blinding // If blinded address, insert the pubkey into the nonce field for later substitution by blinding
if (IsBlindDestination(asset_address)) { if (IsBlindDestination(asset_dest)) {
CPubKey asset_blind = GetDestinationBlindingKey(asset_address); CPubKey asset_blind = GetDestinationBlindingKey(asset_dest);
asset_out.nNonce.vchCommitment = std::vector<unsigned char>(asset_blind.begin(), asset_blind.end()); asset_out.nNonce.vchCommitment = std::vector<unsigned char>(asset_blind.begin(), asset_blind.end());
} }
assert(asset_amount > 0); assert(asset_amount > 0);
@ -2957,9 +2953,6 @@ UniValue rawissueasset(const JSONRPCRequest& request)
UniValue issuances = request.params[1].get_array(); UniValue issuances = request.params[1].get_array();
std::string asset_address_str = "";
std::string token_address_str = "";
UniValue ret(UniValue::VARR); UniValue ret(UniValue::VARR);
// Count issuances, only append hex to final one // Count issuances, only append hex to final one
@ -2969,6 +2962,9 @@ UniValue rawissueasset(const JSONRPCRequest& request)
const UniValue& issuance = issuances[idx]; const UniValue& issuance = issuances[idx];
const UniValue& issuance_o = issuance.get_obj(); const UniValue& issuance_o = issuance.get_obj();
CTxDestination asset_dest = CNoDestination();
CTxDestination token_dest = CNoDestination();
CAmount asset_amount = 0; CAmount asset_amount = 0;
const UniValue& asset_amount_uni = issuance_o["asset_amount"]; const UniValue& asset_amount_uni = issuance_o["asset_amount"];
if (asset_amount_uni.isNum()) { if (asset_amount_uni.isNum()) {
@ -2980,7 +2976,10 @@ UniValue rawissueasset(const JSONRPCRequest& request)
if (!asset_address_uni.isStr()) { if (!asset_address_uni.isStr()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing corresponding asset_address"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing corresponding asset_address");
} }
asset_address_str = asset_address_uni.get_str(); asset_dest = DecodeDestination(asset_address_uni.get_str());
if (boost::get<CNoDestination>(&asset_dest)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid asset address provided: %s", asset_address_uni.get_str()));
}
} }
CAmount token_amount = 0; CAmount token_amount = 0;
@ -2994,8 +2993,12 @@ UniValue rawissueasset(const JSONRPCRequest& request)
if (!token_address_uni.isStr()) { if (!token_address_uni.isStr()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing corresponding token_address"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing corresponding token_address");
} }
token_address_str = token_address_uni.get_str(); token_dest = DecodeDestination(token_address_uni.get_str());
if (boost::get<CNoDestination>(&token_dest)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid token address provided: %s", token_address_uni.get_str()));
}
} }
if (asset_amount == 0 && token_amount == 0) { if (asset_amount == 0 && token_amount == 0) {
throw JSONRPCError(RPC_TYPE_ERROR, "Issuance must have one non-zero component"); throw JSONRPCError(RPC_TYPE_ERROR, "Issuance must have one non-zero component");
} }
@ -3012,7 +3015,7 @@ UniValue rawissueasset(const JSONRPCRequest& request)
RawIssuanceDetails details; RawIssuanceDetails details;
issueasset_base(mtx, details, asset_amount, token_amount, asset_address_str, token_address_str, blind_issuance, contract_hash); issueasset_base(mtx, details, asset_amount, token_amount, asset_dest, token_dest, blind_issuance, contract_hash);
if (details.input_index == -1) { if (details.input_index == -1) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Failed to find enough blank inputs for listed issuances."); throw JSONRPCError(RPC_INVALID_PARAMETER, "Failed to find enough blank inputs for listed issuances.");
} }
@ -3096,7 +3099,10 @@ UniValue rawreissueasset(const JSONRPCRequest& request)
if (!asset_address_uni.isStr()) { if (!asset_address_uni.isStr()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Reissuance missing asset_address"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Reissuance missing asset_address");
} }
std::string asset_address_str = asset_address_uni.get_str(); CTxDestination asset_dest = DecodeDestination(asset_address_uni.get_str());
if (boost::get<CNoDestination>(&asset_dest)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid asset address provided: %s", asset_address_uni.get_str()));
}
int input_index = -1; int input_index = -1;
const UniValue& input_index_o = issuance_o["input_index"]; const UniValue& input_index_o = issuance_o["input_index"];
@ -3113,7 +3119,7 @@ UniValue rawreissueasset(const JSONRPCRequest& request)
uint256 entropy = ParseHashV(issuance_o["entropy"], "entropy"); uint256 entropy = ParseHashV(issuance_o["entropy"], "entropy");
reissueasset_base(mtx, input_index, asset_amount, asset_address_str, asset_blinder, entropy); reissueasset_base(mtx, input_index, asset_amount, asset_dest, asset_blinder, entropy);
if (input_index == -1) { if (input_index == -1) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Selected transaction input already has issuance data."); throw JSONRPCError(RPC_INVALID_PARAMETER, "Selected transaction input already has issuance data.");
} }

View file

@ -290,6 +290,20 @@ class IssuanceTest(BitcoinTestFramework):
# Default "blind" value is true, omitting explicit argument for last # Default "blind" value is true, omitting explicit argument for last
process_raw_issuance(self.nodes[0], [{"asset_amount": 1, "asset_address": nonblind_addr, "token_amount":2, "token_address":blind_addr, "blind":True}, {"asset_amount":3, "asset_address":nonblind_addr, "blind":True}, {"asset_amount":4, "asset_address":nonblind_addr, "token_amount":5, "token_address":blind_addr, "blind":True}, {"asset_amount":6, "asset_address":nonblind_addr, "token_amount":7, "token_address":blind_addr, "blind":True}, {"asset_amount":8, "asset_address":nonblind_addr, "token_amount":9, "token_address":blind_addr}]) process_raw_issuance(self.nodes[0], [{"asset_amount": 1, "asset_address": nonblind_addr, "token_amount":2, "token_address":blind_addr, "blind":True}, {"asset_amount":3, "asset_address":nonblind_addr, "blind":True}, {"asset_amount":4, "asset_address":nonblind_addr, "token_amount":5, "token_address":blind_addr, "blind":True}, {"asset_amount":6, "asset_address":nonblind_addr, "token_amount":7, "token_address":blind_addr, "blind":True}, {"asset_amount":8, "asset_address":nonblind_addr, "token_amount":9, "token_address":blind_addr}])
# Make sure that invalid addresses are rejected.
valid_addr = self.nodes[0].getnewaddress()
raw_tx = self.nodes[0].createrawtransaction([], {valid_addr: Decimal("1")})
funded_tx = raw_tx #self.nodes[0].fundrawtransaction(raw_tx, {"feeRate": Decimal('0.00050000')})['hex']
assert_raises_rpc_error(-8, "Invalid asset address provided: foobar",
self.nodes[0].rawissueasset, funded_tx, [{"asset_amount": 1, "asset_address": "foobar"}])
assert_raises_rpc_error(-8, "Invalid token address provided: foobar",
self.nodes[0].rawissueasset, funded_tx, [{"token_amount": 1, "token_address": "foobar"}])
# Also test for missing value.
assert_raises_rpc_error(-8, "Invalid parameter, missing corresponding asset_address",
self.nodes[0].rawissueasset, funded_tx, [{"asset_amount": 1, "token_address": valid_addr}])
assert_raises_rpc_error(-8, "Invalid parameter, missing corresponding token_address",
self.nodes[0].rawissueasset, funded_tx, [{"token_amount": 1, "asset_address": valid_addr}])
# Make sure contract hash is being interpreted as expected, resulting in different asset ids # Make sure contract hash is being interpreted as expected, resulting in different asset ids
raw_tx = self.nodes[0].createrawtransaction([], {nonblind_addr:self.nodes[0].getbalance()['bitcoin']-1}) raw_tx = self.nodes[0].createrawtransaction([], {nonblind_addr:self.nodes[0].getbalance()['bitcoin']-1})
funded_tx = self.nodes[0].fundrawtransaction(raw_tx)['hex'] funded_tx = self.nodes[0].fundrawtransaction(raw_tx)['hex']