Skip to content

Commit

Permalink
Release/v1.7.1 (#205)
Browse files Browse the repository at this point in the history
See CHANGELOG.md for details.

Review: #205
  • Loading branch information
algorotem authored Sep 15, 2020
1 parent 3999aa4 commit dc857c3
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 7 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# 1.7.1
## Fixed
- Fixed set Accept on GET calls
## Changed
- Change algosdk.signMultisigTransaction to accept either a built Transaction or a dict of constructor args
# 1.7.0
## Added
- Support for Application Call Transactions, also known as Stateful TEAL
Expand Down
2 changes: 1 addition & 1 deletion dist/algosdk.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "algosdk",
"version": "1.7.0",
"version": "1.7.1",
"description": "algosdk is Algorand's official javascript SDK",
"main": "index.js",
"directories": {
Expand Down
22 changes: 21 additions & 1 deletion src/client/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,26 @@ function removeEmpty(obj) {
return obj;
}

/**
* getAccceptFormat returns the correct Accept header depending on the
* requested format.
* @param query
* @returns {string}
*/
function getAccceptFormat(query) {
if (query !== undefined && query.hasOwnProperty('format'))
switch(query.format) {
case 'msgpack':
return 'application/msgpack';
case 'json':
return 'application/json';
default:
return 'application/json';
}
else
return "application/json"
}

function HTTPClient(token, baseServer, port, headers={}) {
// Do not need colon if port is empty
if (port !== '') {
Expand All @@ -30,7 +50,7 @@ function HTTPClient(token, baseServer, port, headers={}) {
.set(this.token)
.set(this.defaultHeaders)
.set(requestHeaders)
.set('Accept', 'application/json')
.set('Accept', getAccceptFormat(query))
.query(removeEmpty(query));
} catch (e) {
throw e;
Expand Down
17 changes: 14 additions & 3 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,19 +165,30 @@ function signMultisigTransaction(txn, {version, threshold, addrs}, sk) {
// check that the from field matches the mSigPreImage. If from field is not populated, fill it in.
let expectedFromRaw = address.fromMultisigPreImgAddrs({version, threshold, addrs});
if (txn.hasOwnProperty('from')) {
if (txn.from !== expectedFromRaw) {
if ((txn.from !== expectedFromRaw) && (address.encode(txn.from.publicKey) !== expectedFromRaw)) {
throw ERROR_MULTISIG_BAD_SENDER;
}
} else {
txn.from = expectedFromRaw;
}
let algoTxn = new multisig.MultisigTransaction(txn);
// build pks for partialSign
const pks = addrs.map(addr => {
return address.decode(addr).publicKey;
});
// `txn` needs to be handled differently if it's a constructed `Transaction` vs a dict of constructor args
let txnAlreadyBuilt = (txn instanceof txnBuilder.Transaction);
let algoTxn;
let blob;
if (txnAlreadyBuilt) {
algoTxn = txn;
blob = multisig.MultisigTransaction.prototype.partialSignTxn.call(algoTxn, {version, threshold, pks}, sk);
} else {
algoTxn = new multisig.MultisigTransaction(txn);
blob = algoTxn.partialSignTxn({version, threshold, pks}, sk);
}
return {
"txID": algoTxn.txID().toString(),
"blob": algoTxn.partialSignTxn({version, threshold, pks}, sk),
"blob": blob
};
}

Expand Down
47 changes: 47 additions & 0 deletions tests/7.AlgoSDK.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,53 @@ describe('Algosdk (AKA end to end)', function () {
let tx_golden = "TDIO6RJWJIVDDJZELMSX5CPJW7MUNM3QR4YAHYAKHF3W2CFRTI7A";
assert.deepStrictEqual(js_dec.txID, tx_golden);
});

it('should return the same blob whether using dict-of-args or algosdk.makeFooTransaction', function () {
const params = {
version: 1,
threshold: 2,
addrs: [
"DN7MBMCL5JQ3PFUQS7TMX5AH4EEKOBJVDUF4TCV6WERATKFLQF4MQUPZTA",
"BFRTECKTOOE7A5LHCF3TTEOH2A7BW46IYT2SX5VP6ANKEXHZYJY77SJTVM",
"47YPQTIGQEO7T4Y4RWDYWEKV6RTR2UNBQXBABEEGM72ESWDQNCQ52OPASU",
],
}; // msig address - RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM

let mnemonic = "advice pudding treat near rule blouse same whisper inner electric quit surface sunny dismiss leader blood seat clown cost exist hospital century reform able sponsor";
let seed = passphrase.seedFromMnemonic(mnemonic);
let keys = nacl.keyPairFromSeed(seed);
let sk = keys.secretKey;

let to_addr = "PNWOET7LLOWMBMLE4KOCELCX6X3D3Q4H2Q4QJASYIEOF7YIPPQBG3YQ5YI";
let from_addr = "RWJLJCMQAFZ2ATP2INM2GZTKNL6OULCCUBO5TQPXH3V2KR4AG7U5UA5JNM";
let fee = 4;
let amount = 1000;
let firstRound = 12466;
let lastRound = 13466;
let genesisID = "devnet-v33.0";
let genesisHash = "JgsgCaCTqIaLeVhyL6XlRu3n7Rfk2FxMeK+wRSaQ7dI=";
let closeRemainder = "IDUTJEUIEVSMXTU4LGTJWZ2UE2E6TIODUKU6UW3FU3UKIQQ77RLUBBBFLA";
let note = new Uint8Array(Buffer.from("X4Bl4wQ9rCo=", "base64"))
let o_dict = {
"to": to_addr,
"from": from_addr,
"fee": fee,
"amount": amount,
"firstRound": firstRound,
"lastRound": lastRound,
"genesisID": genesisID,
"genesisHash": genesisHash,
"closeRemainderTo": closeRemainder,
"note": note
};
let o_obj = algosdk.makePaymentTxn(from_addr, to_addr, fee, amount, closeRemainder,
firstRound, lastRound, note, genesisHash, genesisID);

let o_dict_output = algosdk.signMultisigTransaction(o_dict, params, sk);
let o_obj_output = algosdk.signMultisigTransaction(o_obj, params, sk);
assert.deepStrictEqual(o_dict_output.txID, o_obj_output.txID);
assert.deepStrictEqual(o_dict_output.blob, o_obj_output.blob);
});
});


Expand Down
17 changes: 16 additions & 1 deletion tests/cucumber/steps/stepdefs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2287,6 +2287,19 @@ When('I use {int} to lookup application with {int}', async function (clientNum,
this.responseForDirectJsonComparison = await ic.lookupApplications(appID).do();
});

function sortKeys(x) {
// recursively sorts on keys, unless the passed object is an array of dicts that all contain the property 'key',
// in which case it sorts on the value corresponding to key 'key'
if (typeof x !== 'object' || !x) return x;
if (Array.isArray(x)) {
if (x.every(subobject => ((typeof subobject == 'object') && subobject.hasOwnProperty('key')))) {
return x.sort((a, b) => (a.key > b.key) ? 1 : -1)
}
return x.map(sortKeys);
}
return Object.keys(x).sort().reduce((o, k) => ({...o, [k]: sortKeys(x[k])}), {});
}

Then('the parsed response should equal {string}.', function (jsonFile) {
let responseFromFile = "";
let mockResponsePath = "file://" + process.env.UNITTESTDIR + "/../resources/" + jsonFile;
Expand All @@ -2296,7 +2309,9 @@ Then('the parsed response should equal {string}.', function (jsonFile) {
responseFromFile = xml.responseText.trim();
};
xml.send();
assert.strictEqual(JSON.stringify(this.responseForDirectJsonComparison), JSON.stringify(JSON.parse(responseFromFile)));
this.responseForDirectJsonComparison = sortKeys(this.responseForDirectJsonComparison);
responseFromFile = sortKeys(JSON.parse(responseFromFile));
assert.strictEqual(JSON.stringify(this.responseForDirectJsonComparison), JSON.stringify(responseFromFile));
});

When('I get the next page using {int} to search for transactions with {int} and {int}', async function (clientNum, limit, maxRound) {
Expand Down

0 comments on commit dc857c3

Please sign in to comment.