Skip to content

Commit bb65b9e

Browse files
committed
[Refactoring] Additions/updates to pivx-tx.cpp
1 parent 2c944c7 commit bb65b9e

File tree

2 files changed

+169
-67
lines changed

2 files changed

+169
-67
lines changed

src/pivx-tx.cpp

Lines changed: 166 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -154,103 +154,207 @@ static void RegisterLoad(const std::string& strInput)
154154
RegisterSetJson(key, valStr);
155155
}
156156

157+
static CAmount ExtractAndValidateValue(const std::string& strValue)
158+
{
159+
CAmount value;
160+
if (!ParseMoney(strValue, value))
161+
throw std::runtime_error("invalid TX output value");
162+
return value;
163+
}
164+
157165
static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal)
158166
{
159-
int64_t newVersion = atoi64(cmdVal);
160-
if (newVersion < 1 || newVersion >= CTransaction::TxVersion::TOOHIGH)
161-
throw std::runtime_error("Invalid TX version requested");
167+
int64_t newVersion;
168+
if (!ParseInt64(cmdVal, &newVersion) || newVersion < 1 || newVersion >= CTransaction::TxVersion::TOOHIGH)
169+
throw std::runtime_error("Invalid TX version requested: '" + cmdVal + "'");
162170

163171
tx.nVersion = (int)newVersion;
164172
}
165173

166174
static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal)
167175
{
168-
int64_t newLocktime = atoi64(cmdVal);
169-
if (newLocktime < 0LL || newLocktime > 0xffffffffLL)
170-
throw std::runtime_error("Invalid TX locktime requested");
176+
int64_t newLocktime;
177+
if (!ParseInt64(cmdVal, &newLocktime) || newLocktime < 0LL || newLocktime > 0xffffffffLL)
178+
throw std::runtime_error("Invalid TX locktime requested: '" + cmdVal + "'");
171179

172180
tx.nLockTime = (unsigned int)newLocktime;
173181
}
174182

175183
static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInput)
176184
{
185+
std::vector<std::string> vStrInputParts;
186+
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
187+
177188
// separate TXID:VOUT in string
178-
size_t pos = strInput.find(':');
179-
if ((pos == std::string::npos) ||
180-
(pos == 0) ||
181-
(pos == (strInput.size() - 1)))
189+
if (vStrInputParts.size()<2)
182190
throw std::runtime_error("TX input missing separator");
183191

184192
// extract and validate TXID
185-
std::string strTxid = strInput.substr(0, pos);
186-
if ((strTxid.size() != 64) || !IsHex(strTxid))
193+
uint256 txid;
194+
if (!ParseHashStr(vStrInputParts[0], txid)) {
187195
throw std::runtime_error("invalid TX input txid");
188-
uint256 txid(strTxid);
196+
}
189197

190198
static const unsigned int minTxOutSz = 9;
191199
unsigned int nMaxSize = MAX_BLOCK_SIZE_LEGACY;
192200
static const unsigned int maxVout = nMaxSize / minTxOutSz;
193201

194202
// extract and validate vout
195-
std::string strVout = strInput.substr(pos + 1, std::string::npos);
196-
int vout = atoi(strVout);
197-
if ((vout < 0) || (vout > (int)maxVout))
198-
throw std::runtime_error("invalid TX input vout");
203+
const std::string& strVout = vStrInputParts[1];
204+
int64_t vout;
205+
if (!ParseInt64(strVout, &vout) || vout < 0 || vout > static_cast<int64_t>(maxVout))
206+
throw std::runtime_error("invalid TX input vout '" + strVout + "'");
207+
208+
// extract the optional sequence number
209+
uint32_t nSequenceIn = CTxIn::SEQUENCE_FINAL;
210+
if (vStrInputParts.size() > 2)
211+
nSequenceIn = std::stoul(vStrInputParts[2]);
199212

200213
// append to transaction input list
201-
CTxIn txin(txid, vout);
202-
tx.vin.push_back(txin);
214+
tx.vin.emplace_back(txid, vout, CScript(), nSequenceIn);
203215
}
204216

