Skip to content

Commit

Permalink
fix: serialize deserialize FileAppend (hashgraph#2532)
Browse files Browse the repository at this point in the history
* fix: serialize deserialize fileappend

Signed-off-by: Ivaylo Nikolov <[email protected]>

* fix: return _makeTransactionData

Signed-off-by: Ivaylo Nikolov <[email protected]>

* test: add integration test for fromByte toByte converison

Signed-off-by: Ivaylo Nikolov <[email protected]>

* refactor: implement better transaction conversion

Signed-off-by: Ivaylo Nikolov <[email protected]>

* fix: fileappend hip-765 finish implementation

Signed-off-by: Ivaylo Nikolov <[email protected]>

* revert: remove  multi signature multi node feat for FileAppend

Signed-off-by: Ivaylo Nikolov <[email protected]>

* test: freeze transaction after fromBytes

Signed-off-by: Ivaylo Nikolov <[email protected]>

* refactor: disallow addSignature check for chunked transactions in transaction class

Signed-off-by: Ivaylo Nikolov <[email protected]>

* refactor: linter complains about skipped test

Signed-off-by: Ivaylo Nikolov <[email protected]>

* fix: move type definitions because of linter

Signed-off-by: Ivaylo Nikolov <[email protected]>

* revert: old type definitions

Signed-off-by: Ivaylo Nikolov <[email protected]>

* fix: throw error when required chunks are more than max chunks

Signed-off-by: Ivaylo Nikolov <[email protected]>

* refactor: required chunks should not be an error

Signed-off-by: Ivaylo Nikolov <[email protected]>

* style: fix jsdoc comment when building incomplete transaction

Signed-off-by: Ivaylo Nikolov <[email protected]>

* refactor: reuse variables in file append integration tests

Signed-off-by: Ivaylo Nikolov <[email protected]>

* refactor: delete already defined variables

Signed-off-by: Ivaylo Nikolov <[email protected]>

* test: fix typo in test name

Signed-off-by: Ivaylo Nikolov <[email protected]>

* refactor: return back addSignature return  type

Signed-off-by: Ivaylo Nikolov <[email protected]>

* test: fix typo

Signed-off-by: Ivaylo Nikolov <[email protected]>

* fix: implement maxChunks when serializing content too

Signed-off-by: Ivaylo Nikolov <[email protected]>

* fix: wrong comparison

Signed-off-by: Ivaylo Nikolov <[email protected]>

* style: add empty lines

Signed-off-by: Ivaylo Nikolov <[email protected]>

* refactor: generate new transactionId

Signed-off-by: Ivaylo Nikolov <[email protected]>

* test: add more integration tests

Signed-off-by: Ivaylo Nikolov <[email protected]>

* refactor: extract dummy account id to const

Signed-off-by: Ivaylo Nikolov <[email protected]>

* refactor: fix function nane typo

Signed-off-by: Ivaylo Nikolov <[email protected]>

* refactor: fix function name typo 2

Signed-off-by: Ivaylo Nikolov <[email protected]>

* chore: remove incompleted references

Signed-off-by: Ivaylo Nikolov <[email protected]>

---------

Signed-off-by: Ivaylo Nikolov <[email protected]>
Signed-off-by: b-l-u-e <[email protected]>
  • Loading branch information
ivaylonikolov7 authored and b-l-u-e committed Oct 13, 2024
1 parent 6782b1a commit 6430e93
Show file tree
Hide file tree
Showing 14 changed files with 299 additions and 51 deletions.
2 changes: 1 addition & 1 deletion examples/serialize-deserialize-10.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
import dotenv from "dotenv";

/**
* @description Serialize and deserialize so-called incompleted transaction (chunked), set transaction id and execute it
* @description Serialize and deserialize so-called incomplete transaction (chunked), set transaction id and execute it
*/

async function main() {
Expand Down
2 changes: 1 addition & 1 deletion examples/serialize-deserialize-11.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import dotenv from "dotenv";

/**
* @description Serialize and deserialize so-called incompleted transaction (chunked), set node account ids and execute it
* @description Serialize and deserialize so-called incomplete transaction (chunked), set node account ids and execute it
*/

async function main() {
Expand Down
2 changes: 1 addition & 1 deletion examples/serialize-deserialize-12.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import dotenv from "dotenv";

/**
* @description Create, serialize and deserialize so-called incompleted transaction, then freeze it, serialize and deserialize it again, and execute it
* @description Create, serialize and deserialize so-called incomplete transaction, then freeze it, serialize and deserialize it again, and execute it
*/

async function main() {
Expand Down
2 changes: 1 addition & 1 deletion examples/serialize-deserialize-3.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import dotenv from "dotenv";

/**
* @description Serialize and deserialize so-called incompleted transaction, and execute it
* @description Serialize and deserialize so-called incomplete transaction, and execute it
*/

async function main() {
Expand Down
2 changes: 1 addition & 1 deletion examples/serialize-deserialize-4.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import dotenv from "dotenv";

/**
* @description Serialize and deserialize so-called incompleted transaction, set node account ids and execute it
* @description Serialize and deserialize so-called incomplete transaction, set node account ids and execute it
*/

async function main() {
Expand Down
2 changes: 1 addition & 1 deletion examples/serialize-deserialize-5.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
import dotenv from "dotenv";

/**
* @description Serialize and deserialize so-called incompleted transaction, set transaction id and execute it
* @description Serialize and deserialize so-called incomplete transaction, set transaction id and execute it
*/

async function main() {
Expand Down
2 changes: 1 addition & 1 deletion examples/serialize-deserialize-6.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import dotenv from "dotenv";

/**
* @description Serialize and deserialize so-called incompleted transaction, update and execute it
* @description Serialize and deserialize so-called incomplete transaction, update and execute it
*/

async function main() {
Expand Down
2 changes: 1 addition & 1 deletion examples/serialize-deserialize-8.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import dotenv from "dotenv";

/**
* @description Serialize and deserialize so-called incompleted transaction (chunked), and execute it
* @description Serialize and deserialize so-called incomplete transaction (chunked), and execute it
*/

async function main() {
Expand Down
2 changes: 1 addition & 1 deletion examples/serialize-deserialize-9.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import dotenv from "dotenv";

/**
* @description Serialize and deserialize so-called incompleted transaction (chunked), update and execute it
* @description Serialize and deserialize so-called incomplete transaction (chunked), update and execute it
*/

async function main() {
Expand Down
117 changes: 95 additions & 22 deletions src/file/FileAppendTransaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import FileId from "./FileId.js";
import TransactionId from "../transaction/TransactionId.js";
import Timestamp from "../Timestamp.js";
import List from "../transaction/List.js";
import AccountId from "../account/AccountId.js";

/**
* @namespace proto
Expand All @@ -40,9 +41,9 @@ import List from "../transaction/List.js";
*/

/**
* @typedef {import("../PublicKey.js").default} PublicKey
* @typedef {import("../channel/Channel.js").default} Channel
* @typedef {import("../client/Client.js").default<Channel, *>} Client
* @typedef {import("../account/AccountId.js").default} AccountId
* @typedef {import("../transaction/TransactionResponse.js").default} TransactionResponse
* @typedef {import("../schedule/ScheduleCreateTransaction.js").default} ScheduleCreateTransaction
*/
Expand Down Expand Up @@ -220,6 +221,21 @@ export default class FileAppendTransaction extends Transaction {
return this;
}

/**
* @override
* @returns {number}
*/
getRequiredChunks() {
if (this._contents == null) {
return 0;
}

const result = Math.floor(
(this._contents.length + (this._chunkSize - 1)) / this._chunkSize,
);
return result;
}

/**
* @returns {?Uint8Array}
*/
Expand Down Expand Up @@ -301,16 +317,6 @@ export default class FileAppendTransaction extends Transaction {
return this;
}

const chunks = Math.floor(
(this._contents.length + (this._chunkSize - 1)) / this._chunkSize,
);

if (chunks > this._maxChunks) {
throw new Error(
`Contents with size ${this._contents.length} too long for ${this._maxChunks} chunks`,
);
}

let nextTransactionId = this._getTransactionId();

// Hack around the locked list. Should refactor a bit to remove such code
Expand All @@ -320,7 +326,7 @@ export default class FileAppendTransaction extends Transaction {
this._transactionIds.clear();
this._signedTransactions.clear();

for (let chunk = 0; chunk < chunks; chunk++) {
for (let chunk = 0; chunk < this.getRequiredChunks(); chunk++) {
this._transactionIds.push(nextTransactionId);
this._transactionIds.advance();

Expand Down Expand Up @@ -379,6 +385,12 @@ export default class FileAppendTransaction extends Transaction {
* @returns {Promise<TransactionResponse[]>}
*/
async executeAll(client, requestTimeout) {
if (this.maxChunks && this.getRequiredChunks() > this.maxChunks) {
throw new Error(
`cannot execute \`FileAppendTransaction\` with more than ${this.maxChunks} chunks`,
);
}

if (!super._isFrozen()) {
this.freezeWith(client);
}
Expand Down Expand Up @@ -445,6 +457,77 @@ export default class FileAppendTransaction extends Transaction {
return "fileAppend";
}

/**
* Build all the transactions
* when transactions are not complete.
* @override
* @internal
*/
_buildIncompleteTransactions() {
const dummyAccountId = AccountId.fromString("0.0.0");
if (this._contents == null) {
throw new Error("contents is not set");
}

if (this.maxChunks && this.getRequiredChunks() > this.maxChunks) {
throw new Error(
`cannot build \`FileAppendTransaction\` with more than ${this.maxChunks} chunks`,
);
}

// Hack around the locked list. Should refactor a bit to remove such code
this._transactionIds.locked = false;

this._transactions.clear();
this._transactionIds.clear();
this._signedTransactions.clear();

for (let chunk = 0; chunk < this.getRequiredChunks(); chunk++) {
let nextTransactionId = TransactionId.generate(dummyAccountId);
this._transactionIds.push(nextTransactionId);
this._transactionIds.advance();

if (this._nodeAccountIds.list.length === 0) {
this._transactions.push(this._makeSignedTransaction(null));
} else {
for (const nodeAccountId of this._nodeAccountIds.list) {
this._signedTransactions.push(
this._makeSignedTransaction(nodeAccountId),
);
}
}
}

this._transactionIds.advance();
this._transactionIds.setLocked();
}

/**
* Build all the signed transactions
* @override
* @internal
*/
_buildAllTransactions() {
if (this.maxChunks && this.getRequiredChunks() > this.maxChunks) {
throw new Error(
`cannot build \`FileAppendTransaction\` with more than ${this.maxChunks} chunks`,
);
}
for (let i = 0; i < this._signedTransactions.length; i++) {
this._buildTransaction(i);
}
}

/**
* @returns {string}
*/
_getLogId() {
const timestamp = /** @type {import("../Timestamp.js").default} */ (
this._transactionIds.current.validStart
);
return `FileAppendTransaction:${timestamp.toString()}`;
}

/**
* @override
* @protected
Expand All @@ -463,16 +546,6 @@ export default class FileAppendTransaction extends Transaction {
: null,
};
}

/**
* @returns {string}
*/
_getLogId() {
const timestamp = /** @type {import("../Timestamp.js").default} */ (
this._transactionIds.current.validStart
);
return `FileAppendTransaction:${timestamp.toString()}`;
}
}

// eslint-disable-next-line @typescript-eslint/unbound-method
Expand Down
29 changes: 22 additions & 7 deletions src/transaction/Transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,16 @@ export default class Transaction extends Executable {
return this;
}

/**
* How many chunk sizes are expected
* @abstract
* @internal
* @returns {number}
*/
getRequiredChunks() {
return 1;
}

/**
* Sign the transaction with the private key
* **NOTE**: This is a thin wrapper around `.signWith()`
Expand Down Expand Up @@ -800,6 +810,11 @@ export default class Transaction extends Executable {
const isSingleSignature = signature instanceof Uint8Array;
const isArraySignature = Array.isArray(signature);

if (this.getRequiredChunks() > 1) {
throw new Error(
"Add signature is not supported for chunked transactions",
);
}
// Check if it is a single signature with NOT exactly one transaction
if (isSingleSignature && this._signedTransactions.length !== 1) {
throw new Error(
Expand Down Expand Up @@ -1048,9 +1063,9 @@ export default class Transaction extends Executable {
/**
* Build all the signed transactions from the node account IDs
*
* @private
* @internal
*/
_buildIncompletedTransactions() {
_buildIncompleteTransactions() {
if (this._nodeAccountIds.length == 0) {
this._transactions.setList([this._makeSignedTransaction(null)]);
} else {
Expand Down Expand Up @@ -1196,7 +1211,7 @@ export default class Transaction extends Executable {
// Build all the transactions without signing
this._buildAllTransactions();
} else {
this._buildIncompletedTransactions();
this._buildIncompleteTransactions();
}

// Construct and encode the transaction list
Expand Down Expand Up @@ -1456,7 +1471,7 @@ export default class Transaction extends Executable {
/**
* Build each signed transaction in a loop
*
* @private
* @internal
*/
_buildAllTransactions() {
for (let i = 0; i < this._signedTransactions.length; i++) {
Expand Down Expand Up @@ -1492,7 +1507,7 @@ export default class Transaction extends Executable {
/**
* Build a transaction at a particular index
*
* @private
* @internal
* @param {number} index
*/
_buildTransaction(index) {
Expand All @@ -1502,9 +1517,9 @@ export default class Transaction extends Executable {
}
}

// In case when an incompleted transaction is created, serialized and
// In case when an incomplete transaction is created, serialized and
// deserialized,and then the transaction being frozen, the copy of the
// incompleted transaction must be updated in order to be prepared for execution
// incomplete transaction must be updated in order to be prepared for execution
if (this._transactions.list[index] != null) {
this._transactions.set(index, {
signedTransactionBytes:
Expand Down
Loading

0 comments on commit 6430e93

Please sign in to comment.