Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: file append transaction id after deserialization #2583

Merged
merged 9 commits into from
Oct 16, 2024
38 changes: 36 additions & 2 deletions src/file/FileAppendTransaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
* @param {Uint8Array | string} [props.contents]
* @param {number} [props.maxChunks]
* @param {number} [props.chunkSize]
* @param {number} [props.chunkInterval]
*/
constructor(props = {}) {
super();
Expand Down Expand Up @@ -88,6 +89,12 @@
*/
this._chunkSize = 4096;

/**
* @private
* @type {number}
*/
this._chunkInterval = 10;

this._defaultMaxTransactionFee = new Hbar(5);

if (props.fileId != null) {
Expand All @@ -106,6 +113,10 @@
this.setChunkSize(props.chunkSize);
}

if (props.chunkInterval != null) {
this.setChunkInterval(props.chunkInterval);
}

Check warning on line 118 in src/file/FileAppendTransaction.js

View check run for this annotation

Codecov / codecov/patch

src/file/FileAppendTransaction.js#L117-L118

Added lines #L117 - L118 were not covered by tests

/** @type {List<TransactionId>} */
this._transactionIds = new List();
}
Expand Down Expand Up @@ -300,6 +311,22 @@
return this;
}

/**
* @returns {number}
*/
get chunkInterval() {
return this._chunkInterval;
}

Check warning on line 319 in src/file/FileAppendTransaction.js

View check run for this annotation

Codecov / codecov/patch

src/file/FileAppendTransaction.js#L318-L319

Added lines #L318 - L319 were not covered by tests

/**
* @param {number} chunkInterval The valid start interval between chunks in nanoseconds
* @returns {this}
*/
setChunkInterval(chunkInterval) {
this._chunkInterval = chunkInterval;
return this;
}

/**
* Freeze this transaction from further modification to prepare for
* signing or serialization.
Expand Down Expand Up @@ -344,7 +371,7 @@
).seconds,
/** @type {Timestamp} */ (
nextTransactionId.validStart
).nanos.add(1),
).nanos.add(this._chunkInterval),
),
);
}
Expand Down Expand Up @@ -465,6 +492,10 @@
*/
_buildIncompleteTransactions() {
const dummyAccountId = AccountId.fromString("0.0.0");
const accountId = this.transactionId?.accountId || dummyAccountId;
const validStart =
this.transactionId?.validStart || Timestamp.fromDate(new Date());

if (this._contents == null) {
throw new Error("contents is not set");
}
Expand All @@ -483,7 +514,10 @@
this._signedTransactions.clear();

for (let chunk = 0; chunk < this.getRequiredChunks(); chunk++) {
let nextTransactionId = TransactionId.generate(dummyAccountId);
let nextTransactionId = TransactionId.withValidStart(
accountId,
validStart.plusNanos(this._chunkInterval * chunk),
);
this._transactionIds.push(nextTransactionId);
this._transactionIds.advance();

Expand Down
56 changes: 56 additions & 0 deletions test/integration/FileAppendIntegrationTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
FileInfoQuery,
Hbar,
Status,
Timestamp,
TransactionId,
} from "../../src/exports.js";
import { bigContents } from "./contents.js";
import IntegrationTestEnv from "./client/NodeIntegrationTestEnv.js";
Expand All @@ -22,6 +24,7 @@ describe("FileAppend", function () {
newContents = generateUInt8Array(newContentsLength);
operatorKey = env.operatorKey.publicKey;
});

it("should be executable", async function () {
let response = await new FileCreateTransaction()
.setKeys([operatorKey])
Expand Down Expand Up @@ -351,6 +354,59 @@ describe("FileAppend", function () {
expect(receipt.status).to.be.equal(Status.Success);
});

it("should keep transaction id after non-frozen deserialization", async function () {
const operatorKey = env.operatorKey.publicKey;

let response = await new FileCreateTransaction()
.setKeys([operatorKey])
.setContents(Buffer.from(""))
.execute(env.client);

let { fileId } = await response.getReceipt(env.client);

const chunkInterval = 230;
const validStart = Timestamp.fromDate(new Date());

const tx = new FileAppendTransaction()
.setTransactionId(
TransactionId.withValidStart(env.operatorId, validStart),
)
.setFileId(fileId)
.setChunkInterval(chunkInterval)
.setChunkSize(1000)
.setContents(newContents);

const txBytes = tx.toBytes();
const txFromBytes = FileAppendTransaction.fromBytes(txBytes);

expect(
txFromBytes.transactionId.accountId._toProtobuf(),
).to.be.deep.equal(env.operatorId?._toProtobuf());
expect(txFromBytes.transactionId.validStart).to.be.deep.equal(
validStart,
);

txFromBytes._transactionIds.list.forEach(
(transactionId, index, array) => {
if (index > 0) {
const previousTimestamp = array[index - 1].validStart;
const currentTimestamp = transactionId.validStart;
const difference =
currentTimestamp.nanos - previousTimestamp.nanos;
expect(difference).to.be.equal(chunkInterval);
}
},
);
ivaylonikolov7 marked this conversation as resolved.
Show resolved Hide resolved

txFromBytes.freezeWith(env.client);
await txFromBytes.sign(env.operatorKey);

const receipt = await (
await txFromBytes.execute(env.client)
).getReceipt(env.client);
expect(receipt.status).to.be.equal(Status.Success);
});

after(async function () {
await env.close();
});
Expand Down
48 changes: 48 additions & 0 deletions test/unit/FileAppendTransaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,52 @@ describe("FileAppendTransaction", function () {
expect(body.fileAppend.contents.length).to.be.equal(1000);
expect(body.fileAppend.contents[0]).to.be.equal(51);
});

it("setChunkInterval()", function () {
const spenderAccountId1 = new AccountId(7);
const fileId = new FileId(8);
const nodeAccountId = new AccountId(10, 11, 12);
const timestamp1 = new Timestamp(14, 15);
const chunkInterval = 200;

let transaction = new FileAppendTransaction()
.setTransactionId(
TransactionId.withValidStart(spenderAccountId1, timestamp1),
)
.setNodeAccountIds([nodeAccountId])
.setFileId(fileId)
.setChunkSize(1000)
.setChunkInterval(chunkInterval)
.setContents("1".repeat(1000) + "2".repeat(1000) + "3".repeat(1000))
.freeze();

expect(transaction._transactionIds.list.length).to.be.equal(3);

for (let i = 0; i < 3; i++) {
ivaylonikolov7 marked this conversation as resolved.
Show resolved Hide resolved
let body = transaction._makeTransactionBody(nodeAccountId);

expect(body.transactionID).to.deep.equal(
transaction._transactionIds.list[i]._toProtobuf(),
);

if (i > 0) {
expect(
transaction._transactionIds.list[i].validStart.nanos.sub(
transaction._transactionIds.list[i - 1].validStart
.nanos,
),
).to.deep.equal(Long.fromNumber(chunkInterval));
}

if (i === 2) {
expect(
transaction._transactionIds.list[2].validStart.nanos.sub(
transaction._transactionIds.list[0].validStart.nanos,
),
).to.deep.equal(Long.fromNumber(chunkInterval * 2));
}

transaction._transactionIds.advance();
}
});
});