Skip to content

Commit

Permalink
feat: Add removeSignature/clearAllSignatures methods (#2491)
Browse files Browse the repository at this point in the history
* feat: Added removeSignature/clearAllSignatures methods to Transaction class, wrote some unit tests for both

Signed-off-by: ivaylogarnev-limechain <[email protected]>

* feat: Added more unit tests for removeSignature/clearAllSignatures and throwing an error if trying to remove non-existing signature

Signed-off-by: ivaylogarnev-limechain <[email protected]>

* refactor: Turned signAndAddSignatures function into arrow

Signed-off-by: ivaylogarnev-limechain <[email protected]>

* refactor: Cleared some unnecessary comments and changed variable assigment

Signed-off-by: ivaylogarnev-limechain <[email protected]>

* feat: Enhancement removeSignature/clearAllSignatures to return the removedSignatures and wrote tests for both

Signed-off-by: ivaylogarnev-limechain <[email protected]>

* refactor: Removed the signature param from removeSignature method and adjusted the logic, renamed clearAllSignatures and changed the return type, adjusted some tests

Signed-off-by: ivaylogarnev-limechain <[email protected]>

* refactor: Remove redundant code

Signed-off-by: ivaylogarnev-limechain <[email protected]>

* Merge branch 'main' into feat/2461-remove-clear-signatures

Signed-off-by: ivaylogarnev-limechain <[email protected]>

* refactor: Cleared some uneccessary comments & changed a bit the flow some methods conditions

Signed-off-by: ivaylogarnev-limechain <[email protected]>

* Merge branch main into feat/2461-remove-clear-signatures

Signed-off-by: ivaylogarnev-limechain <[email protected]>

* refactor: Refactored signTransaction method a bit, added integration tests and examples for the multi-node flow

Signed-off-by: ivaylogarnev-limechain <[email protected]>

* refactor: Removed duplicate integration tests and added description to examples

Signed-off-by: ivaylogarnev-limechain <[email protected]>

* refactor: Examples pre-release warnings fixes

Signed-off-by: ivaylogarnev-limechain <[email protected]>

---------

Signed-off-by: ivaylogarnev-limechain <[email protected]>
Signed-off-by: ivaylogarnev <[email protected]>
Co-authored-by: ivaylonikolov7 <[email protected]>
  • Loading branch information
ivaylogarnev-limechain and ivaylonikolov7 authored Sep 20, 2024
1 parent 50ea77a commit e03746e
Show file tree
Hide file tree
Showing 10 changed files with 1,159 additions and 47 deletions.
209 changes: 209 additions & 0 deletions examples/multi-node-multi-signature-remove.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import {
Client,
PrivateKey,
AccountCreateTransaction,
Hbar,
AccountId,
KeyList,
TransferTransaction,
Transaction,
} from "@hashgraph/sdk";

import dotenv from "dotenv";

dotenv.config();

let aliceKey;
let bobKey;

/**
* @description Create a transaction with multiple nodes and multiple signatures
* and remove one of the signatures from the transaction then add it back
*/
async function main() {
/**
*
* Step 1: Create Client
*
*/
if (
process.env.OPERATOR_ID == null ||
process.env.OPERATOR_KEY == null ||
process.env.HEDERA_NETWORK == null
) {
throw new Error(
"Environment variables OPERATOR_ID, HEDERA_NETWORK, and OPERATOR_KEY are required.",
);
}

const client = Client.forName(process.env.HEDERA_NETWORK).setOperator(
AccountId.fromString(process.env.OPERATOR_ID),
PrivateKey.fromStringED25519(process.env.OPERATOR_KEY),
);

/**
*
* Step 2: Create keys for two users
*
*/
aliceKey = PrivateKey.generate();
bobKey = PrivateKey.generate();

const keyList = new KeyList([aliceKey.publicKey, bobKey.publicKey]);

/**
*
* Step 3: Create an account with the keyList
*
*/
const createAccountTransaction = new AccountCreateTransaction()
.setInitialBalance(new Hbar(2))
.setKey(keyList);

const createResponse = await createAccountTransaction.execute(client);
const createReceipt = await createResponse.getReceipt(client);

/**
*
* Step 4: Create a transfer transaction with multiple nodes
*
*/
const transferTransaction = new TransferTransaction()
.addHbarTransfer(createReceipt.accountId, new Hbar(-1))
.addHbarTransfer("0.0.3", new Hbar(1))
// Set multiple nodes
.setNodeAccountIds([
new AccountId(3),
new AccountId(4),
new AccountId(5),
])
.freezeWith(client);

/**
*
* Step 5: Serialize the transaction
* & Collect multiple signatures (Uint8Array[]) from one key
*
*/

const transferTransactionBytes = transferTransaction.toBytes();

const aliceSignatures = aliceKey.signTransaction(transferTransaction);
const bobSignatures = bobKey.signTransaction(transferTransaction);

/**
*
* Step 6: Deserialize the transaction
* & Add the previously collected signatures
*
*/
const signedTransaction = Transaction.fromBytes(transferTransactionBytes);

signedTransaction.addSignature(aliceKey.publicKey, aliceSignatures);
signedTransaction.addSignature(bobKey.publicKey, bobSignatures);

console.log("ADDED users signatures below: \n");

if (Array.isArray(aliceSignatures) && Array.isArray(bobSignatures)) {
console.log(
"Alice Signatures =>",
aliceSignatures.map((aliceSig) =>
PrivateKey.fromBytes(aliceSig).toStringDer(),
),
);

console.log(
"Bob Signatures =>",
bobSignatures.map((bobSig) =>
PrivateKey.fromBytes(bobSig).toStringDer(),
),
);
}

const signaturesInTheTransactionBefore =
getAllSignaturesFromTransaction(signedTransaction);

console.log("\n\nSignatures in the transaction: ");
console.log(signaturesInTheTransactionBefore);

/**
*
* Step 7: Remove the signatures for Alice from the transaction
*
*/

const removedAliceSignatures = signedTransaction.removeSignature(
aliceKey.publicKey,
);

console.log("\nREMOVED Alice signatures below: \n");

if (Array.isArray(aliceSignatures) && Array.isArray(bobSignatures)) {
console.log(
"Alice removed signatures =>",
removedAliceSignatures.map((aliceSig) =>
PrivateKey.fromBytes(aliceSig).toStringDer(),
),
);
}

const signaturesInTheTransactionAfter =
getAllSignaturesFromTransaction(signedTransaction);

console.log("\n\nSignatures left in the transaction: ");
console.log(signaturesInTheTransactionAfter);

/**
*
* Step 8: Add the removed signature back to the transaction
*
*/
signedTransaction.addSignature(aliceKey.publicKey, removedAliceSignatures);

/**
*
* Step 9: Execute and take the receipt
*
*/
const result = await signedTransaction.execute(client);

const receipt = await result.getReceipt(client);

console.log(`\n \nTransaction status: ${receipt.status.toString()}`);

client.close();
}

void main();

/**
* Extracts all signatures from a signed transaction.
* @param {Transaction} signedTransaction - The signed transaction object containing the list of signed transactions.
* @returns {string[]} An array of signatures in DER format.
*/
const getAllSignaturesFromTransaction = (signedTransaction) => {
/** @type {string[]} */
const signatures = [];

signedTransaction._signedTransactions.list.forEach((transaction) => {
if (transaction.sigMap?.sigPair) {
transaction.sigMap.sigPair.forEach((sigPair) => {
if (sigPair.ed25519) {
signatures.push(
PrivateKey.fromBytesED25519(
sigPair.ed25519,
).toStringDer(),
);
} else if (sigPair.ECDSASecp256k1) {
signatures.push(
PrivateKey.fromBytesECDSA(
sigPair.ECDSASecp256k1,
).toStringDer(),
);
}
});
}
});

return signatures;
};
Loading

0 comments on commit e03746e

Please sign in to comment.