Skip to content
This repository has been archived by the owner on Jul 4, 2023. It is now read-only.

Commit

Permalink
Fixed send tx data error. Added examples for 3 tx types.
Browse files Browse the repository at this point in the history
  • Loading branch information
valar999 committed Aug 11, 2020
1 parent e6c3f85 commit 5966724
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 60 deletions.
91 changes: 71 additions & 20 deletions example-node/src/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import "babel-polyfill";
import ed25519 from "ed25519";
import crypto from 'crypto';
import TransportNodeHid from "@ledgerhq/hw-transport-node-hid";
import AppSmesh, { utils } from "../../lib/Smesh"; //"@spacemeshos/ledger-sdk";

//const publicKey = utils.hex_to_buf('8eb640bb3c0b63923637e07e4bc82ea032e2cceec59ada068dd5849d971bd099');

const makeExamples = appSmesh => ({
getVersion: async () => {
console.log("getVersion");
Expand All @@ -10,15 +14,9 @@ const makeExamples = appSmesh => ({

getExtendedPublicKey: async () => {
console.log("getExtendedPublicKey");
console.log(
await appSmesh.getExtendedPublicKey([
utils.HARDENED + 44,
utils.HARDENED + 540,
utils.HARDENED + 0,
0,
utils.HARDENED + 0
])
);
const response = await appSmesh.getExtendedPublicKey(utils.str_to_path("44'/540'/0'/0/0'"));
console.log(response);
appSmesh.publicKey = response.publicKey;
},

getAddress: async () => {
Expand All @@ -35,23 +33,74 @@ const makeExamples = appSmesh => ({
);
},

signTx: async () => {
console.log("signTx");
signCoinTx: async () => {
console.log("signCoinTx");
const tx = Buffer.concat([
utils.uint8_to_buf(1), // type
utils.uint8_to_buf(0), // // coin transaction with ed
utils.uint64_to_buf(1), // nonce
utils.hex_to_buf("0000000000000000000000000000000000000000"), // recepient
utils.uint64_to_buf(1000000), // gas limit
utils.uint64_to_buf(1000), // gas price
utils.uint64_to_buf(1000000000000), // amount
utils.hex_to_buf("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), // bin data
utils.hex_to_buf("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), // bin data
utils.hex_to_buf("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), // bin data
utils.hex_to_buf("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") // bin data
appSmesh.publicKey
]);
console.log(
await appSmesh.signTx(utils.str_to_path("44'/540'/0'/0/0'"), tx)
);
var hash = crypto.createHash('sha512').update(tx.slice(1)).digest();

const response = await appSmesh.signTx(utils.str_to_path("44'/540'/0'/0/0'"), tx);
const signature = response.slice(1, 65);
console.log(signature.length, signature);

console.log("Verify coin tx:", ed25519.Verify(hash, signature, appSmesh.publicKey));
},

signAppTx: async () => {
console.log("signAppTx");
const tx = Buffer.concat([
utils.uint8_to_buf(2), // exec app transaction with ed
utils.uint64_to_buf(1), // nonce
utils.hex_to_buf("0000000000000000000000000000000000000000"), // app address
utils.uint64_to_buf(1000000), // gas limit
utils.uint64_to_buf(1000), // gas price
utils.uint64_to_buf(1000000000000) // amount
// call data
, utils.hex_to_buf("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") // bin data
, utils.hex_to_buf("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") // bin data
, utils.hex_to_buf("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") // bin data
, utils.hex_to_buf("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") // bin data
, appSmesh.publicKey
]);
var hash = crypto.createHash('sha512').update(tx.slice(1)).digest(); //returns a buffer

const response = await appSmesh.signTx(utils.str_to_path("44'/540'/0'/0/0'"), tx);
const signature = response.slice(1, 65);
console.log(signature.length, signature);

console.log("Verify app tx:", ed25519.Verify(hash, signature, appSmesh.publicKey));
},

signSpawnTx: async () => {
console.log("signSpawnTx");
const tx = Buffer.concat([
utils.uint8_to_buf(4), // spawn app + ed
utils.uint64_to_buf(1), // nonce
utils.hex_to_buf("0000000000000000000000000000000000000000"), // template address
utils.uint64_to_buf(1000000), // gas limit
utils.uint64_to_buf(1000), // gas price
utils.uint64_to_buf(1000000000000) // amount
// call data
, utils.hex_to_buf("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") // bin data
, utils.hex_to_buf("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") // bin data
, utils.hex_to_buf("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") // bin data
, utils.hex_to_buf("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") // bin data
, appSmesh.publicKey
]);
var hash = crypto.createHash('sha512').update(tx.slice(1)).digest(); //returns a buffer

const response = await appSmesh.signTx(utils.str_to_path("44'/540'/0'/0/0'"), tx);
const signature = response.slice(1, 65);
console.log(signature.length, signature);

console.log("Verify spawn tx:", ed25519.Verify(hash, signature, appSmesh.publicKey));
}
});

Expand All @@ -68,7 +117,9 @@ async function example() {
await examples.getExtendedPublicKey();
await examples.getAddress();
await examples.showAddress();
await examples.signTx();
await examples.signCoinTx();
await examples.signAppTx();
await examples.signSpawnTx();
} catch (error) {
console.log(error);
}
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@ledgerhq/hw-transport": "^5.12.0",
"babel-polyfill": "^6.26.0",
"babel-runtime": "^6.26.0",
"ed25519": "0.0.4",
"node-int64": "^0.4.0"
},
"devDependencies": {
Expand Down
53 changes: 13 additions & 40 deletions src/Smesh.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,33 +47,14 @@ export type GetVersionResponse = {|
|};

export type GetAddressResponse = {|
address: string
address: Buffer
|};

export type GetExtendedPublicKeyResponse = {|
publicKeyHex: string,
chainCodeHex: string
publicKey: Buffer,
chainCode: Buffer
|};

// It can happen that we try to send a message to the device
// when the device thinks it is still in a middle of previous ADPU stream.
// This happens mostly if host does abort communication for some reason
// leaving ledger mid-call.
// In this case Ledger will respond by ERR_STILL_IN_CALL *and* resetting its state to
// default. We can therefore transparently retry the request.
// Note though that only the *first* request in an multi-APDU exchange should be retried.
const wrapRetryStillInCall = fn => async (...args: any) => {
try {
return await fn(...args);
} catch (e) {
if (e && e.statusCode && e.statusCode === ErrorCodes.ERR_STILL_IN_CALL) {
// Do the retry
return await fn(...args);
}
throw e;
}
};

const wrapConvertError = fn => async (...args) => {
try {
return await fn(...args);
Expand Down Expand Up @@ -106,7 +87,7 @@ export default class Smesh {
this.methods = [
"getVersion",
"getExtendedPublicKey",
"signTransaction",
"signTx",
"getAddress",
"showAddress"
];
Expand All @@ -131,11 +112,7 @@ export default class Smesh {
);
const P1_UNUSED = 0x00;
const P2_UNUSED = 0x00;
const response = await wrapRetryStillInCall(_send)(
P1_UNUSED,
P2_UNUSED,
utils.hex_to_buf("")
);
const response = await _send(P1_UNUSED, P2_UNUSED, utils.hex_to_buf(""));
Assert.assert(response.length == 4);
const [major, minor, patch, flags_value] = response;

Expand Down Expand Up @@ -181,18 +158,14 @@ export default class Smesh {

console.log(data);

const response = await wrapRetryStillInCall(_send)(
P1_UNUSED,
P2_UNUSED,
data
);
const response = await _send(P1_UNUSED, P2_UNUSED, data);

const [publicKey, chainCode, rest] = utils.chunkBy(response, [32, 32]);
const [publicKey, chainCode, privateKey, rest] = utils.chunkBy(response, [32, 32, 64]);
Assert.assert(rest.length == 0);

return {
publicKeyHex: publicKey.toString("hex"),
chainCodeHex: chainCode.toString("hex")
publicKey: publicKey,
chainCode: chainCode
};
}

Expand Down Expand Up @@ -294,24 +267,24 @@ export default class Smesh {
]);

if (data.length <= MAX_PACKET_LENGTH) {
response = await _send(P1_HAS_HEADER | P1_HAS_DATA | P1_IS_LAST, P2_UNUSED, data);
response = await _send(P1_HAS_HEADER | P1_IS_LAST, P2_UNUSED, data);
} else {
let dataSize = data.length;
let chunkSize = MAX_PACKET_LENGTH;
let offset = 0;
// Send tx header + tx data
response = await _send(P1_HAS_HEADER | P1_HAS_DATA, P2_UNUSED, data.slice(offset, chunkSize));
response = await _send(P1_HAS_HEADER | P1_HAS_DATA, P2_UNUSED, data.slice(offset, offset + chunkSize));
Assert.assert(response.length == 0);
dataSize -= chunkSize;
offset += chunkSize;
// Send tx data
while (dataSize > MAX_PACKET_LENGTH) {
response = await _send(P1_HAS_DATA, P2_UNUSED, data.slice(offset, chunkSize));
response = await _send(P1_HAS_DATA, P2_UNUSED, data.slice(offset, offset + chunkSize));
Assert.assert(response.length == 0);
dataSize -= chunkSize;
offset += chunkSize;
}
response = await _send(P1_HAS_DATA | P1_IS_LAST, P2_UNUSED, data.slice(offset, dataSize));
response = await _send(P1_IS_LAST, P2_UNUSED, data.slice(offset));
}

Assert.assert(response !== null);
Expand Down

0 comments on commit 5966724

Please sign in to comment.