205217
static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strInput)
206218
{
207-
// separate VALUE:ADDRESS in string
208-
size_t pos = strInput.find(':');
209-
if ((pos == std::string::npos) ||
210-
(pos == 0) ||
211-
(pos == (strInput.size() - 1)))
212-
throw std::runtime_error("TX output missing separator");
219+
// Separate into VALUE:ADDRESS
220+
std::vector<std::string> vStrInputParts;
221+
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
213222

214-
// extract and validate VALUE
215-
std::string strValue = strInput.substr(0, pos);
216-
CAmount value;
217-
if (!ParseMoney(strValue, value))
218-
throw std::runtime_error("invalid TX output value");
223+
if (vStrInputParts.size() != 2)
224+
throw std::runtime_error("TX output missing or too many separators");
225+
226+
// Extract and validate VALUE
227+
CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
219228

220229
// extract and validate ADDRESS
221-
std::string strAddr = strInput.substr(pos + 1, std::string::npos);
230+
std::string strAddr = vStrInputParts[1];
222231
CTxDestination destination = DecodeDestination(strAddr);
223232
if (!IsValidDestination(destination)) {
224233
throw std::runtime_error("invalid TX output address");
225234
}
226235
CScript scriptPubKey = GetScriptForDestination(destination);
227236

228237
// construct TxOut, append to transaction output list
229-
CTxOut txout(value, scriptPubKey);
230-
tx.vout.push_back(txout);
238+
tx.vout.emplace_back(value, scriptPubKey);
239+
}
240+
241+
static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& strInput)
242+
{
243+
// Separate into VALUE:PUBKEY[:FLAGS]
244+
std::vector<std::string> vStrInputParts;
245+
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
246+
247+
if (vStrInputParts.size() < 2 || vStrInputParts.size() > 3)
248+
throw std::runtime_error("TX output missing or too many separators");
249+
250+
// Extract and validate VALUE
251+
CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
252+
253+
// Extract and validate PUBKEY
254+
CPubKey pubkey(ParseHex(vStrInputParts[1]));
255+
if (!pubkey.IsFullyValid())
256+
throw std::runtime_error("invalid TX output pubkey");
257+
CScript scriptPubKey = GetScriptForRawPubKey(pubkey);
258+
259+
// Extract and validate FLAGS
260+
bool bScriptHash = false;
261+
if (vStrInputParts.size() == 3) {
262+
std::string flags = vStrInputParts[2];
263+
bScriptHash = (flags.find('S') != std::string::npos);
264+
}
265+
if (bScriptHash) {
266+
// Get the ID for the script, and then construct a P2SH destination for it.
267+
scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
268+
}
269+
270+
// construct TxOut, append to transaction output list
271+
tx.vout.emplace_back(value, scriptPubKey);
272+
}
273+
274+
static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& strInput)
275+
{
276+
// Separate into VALUE:REQUIRED:NUMKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]
277+
std::vector<std::string> vStrInputParts;
278+
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
279+
280+
// Check that there are enough parameters
281+
if (vStrInputParts.size() < 3)
282+
throw std::runtime_error("Not enough multisig parameters");
283+
284+
// Extract and validate VALUE
285+
CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
286+
287+
// Extract REQUIRED
288+
uint32_t required = stoul(vStrInputParts[1]);
289+
290+
// Extract NUMKEYS
291+
uint32_t numkeys = stoul(vStrInputParts[2]);
292+
293+
// Validate there are the correct number of pubkeys
294+
if (vStrInputParts.size() < numkeys + 3)
295+
throw std::runtime_error("incorrect number of multisig pubkeys");
296+
297+
if (required < 1 || required > MAX_PUBKEYS_PER_MULTISIG || numkeys < 1 || numkeys > MAX_PUBKEYS_PER_MULTISIG || numkeys < required)
298+
throw std::runtime_error("multisig parameter mismatch. Required " \
299+
+ std::to_string(required) + " of " + std::to_string(numkeys) + "signatures.");
300+
301+
// extract and validate PUBKEYs
302+
std::vector<CPubKey> pubkeys;
303+
for(int pos = 1; pos <= int(numkeys); pos++) {
304+
CPubKey pubkey(ParseHex(vStrInputParts[pos + 2]));
305+
if (!pubkey.IsFullyValid())
306+
throw std::runtime_error("invalid TX output pubkey");
307+
pubkeys.push_back(pubkey);
308+
}
309+
310+
// Extract FLAGS
311+
bool bScriptHash = false;
312+
if (vStrInputParts.size() == numkeys + 4) {
313+
std::string flags = vStrInputParts.back();
314+
bScriptHash = (flags.find('S') != std::string::npos);
315+
}
316+
else if (vStrInputParts.size() > numkeys + 4) {
317+
// Validate that there were no more parameters passed
318+
throw std::runtime_error("Too many parameters");
319+
}
320+
321+
CScript scriptPubKey = GetScriptForMultisig(required, pubkeys);
322+
323+
if (bScriptHash) {
324+
if (scriptPubKey.size() > MAX_SCRIPT_ELEMENT_SIZE) {
325+
throw std::runtime_error(strprintf(
326+
"redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE));
327+
}
328+
// Get the ID for the script, and then construct a P2SH destination for it.
329+
scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
330+
}
331+
332+
// construct TxOut, append to transaction output list
333+
tx.vout.emplace_back(value, scriptPubKey);
231334
}
232335

233336
static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& strInput)
234337
{
235-
// separate VALUE:SCRIPT in string
236-
size_t pos = strInput.find(':');
237-
if ((pos == std::string::npos) ||
238-
(pos == 0))
338+
// separate VALUE:SCRIPT
339+
std::vector<std::string> vStrInputParts;
340+
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
341+
if (vStrInputParts.size() < 2)
239342
throw std::runtime_error("TX output missing separator");
240343

241-
// extract and validate VALUE
242-
std::string strValue = strInput.substr(0, pos);
243-
CAmount value;
244-
if (!ParseMoney(strValue, value))
245-
throw std::runtime_error("invalid TX output value");
344+
// Extract and validate VALUE
345+
CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
246346

247347
// extract and validate script
248-
std::string strScript = strInput.substr(pos + 1, std::string::npos);
249-
CScript scriptPubKey = ParseScript(strScript); // throws on err
348+
std::string strScript = vStrInputParts[1];
349+
CScript scriptPubKey = ParseScript(strScript);
350+
351+
if (scriptPubKey.size() > MAX_SCRIPT_SIZE) {
352+
throw std::runtime_error(strprintf(
353+
"script exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_SIZE));
354+
}
250355

251356
// construct TxOut, append to transaction output list
252-
CTxOut txout(value, scriptPubKey);
253-
tx.vout.push_back(txout);
357+
tx.vout.emplace_back(value, scriptPubKey);
254358
}
255359

256360
static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInIdx)
@@ -495,36 +599,34 @@ class Secp256k1Init
495599
static void MutateTx(CMutableTransaction& tx, const std::string& command, const std::string& commandVal)
496600
{
497601
std::unique_ptr<Secp256k1Init> ecc;
498-
if (command == "nversion")
602+
if (command == "nversion") {
499603
MutateTxVersion(tx, commandVal);
500-
else if (command == "locktime")
604+
} else if (command == "locktime") {
501605
MutateTxLocktime(tx, commandVal);
502-
503-
else if (command == "delin")
606+
} else if (command == "delin") {
504607
MutateTxDelInput(tx, commandVal);
505-
else if (command == "in")
608+
} else if (command == "in") {
506609
MutateTxAddInput(tx, commandVal);
507-
508-
else if (command == "delout")
610+
} else if (command == "delout") {
509611
MutateTxDelOutput(tx, commandVal);
510-
else if (command == "outaddr")
612+
} else if (command == "outaddr") {
511613
MutateTxAddOutAddr(tx, commandVal);
512-
else if (command == "outscript")
614+
} else if (command == "outpubkey") {
615+
MutateTxAddOutPubKey(tx, commandVal);
616+
} else if (command == "outmultisig") {
617+
MutateTxAddOutMultiSig(tx, commandVal);
618+
} else if (command == "outscript") {
513619
MutateTxAddOutScript(tx, commandVal);
514-
515-
else if (command == "sign"){
620+
} else if (command == "sign") {
516621
if (!ecc) { ecc.reset(new Secp256k1Init()); }
517622
MutateTxSign(tx, commandVal);
518-
}
519-
520-
else if (command == "load")
623+
} else if (command == "load") {
521624
RegisterLoad(commandVal);
522-
523-
else if (command == "set")
625+
} else if (command == "set") {
524626
RegisterSet(commandVal);
525-
526-
else
627+
} else {
527628
throw std::runtime_error("unknown command");
629+
}
528630
}
529631

530632
static void OutputTxJSON(const CTransaction& tx)

test/util/data/bitcoin-util-test.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,15 @@
7070
["-create",
7171
"outaddr=1"],
7272
"return_code": 1,
73-
"error_txt": "error: TX output missing separator",
73+
"error_txt": "error: TX output missing or too many separators",
7474
"description": "Malformed outaddr argument (no address specified). Expected to fail."
7575
},
7676
{ "exec": "./pivx-tx",
7777
"args":
7878
["-create",
7979
"outaddr=1:DPvuYbbib66zreC6HNNQgUKzF3jnMmxk71:garbage"],
8080
"return_code": 1,
81-
"error_txt": "error: invalid TX output address",
81+
"error_txt": "error: TX output missing or too many separators",
8282
"description": "Malformed outaddr argument (too many separators). Expected to fail."
8383
},
8484
{ "exec": "./pivx-tx",
@@ -145,4 +145,4 @@
145145
"output_cmp": "txcreatesign.hex",
146146
"description": "Creates a new transaction with a single input and a single output, and then signs the transaction"
147147
}
148-
]
148+
]

0 commit comments

Comments
 (0)