Skip to content

Commit

Permalink
Merge pull request #4 from coderofstuff/path-for-change-2
Browse files Browse the repository at this point in the history
Allow passing path type and index for change
  • Loading branch information
coderofstuff authored May 17, 2023
2 parents f904cf0 + 916443d commit 50f4641
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 10 deletions.
19 changes: 13 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Get Kaspa address (public key) for a BIP32 path.
##### Examples

```javascript
kaspa.getAddress("44'/501'/0'").then(r => r.address)
kaspa.getAddress("44'/111111'/0'").then(r => r.address)
```

Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<{address: [Buffer](https://nodejs.org/api/buffer.html)}>** an object with the address field
Expand Down Expand Up @@ -80,19 +80,26 @@ const txin = new TransactionInput({
});

const txout = new TransactionOutput({
value: 1090000,
value: 1000000,
scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
});

// By convention, the second output MUST be the change address
// It MUST set both addressType and addressIndex
const txoutchange = new TransactionOutput({
value: 90000,
scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
});

const tx = new Transaction({
version: 0,
changeAddressType: 0,
changeAddressIndex: 0,
inputs: [txin],
outputs: [txout],
outputs: [txout, txoutchange],
});

kaspa.signTransaction(tx);
```

Updates the


Updates the transaction by filling in the `signature` property of each `TransactionInput` in the `Transaction` object.
3 changes: 0 additions & 3 deletions src/kaspa.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,6 @@ class Kaspa {
if (!(transaction instanceof Transaction)) {
throw new Error("transaction must be an instance of Transaction");
}
// Ledger app supports only a single derivation path per call ATM
const pathsCountBuffer = Buffer.alloc(1);
pathsCountBuffer.writeUInt8(1, 0);

const header = transaction.serialize();

Expand Down
27 changes: 27 additions & 0 deletions src/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ class Transaction {
* @type {int}
*/
this.version = txData.version;

this.changeAddressType = Number.isInteger(txData.changeAddressType) ? txData.changeAddressType : 0;
this.changeAddressIndex = Number.isInteger(txData.changeAddressIndex) ? txData.changeAddressIndex : 0;

if (!(this.changeAddressType === 0 || this.changeAddressType === 1)) {
throw new Error(`changeAddressType must be 0 or 1 if set`);
}

if (this.changeAddressIndex < 0x00000000 || this.changeAddressIndex > 0xFFFFFFFF) {
throw new Error(`changeAddressIndex must be between 0x00000000 and 0xFFFFFFFF`);
}
}

serialize() {
Expand All @@ -26,10 +37,18 @@ class Transaction {
const inputLenBuf = Buffer.alloc(1);
inputLenBuf.writeUInt8(this.inputs.length);

const changeAddressTypeBuf = Buffer.alloc(1);
changeAddressTypeBuf.writeUInt8(this.changeAddressType || 0);

const changeAddressIndexBuf = Buffer.alloc(4);
changeAddressIndexBuf.writeUInt32BE(this.changeAddressIndex || 0);

return Buffer.concat([
versionBuf,
outputLenBuf,
inputLenBuf,
changeAddressTypeBuf,
changeAddressIndexBuf,
]);
}

Expand Down Expand Up @@ -116,7 +135,15 @@ class TransactionInput {

class TransactionOutput {
constructor(outputData = {}) {
if (!outputData.value || outputData.value < 0 || outputData.value > 0xFFFFFFFFFFFFFFFF) {
throw new Error('value must be set to a value greater than 0 and less than 0xFFFFFFFFFFFFFFFF');
}
this.value = outputData.value;
if (!outputData.scriptPublicKey) {
throw new Error('scriptPublicKey must be set');
}

// Only then do we care about the script public key
this.scriptPublicKey = outputData.scriptPublicKey;
}

Expand Down
118 changes: 117 additions & 1 deletion tests/kaspa.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ describe("kaspa", () => {
it("signTransaction with simple data", async () => {
const transport = await openTransportReplayer(
RecordStore.fromString(`
=> e00600800400000101
=> e006008009000001010102030405
<= 9000
=> e00601802a000000000010a1d02011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac
<= 9000
Expand All @@ -74,10 +74,126 @@ describe("kaspa", () => {
version: 0,
inputs: [txin],
outputs: [txout],
changeAddressType: 1,
changeAddressIndex: 0x02030405,
});

await kaspa.signTransaction(tx);
expect(txin.signature).toEqual("ec4a7f581dc2450ab43b412a67bdfdafa6f98281f854a1508852042e41ef86695ec7f0fa36122193fa201ce783618710d65c85cf94640cb93e965f5158fd84a3");
expect(txin.sighash).toEqual("00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff");
});
});

describe("Transaction", () => {
it("should serialize", () => {
const txin = new TransactionInput({
prevTxId: "40b022362f1a303518e2b49f86f87a317c87b514ca0f3d08ad2e7cf49d08cc70",
value: 1100000,
addressType: 0,
addressIndex: 0,
outpointIndex: 0,
});

const txout = new TransactionOutput({
value: 1090000,
scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
});

const tx = new Transaction({
version: 0,
inputs: [txin],
outputs: [txout],
changeAddressType: 1,
changeAddressIndex: 0x02030405
});

const expectation = Buffer.from([0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x03, 0x04, 0x05]);

expect(tx.serialize().equals(expectation)).toBeTruthy();
});
});

describe("TransactionOutput", () => {
it("should throw no error if only scriptPublicKey and value is set", () => {
let err = null;
try {
new TransactionOutput({
value: 1090000,
scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
});
} catch (e) {
err = e;
}

expect(err).toBe(null);
});

it("should serialize value and scriptPublicKey", () => {
const serial = new TransactionOutput({
value: 1090000,
scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
}).serialize();

const expectation = Buffer.from([
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xa1, 0xd0,
0x20, 0x11, 0xa7, 0x21, 0x5f, 0x66, 0x8e, 0x92,
0x10, 0x13, 0xeb, 0x7a, 0xac, 0x9b, 0x7e, 0x64,
0xb9, 0xec, 0x6e, 0x75, 0x7c, 0x1b, 0x64, 0x8e,
0x89, 0x38, 0x8c, 0x91, 0x9f, 0x67, 0x6a, 0xa8,
0x8c, 0xac,
]);

expect(serial.equals(expectation)).toBeTruthy();
});

it("should throw errors if none of scriptPublicKey or (addressType, addressIndex) are set", () => {
let err = null;
try {
new TransactionOutput({
value: 1090000
});
} catch (e) {
err = e;
}

expect(err).not.toBe(null);
});

it("should throw errors if value is not set", () => {
let err = null;
try {
new TransactionOutput({
scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
});
} catch (e) {
err = e;
}

expect(err).not.toBe(null);
});

it("should throw errors if value is < 0 or > 0xFFFFFFFFFFFFFFFF", () => {
let err = null;
try {
new TransactionOutput({
value: 0,
scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
});
} catch (e) {
err = e;
}

expect(err).not.toBe(null);

try {
new TransactionOutput({
value: 0xFFFFFFFFFFFFFFFF + 1,
scriptPublicKey: "2011a7215f668e921013eb7aac9b7e64b9ec6e757c1b648e89388c919f676aa88cac",
});
} catch (e) {
err = e;
}

expect(err).not.toBe(null);
});
});

0 comments on commit 50f4641

Please sign in to comment.