Skip to content

Commit

Permalink
Initialize errors when they occur for improved error traces (#291)
Browse files Browse the repository at this point in the history
  • Loading branch information
jdtzmn authored Mar 9, 2021
1 parent eea58c7 commit 3c7499f
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 57 deletions.
6 changes: 3 additions & 3 deletions src/convert.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const MICROALGOS_TO_ALGOS_RATIO = 1e6;
const ERROR_INVALID_MICROALGOS = new Error("Microalgos should be positive and less than 2^53 - 1.");
const INVALID_MICROALGOS_ERROR_MSG = "Microalgos should be positive and less than 2^53 - 1.";

/**
* microalgosToAlgos converts microalgos to algos
Expand All @@ -8,7 +8,7 @@ const ERROR_INVALID_MICROALGOS = new Error("Microalgos should be positive and le
*/
function microalgosToAlgos(microalgos) {
if (microalgos < 0 || !Number.isSafeInteger(microalgos)){
throw ERROR_INVALID_MICROALGOS;
throw new Error(INVALID_MICROALGOS_ERROR_MSG);
}
return microalgos/MICROALGOS_TO_ALGOS_RATIO
}
Expand All @@ -26,5 +26,5 @@ function algosToMicroalgos(algos) {
module.exports = {
microalgosToAlgos,
algosToMicroalgos,
ERROR_INVALID_MICROALGOS,
INVALID_MICROALGOS_ERROR_MSG,
};
38 changes: 19 additions & 19 deletions src/encoding/address.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ const ALGORAND_ZERO_ADDRESS_STRING = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
// Convert "MultisigAddr" UTF-8 to byte array
const MULTISIG_PREIMG2ADDR_PREFIX = new Uint8Array([77, 117, 108, 116, 105, 115, 105, 103, 65, 100, 100, 114]);

const MALFORMED_ADDRESS_ERROR = new Error("address seems to be malformed");
const CHECKSUM_ADDRESS_ERROR = new Error("wrong checksum for address");
const INVALID_MSIG_VERSION = new Error("invalid multisig version");
const INVALID_MSIG_THRESHOLD = new Error("bad multisig threshold");
const INVALID_MSIG_PK = new Error("bad multisig public key - wrong length");
const UNEXPECTED_PK_LEN = new Error("nacl public key length is not 32 bytes");
const MALFORMED_ADDRESS_ERROR_MSG = "address seems to be malformed";
const CHECKSUM_ADDRESS_ERROR_MSG = "wrong checksum for address";
const INVALID_MSIG_VERSION_ERROR_MSG = "invalid multisig version";
const INVALID_MSIG_THRESHOLD_ERROR_MSG = "bad multisig threshold";
const INVALID_MSIG_PK_ERROR_MSG = "bad multisig public key - wrong length";
const UNEXPECTED_PK_LEN_ERROR_MSG = "nacl public key length is not 32 bytes";

/**
* isValidAddress checks if a string is a valid Algorand address.
Expand All @@ -39,13 +39,13 @@ function isValidAddress(address) {
*/
function decodeAddress(address) {
if (!(typeof address === "string" || address instanceof String) || address.length !== ALGORAND_ADDRESS_LENGTH)
throw MALFORMED_ADDRESS_ERROR;
throw new Error(MALFORMED_ADDRESS_ERROR_MSG);

//try to decode
let decoded = base32.decode.asBytes(address);

// Sanity check
if (decoded.length !== ALGORAND_ADDRESS_BYTE_LENGTH) throw MALFORMED_ADDRESS_ERROR;
if (decoded.length !== ALGORAND_ADDRESS_BYTE_LENGTH) throw new Error(MALFORMED_ADDRESS_ERROR_MSG);

// Find publickey and checksum
let pk = new Uint8Array(decoded.slice(0, ALGORAND_ADDRESS_BYTE_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH));
Expand All @@ -55,7 +55,7 @@ function decodeAddress(address) {
let checksum = nacl.genericHash(pk).slice(nacl.HASH_BYTES_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH,nacl.HASH_BYTES_LENGTH);

// Check if the checksum and the address are equal
if(!utils.arrayEqual(checksum, cs)) throw CHECKSUM_ADDRESS_ERROR;
if(!utils.arrayEqual(checksum, cs)) throw new Error(CHECKSUM_ADDRESS_ERROR_MSG);

return {"publicKey": pk, "checksum": cs}
}
Expand Down Expand Up @@ -85,22 +85,22 @@ function encodeAddress(address) {
function fromMultisigPreImg({version, threshold, pks}) {
if (version !== 1 || version > 255 || version < 0) {
// ^ a tad redundant, but in case in the future version != 1, still check for uint8
throw INVALID_MSIG_VERSION;
throw new Error(INVALID_MSIG_VERSION_ERROR_MSG);
}
if (threshold === 0 || pks.length === 0 || threshold > pks.length || threshold > 255) {
throw INVALID_MSIG_THRESHOLD;
throw new Error(INVALID_MSIG_THRESHOLD_ERROR_MSG);
}
let pkLen = ALGORAND_ADDRESS_BYTE_LENGTH - ALGORAND_CHECKSUM_BYTE_LENGTH;
if (pkLen !== nacl.PUBLIC_KEY_LENGTH) {
throw UNEXPECTED_PK_LEN;
throw new Error(UNEXPECTED_PK_LEN_ERROR_MSG);
}
let merged = new Uint8Array(MULTISIG_PREIMG2ADDR_PREFIX.length + 2 + pkLen*pks.length);
merged.set(MULTISIG_PREIMG2ADDR_PREFIX, 0);
merged.set([version], MULTISIG_PREIMG2ADDR_PREFIX.length);
merged.set([threshold], MULTISIG_PREIMG2ADDR_PREFIX.length + 1);
for (var i = 0; i < pks.length; i++) {
if (pks[i].length !== pkLen) {
throw INVALID_MSIG_PK;
throw new Error(INVALID_MSIG_PK_ERROR_MSG);
}
merged.set(pks[i], MULTISIG_PREIMG2ADDR_PREFIX.length + 2 + i*pkLen);
}
Expand All @@ -127,11 +127,11 @@ module.exports = {
encodeAddress,
fromMultisigPreImg,
fromMultisigPreImgAddrs,
MALFORMED_ADDRESS_ERROR,
CHECKSUM_ADDRESS_ERROR,
INVALID_MSIG_VERSION,
INVALID_MSIG_THRESHOLD,
INVALID_MSIG_PK,
UNEXPECTED_PK_LEN,
MALFORMED_ADDRESS_ERROR_MSG,
CHECKSUM_ADDRESS_ERROR_MSG,
INVALID_MSIG_VERSION_ERROR_MSG,
INVALID_MSIG_THRESHOLD_ERROR_MSG,
INVALID_MSIG_PK_ERROR_MSG,
UNEXPECTED_PK_LEN_ERROR_MSG,
ALGORAND_ZERO_ADDRESS_STRING
};
10 changes: 6 additions & 4 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ let Indexer = indexer.IndexerClient
const SIGN_BYTES_PREFIX = Buffer.from([77, 88]); // "MX"

// Errors
const ERROR_MULTISIG_BAD_SENDER = new Error("The transaction sender address and multisig preimage do not match.");
const MULTISIG_BAD_SENDER_ERROR_MSG = "The transaction sender address and multisig preimage do not match.";

/**
* signTransaction takes an object with either payment or key registration fields and
Expand Down Expand Up @@ -115,7 +115,7 @@ function signMultisigTransaction(txn, {version, threshold, addrs}, sk) {
let expectedFromRaw = address.fromMultisigPreImgAddrs({version, threshold, addrs});
if (txn.hasOwnProperty('from')) {
if ((txn.from !== expectedFromRaw) && (address.encodeAddress(txn.from.publicKey) !== expectedFromRaw)) {
throw ERROR_MULTISIG_BAD_SENDER;
throw new Error(MULTISIG_BAD_SENDER_ERROR_MSG);
}
} else {
txn.from = expectedFromRaw;
Expand Down Expand Up @@ -230,8 +230,10 @@ module.exports = {
mergeMultisigTransactions,
signMultisigTransaction,
multisigAddress,
ERROR_MULTISIG_BAD_SENDER,
ERROR_INVALID_MICROALGOS: convert.ERROR_INVALID_MICROALGOS,
MULTISIG_BAD_SENDER_ERROR_MSG,
ERROR_MULTISIG_BAD_SENDER: new Error(MULTISIG_BAD_SENDER_ERROR_MSG),
INVALID_MICROALGOS_ERROR_MSG: convert.ERROR_INVALID_MICROALGOS,
ERROR_INVALID_MICROALGOS: new Error(convert.ERROR_INVALID_MICROALGOS),
microalgosToAlgos: convert.microalgosToAlgos,
algosToMicroalgos: convert.algosToMicroalgos,
computeGroupID: group.computeGroupID,
Expand Down
16 changes: 8 additions & 8 deletions src/mnemonic/mnemonic.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ const english = require("./wordlists/english");
const nacl = require("../nacl/naclWrappers");
const address = require("../encoding/address");

const ERROR_FAIL_TO_DECODE_MNEMONIC = Error('failed to decode mnemonic');
const ERROR_NOT_IN_WORDS_LIST = Error('the mnemonic contains a word that is not in the wordlist');
const FAIL_TO_DECODE_MNEMONIC_ERROR_MSG = 'failed to decode mnemonic';
const NOT_IN_WORDS_LIST_ERROR_MSG = 'the mnemonic contains a word that is not in the wordlist';

/**
* mnemonicFromSeed converts a 32-byte key into a 25 word mnemonic. The generated mnemonic includes a checksum.
Expand Down Expand Up @@ -36,7 +36,7 @@ function seedFromMnemonic(mnemonic) {

//Check that all words are in list
for (let w of key) {
if (english.indexOf(w) === -1) throw ERROR_NOT_IN_WORDS_LIST;
if (english.indexOf(w) === -1) throw new Error(NOT_IN_WORDS_LIST_ERROR_MSG);
}

const checksum = words[words.length - 1];
Expand All @@ -52,10 +52,10 @@ function seedFromMnemonic(mnemonic) {
// While converting back to byte array, our new 264 bits array is divisible by 8 but the last byte is just the padding.

// check that we have 33 bytes long array as expected
if (uint8Array.length !== 33) throw ERROR_FAIL_TO_DECODE_MNEMONIC;
if (uint8Array.length !== 33) throw new Error(FAIL_TO_DECODE_MNEMONIC_ERROR_MSG);

// check that the last byte is actually 0x0
if (uint8Array[uint8Array.length - 1] !== 0x0) throw ERROR_FAIL_TO_DECODE_MNEMONIC;
if (uint8Array[uint8Array.length - 1] !== 0x0) throw new Error(FAIL_TO_DECODE_MNEMONIC_ERROR_MSG);

// chop it !
uint8Array = uint8Array.slice(0, uint8Array.length - 1);
Expand All @@ -67,7 +67,7 @@ function seedFromMnemonic(mnemonic) {
// success!
if (cs === checksum) return uint8Array;

throw ERROR_FAIL_TO_DECODE_MNEMONIC;
throw new Error(FAIL_TO_DECODE_MNEMONIC_ERROR_MSG);
}

function computeChecksum(seed) {
Expand Down Expand Up @@ -180,8 +180,8 @@ function masterDerivationKeyToMnemonic(mdk) {
module.exports = {
mnemonicFromSeed,
seedFromMnemonic,
ERROR_FAIL_TO_DECODE_MNEMONIC,
ERROR_NOT_IN_WORDS_LIST,
FAIL_TO_DECODE_MNEMONIC_ERROR_MSG,
NOT_IN_WORDS_LIST_ERROR_MSG,
mnemonicToSecretKey,
secretKeyToMnemonic,
mnemonicToMasterDerivationKey,
Expand Down
34 changes: 17 additions & 17 deletions src/multisig.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ const utils = require('./utils/utils');
Utilities for manipulating multisig transaction blobs.
*/

const ERROR_MULTISIG_MERGE_LESSTHANTWO = new Error("Not enough multisig transactions to merge. Need at least two");
const ERROR_MULTISIG_MERGE_MISMATCH = new Error("Cannot merge txs. txIDs differ");
const ERROR_MULTISIG_MERGE_WRONG_PREIMAGE = new Error("Cannot merge txs. Multisig preimages differ");
const ERROR_MULTISIG_MERGE_SIG_MISMATCH = new Error("Cannot merge txs. subsigs are mismatched.");
const ERROR_MULTISIG_BAD_FROM_FIELD = new Error("The transaction from field and multisig preimage do not match.");
const ERROR_MULTISIG_KEY_NOT_EXIST = new Error("Key does not exist");
const MULTISIG_MERGE_LESSTHANTWO_ERROR_MSG = "Not enough multisig transactions to merge. Need at least two";
const MULTISIG_MERGE_MISMATCH_ERROR_MSG = "Cannot merge txs. txIDs differ";
const MULTISIG_MERGE_WRONG_PREIMAGE_ERROR_MSG = "Cannot merge txs. Multisig preimages differ";
const MULTISIG_MERGE_SIG_MISMATCH_ERROR_MSG = "Cannot merge txs. subsigs are mismatched.";
const MULTISIG_BAD_FROM_FIELD_ERROR_MSG = "The transaction from field and multisig preimage do not match.";
const MULTISIG_KEY_NOT_EXIST_ERROR_MSG = "Key does not exist";

/**
* MultisigTransaction is a Transaction that also supports creating partially-signed multisig transactions.
Expand Down Expand Up @@ -56,7 +56,7 @@ class MultisigTransaction extends txnBuilder.Transaction {
if (!this.hasOwnProperty("objForEncoding")) {
let expectedFromRaw = address.fromMultisigPreImg({version, threshold, pks});
if (address.encodeAddress(this.from.publicKey) !== address.encodeAddress(expectedFromRaw)) {
throw ERROR_MULTISIG_BAD_FROM_FIELD;
throw new Error(MULTISIG_BAD_FROM_FIELD_ERROR_MSG);
}
}
// get signature verifier
Expand Down Expand Up @@ -93,7 +93,7 @@ function createMultisigTransaction(txnForEncoding, {rawSig, myPk}, {version, thr
return {"pk": Buffer.from(pk)};
});
if (keyExist === false) {
throw ERROR_MULTISIG_KEY_NOT_EXIST;
throw new Error(MULTISIG_KEY_NOT_EXIST_ERROR_MSG);
}
let msig = {
"v": version,
Expand All @@ -114,7 +114,7 @@ function createMultisigTransaction(txnForEncoding, {rawSig, myPk}, {version, thr
*/
function mergeMultisigTransactions(multisigTxnBlobs) {
if (multisigTxnBlobs.length < 2) {
throw ERROR_MULTISIG_MERGE_LESSTHANTWO;
throw new Error(MULTISIG_MERGE_LESSTHANTWO_ERROR_MSG);
}
const refSigTx = encoding.decode(multisigTxnBlobs[0]);
const refSigAlgoTx = MultisigTransaction.from_obj_for_encoding(refSigTx.txn);
Expand All @@ -126,11 +126,11 @@ function mergeMultisigTransactions(multisigTxnBlobs) {
let unisig = encoding.decode(multisigTxnBlobs[i]);
let unisigAlgoTxn = MultisigTransaction.from_obj_for_encoding(unisig.txn);
if (unisigAlgoTxn.txID().toString() !== refTxIDStr) {
throw ERROR_MULTISIG_MERGE_MISMATCH;
throw new Error(MULTISIG_MERGE_MISMATCH_ERROR_MSG);
}
// check multisig has same preimage as reference
if (unisig.msig.subsig.length !== refSigTx.msig.subsig.length) {
throw ERROR_MULTISIG_MERGE_WRONG_PREIMAGE;
throw new Error(MULTISIG_MERGE_WRONG_PREIMAGE_ERROR_MSG);
}
let preimg = {
"version": unisig.msig.v,
Expand All @@ -140,7 +140,7 @@ function mergeMultisigTransactions(multisigTxnBlobs) {
}),
};
if (from !== address.encodeAddress(address.fromMultisigPreImg(preimg))) {
throw ERROR_MULTISIG_MERGE_WRONG_PREIMAGE;
throw new Error(MULTISIG_MERGE_WRONG_PREIMAGE_ERROR_MSG);
}
// now, we can merge
newSubsigs = unisig.msig.subsig.map((uniSubsig, index) => {
Expand All @@ -152,7 +152,7 @@ function mergeMultisigTransactions(multisigTxnBlobs) {
// info: https://github.com/algorand/js-algorand-sdk/issues/252
if (uniSubsig.s && Buffer.compare(Buffer.from(uniSubsig.s), Buffer.from(current.s)) !== 0) {
// mismatch
throw ERROR_MULTISIG_MERGE_SIG_MISMATCH;
throw new Error(MULTISIG_MERGE_SIG_MISMATCH_ERROR_MSG);
}
return {
"pk": current.pk,
Expand Down Expand Up @@ -233,8 +233,8 @@ module.exports = {
mergeMultisigTransactions,
createMultisigTransaction,
verifyMultisig,
ERROR_MULTISIG_MERGE_LESSTHANTWO,
ERROR_MULTISIG_MERGE_MISMATCH,
ERROR_MULTISIG_MERGE_WRONG_PREIMAGE,
ERROR_MULTISIG_MERGE_SIG_MISMATCH,
MULTISIG_MERGE_LESSTHANTWO_ERROR_MSG,
MULTISIG_MERGE_MISMATCH_ERROR_MSG,
MULTISIG_MERGE_WRONG_PREIMAGE_ERROR_MSG,
MULTISIG_MERGE_SIG_MISMATCH_ERROR_MSG,
};
6 changes: 3 additions & 3 deletions tests/1.Mnemonics_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,21 @@ describe('#mnemonic', function () {
mn = mn.substring(0, mn.length - 2) + lastChar;
assert.throws(() => {
passphrase.seedFromMnemonic(mn)
}, (err) => err === passphrase.ERROR_FAIL_TO_DECODE_MNEMONIC);
}, (err) => err.message === passphrase.FAIL_TO_DECODE_MNEMONIC_ERROR_MSG);
});

it('should fail to verify an invalid mnemonic', function () {
let mn = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon venue abandon abandon abandon abandon abandon abandon abandon abandon abandon invest"
assert.throws(() => {
passphrase.seedFromMnemonic(mn);
}, (err) => err === passphrase.ERROR_FAIL_TO_DECODE_MNEMONIC);
}, (err) => err.message === passphrase.FAIL_TO_DECODE_MNEMONIC_ERROR_MSG);

});
it('should fail to verify an mnemonic with a word that is not in the list ', function () {
let mn = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon venues abandon abandon abandon abandon abandon abandon abandon abandon abandon invest"
assert.throws(() => {
passphrase.seedFromMnemonic(mn);
}, (err) => err === passphrase.ERROR_NOT_IN_WORDS_LIST);
}, (err) => err.message === passphrase.NOT_IN_WORDS_LIST_ERROR_MSG);

});
});
6 changes: 3 additions & 3 deletions tests/3.Address.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ describe('address', function () {
});

it('should fail to verify a malformed Algorand address', function () {
assert.throws(() => { algosdk.decodeAddress(malformed_address1) }, (err) => err === address.MALFORMED_ADDRESS_ERROR);
assert.throws(() => { algosdk.decodeAddress(malformed_address2) }, (err) => err === address.MALFORMED_ADDRESS_ERROR);
assert.throws(() => { algosdk.decodeAddress(malformed_address1) }, (err) => err.message === address.MALFORMED_ADDRESS_ERROR_MSG);
assert.throws(() => { algosdk.decodeAddress(malformed_address2) }, (err) => err.message === address.MALFORMED_ADDRESS_ERROR_MSG);
// Catch an exception possibly thrown by base32 decoding function
assert.throws(() => { algosdk.decodeAddress(malformed_address3) }, (err) => err.message === "Invalid base32 characters");
});

it('should fail to verify a checksum for an invalid Algorand address', function () {
assert.throws(() => { algosdk.decodeAddress(wrong_checksum_address) }, (err) => err === address.CHECKSUM_ADDRESS_ERROR);
assert.throws(() => { algosdk.decodeAddress(wrong_checksum_address) }, (err) => err.message === address.CHECKSUM_ADDRESS_ERROR_MSG);
});

// Check helper functions
Expand Down

0 comments on commit 3c7499f

Please sign in to comment.