Skip to content

Commit fc39259

Browse files
authored
feat: hip-1300 - tests for large transactions (#2560)
Signed-off-by: emiliyank <e.kadiyski@gmail.com>
1 parent 3cd00ca commit fc39259

1 file changed

Lines changed: 356 additions & 0 deletions

File tree

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
package com.hedera.hashgraph.sdk.test.integration;
3+
4+
import static org.assertj.core.api.Assertions.assertThat;
5+
import static org.junit.jupiter.api.Assertions.assertThrows;
6+
7+
import com.hedera.hashgraph.sdk.*;
8+
import java.util.Arrays;
9+
import java.util.Objects;
10+
import org.junit.jupiter.api.Assumptions;
11+
import org.junit.jupiter.api.DisplayName;
12+
import org.junit.jupiter.api.Test;
13+
14+
public class LargeTransactionSystemAccountIntegrationTest {
15+
16+
private static final int LARGE_CONTENT_SIZE_BYTES = 100 * 1024;
17+
private static final int SIZE_THRESHOLD_BYTES = 6 * 1024;
18+
private static final int EXTENDED_SIZE_LIMIT_BYTES = 130 * 1024;
19+
20+
@Test
21+
@DisplayName("Privileged system payer can create files larger than 6kb")
22+
void privilegedSystemAccountCanCreateLargeFile() throws Exception {
23+
try (var testEnv = createSystemAccountTestEnv()) {
24+
var largeContents = new byte[LARGE_CONTENT_SIZE_BYTES];
25+
Arrays.fill(largeContents, (byte) 1);
26+
27+
var transaction = new FileCreateTransaction()
28+
.setKeys(testEnv.operatorKey)
29+
.setContents(largeContents)
30+
.setTransactionMemo("HIP-1300 create large file")
31+
.setMaxTransactionFee(new Hbar(20));
32+
33+
transaction.freezeWith(testEnv.client);
34+
35+
var serialized = transaction.toBytes();
36+
assertThat(serialized.length).isGreaterThan(SIZE_THRESHOLD_BYTES);
37+
assertThat(serialized.length).isLessThan(EXTENDED_SIZE_LIMIT_BYTES);
38+
39+
var receipt = transaction.execute(testEnv.client).getReceipt(testEnv.client);
40+
assertThat(receipt.fileId).isNotNull();
41+
42+
new FileDeleteTransaction()
43+
.setFileId(Objects.requireNonNull(receipt.fileId))
44+
.execute(testEnv.client)
45+
.getReceipt(testEnv.client);
46+
}
47+
}
48+
49+
@Test
50+
@DisplayName("Privileged system payer can update files with contents larger than 6kb")
51+
void privilegedSystemAccountCanUpdateLargeFile() throws Exception {
52+
try (var testEnv = createSystemAccountTestEnv()) {
53+
var initialContents = "test".getBytes();
54+
var fileId = new FileCreateTransaction()
55+
.setKeys(testEnv.operatorKey)
56+
.setContents(initialContents)
57+
.setTransactionMemo("HIP-1300 initial file")
58+
.execute(testEnv.client)
59+
.getReceipt(testEnv.client)
60+
.fileId;
61+
62+
Objects.requireNonNull(fileId);
63+
64+
var updatedContents = new byte[LARGE_CONTENT_SIZE_BYTES];
65+
Arrays.fill(updatedContents, (byte) 2);
66+
67+
var transaction = new FileUpdateTransaction()
68+
.setFileId(fileId)
69+
.setContents(updatedContents)
70+
.setTransactionMemo("HIP-1300 update large file")
71+
.setMaxTransactionFee(new Hbar(20));
72+
73+
transaction.freezeWith(testEnv.client);
74+
75+
var serialized = transaction.toBytes();
76+
assertThat(serialized.length).isGreaterThan(SIZE_THRESHOLD_BYTES);
77+
assertThat(serialized.length).isLessThan(EXTENDED_SIZE_LIMIT_BYTES);
78+
79+
transaction.execute(testEnv.client).getReceipt(testEnv.client);
80+
81+
new FileDeleteTransaction()
82+
.setFileId(fileId)
83+
.execute(testEnv.client)
84+
.getReceipt(testEnv.client);
85+
}
86+
}
87+
88+
@Test
89+
@DisplayName("Privileged system payer can append to file making it larger than 6kb")
90+
void privilegedSystemAccountCanAppendLargeFile() throws Exception {
91+
try (var testEnv = createSystemAccountTestEnv()) {
92+
// 1. Create a small file
93+
var fileId = new FileCreateTransaction()
94+
.setKeys(testEnv.operatorKey)
95+
.setContents("start".getBytes())
96+
.execute(testEnv.client)
97+
.getReceipt(testEnv.client)
98+
.fileId;
99+
100+
Objects.requireNonNull(fileId);
101+
102+
// 2. Append large content - need to set max chunks for content over default limit
103+
var largeContents = new byte[LARGE_CONTENT_SIZE_BYTES];
104+
Arrays.fill(largeContents, (byte) 3);
105+
106+
var transaction = new FileAppendTransaction()
107+
.setFileId(fileId)
108+
.setContents(largeContents)
109+
.setMaxChunks(100)
110+
.setMaxTransactionFee(new Hbar(20))
111+
.setTransactionMemo("HIP-1300 append large file");
112+
113+
transaction.freezeWith(testEnv.client);
114+
assertThat(transaction.toBytes().length).isLessThan(EXTENDED_SIZE_LIMIT_BYTES);
115+
assertThat(transaction.toBytes().length).isGreaterThan(SIZE_THRESHOLD_BYTES);
116+
transaction.execute(testEnv.client).getReceipt(testEnv.client);
117+
118+
new FileDeleteTransaction()
119+
.setFileId(fileId)
120+
.execute(testEnv.client)
121+
.getReceipt(testEnv.client);
122+
}
123+
}
124+
125+
@Test
126+
@DisplayName("Non-privileged account cannot create files larger than 6kb")
127+
void nonPrivilegedAccountCannotCreateLargeFile() throws Exception {
128+
try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount(new Hbar(50))) {
129+
// useThrowawayAccount creates a non-privileged account for testing
130+
// This ensures the test runs even if OPERATOR_ID is 0.0.2 or 0.0.50
131+
132+
var largeContents = new byte[LARGE_CONTENT_SIZE_BYTES];
133+
Arrays.fill(largeContents, (byte) 1);
134+
135+
var transaction = new FileCreateTransaction()
136+
.setKeys(testEnv.operatorKey)
137+
.setContents(largeContents)
138+
.setTransactionMemo("Should fail - too large for non-privileged")
139+
.setMaxTransactionFee(new Hbar(20));
140+
141+
var exception = assertThrows(PrecheckStatusException.class, () -> {
142+
transaction.freezeWith(testEnv.client);
143+
transaction.execute(testEnv.client);
144+
});
145+
146+
assertThat(exception.status).isEqualTo(Status.TRANSACTION_OVERSIZE);
147+
}
148+
}
149+
150+
@Test
151+
@DisplayName("Non-privileged account cannot create account with large KeyList larger than 6kb")
152+
void nonPrivilegedAccountCannotCreateAccountWithLargeKeyList() throws Exception {
153+
try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount(new Hbar(50))) {
154+
// useThrowawayAccount creates a non-privileged account for testing
155+
// This ensures the test runs even if OPERATOR_ID is 0.0.2 or 0.0.50
156+
157+
// Generate 180 key pairs to ensure transaction size exceeds 6KB
158+
var numberOfKeys = 180;
159+
var publicKeys = new PublicKey[numberOfKeys];
160+
161+
for (int i = 0; i < numberOfKeys; i++) {
162+
var key = PrivateKey.generateED25519();
163+
publicKeys[i] = key.getPublicKey();
164+
}
165+
166+
// Create a KeyList with all public keys
167+
var keyList = new KeyList();
168+
for (var publicKey : publicKeys) {
169+
keyList.add(publicKey);
170+
}
171+
172+
var transaction = new AccountCreateTransaction()
173+
.setKeyWithoutAlias(keyList)
174+
.setInitialBalance(new Hbar(1))
175+
.setTransactionMemo("Should fail - too large for non-privileged")
176+
.setMaxTransactionFee(new Hbar(20));
177+
178+
var exception = assertThrows(PrecheckStatusException.class, () -> {
179+
transaction.freezeWith(testEnv.client);
180+
transaction.execute(testEnv.client);
181+
});
182+
183+
assertThat(exception.status).isEqualTo(Status.TRANSACTION_OVERSIZE);
184+
}
185+
}
186+
187+
@Test
188+
@DisplayName("Privileged account can create file just under 130kb limit")
189+
void privilegedAccountAtNear130KBLimit() throws Exception {
190+
try (var testEnv = createSystemAccountTestEnv()) {
191+
// Create content that will result in transaction just under 130KB
192+
var contentSize = 128 * 1024; // 128KB content
193+
var largeContents = new byte[contentSize];
194+
Arrays.fill(largeContents, (byte) 1);
195+
196+
var transaction = new FileCreateTransaction()
197+
.setKeys(testEnv.operatorKey)
198+
.setContents(largeContents)
199+
.setTransactionMemo("HIP-1300 near limit test")
200+
.setMaxTransactionFee(new Hbar(20));
201+
202+
transaction.freezeWith(testEnv.client);
203+
204+
var serialized = transaction.toBytes();
205+
// Should be less than 130KB total
206+
assertThat(serialized.length).isLessThan(EXTENDED_SIZE_LIMIT_BYTES);
207+
// But definitely over 6KB
208+
assertThat(serialized.length).isGreaterThan(SIZE_THRESHOLD_BYTES);
209+
210+
var receipt = transaction.execute(testEnv.client).getReceipt(testEnv.client);
211+
assertThat(receipt.fileId).isNotNull();
212+
213+
new FileDeleteTransaction()
214+
.setFileId(Objects.requireNonNull(receipt.fileId))
215+
.execute(testEnv.client)
216+
.getReceipt(testEnv.client);
217+
}
218+
}
219+
220+
@Test
221+
@DisplayName("Non-privileged account can create file under 6kb limit")
222+
void nonPrivilegedAccountCanCreateSmallFile() throws Exception {
223+
try (var testEnv = new IntegrationTestEnv(1).useThrowawayAccount(new Hbar(50))) {
224+
// useThrowawayAccount creates a non-privileged account for testing
225+
// This ensures the test runs even if OPERATOR_ID is 0.0.2 or 0.0.50
226+
227+
// Small content that stays well under 6KB
228+
var smallContents = new byte[2 * 1024]; // 2KB
229+
Arrays.fill(smallContents, (byte) 1);
230+
231+
var transaction = new FileCreateTransaction()
232+
.setKeys(testEnv.operatorKey)
233+
.setContents(smallContents)
234+
.setTransactionMemo("Small file test")
235+
.setMaxTransactionFee(new Hbar(5));
236+
237+
transaction.freezeWith(testEnv.client);
238+
239+
var serialized = transaction.toBytes();
240+
assertThat(serialized.length).isLessThan(SIZE_THRESHOLD_BYTES);
241+
242+
var receipt = transaction.execute(testEnv.client).getReceipt(testEnv.client);
243+
assertThat(receipt.fileId).isNotNull();
244+
245+
new FileDeleteTransaction()
246+
.setFileId(Objects.requireNonNull(receipt.fileId))
247+
.execute(testEnv.client)
248+
.getReceipt(testEnv.client);
249+
}
250+
}
251+
252+
@Test
253+
@DisplayName("Treasury account (0.0.2) can create large files")
254+
void treasuryAccountCanCreateLargeFile() throws Exception {
255+
try (var testEnv = createSystemAccountTestEnv()) {
256+
// This test specifically validates 0.0.2 if that's the operator
257+
Assumptions.assumeTrue(testEnv.operatorId.num == 2, "Test requires treasury account 0.0.2");
258+
259+
var largeContents = new byte[LARGE_CONTENT_SIZE_BYTES];
260+
Arrays.fill(largeContents, (byte) 1);
261+
262+
var transaction = new FileCreateTransaction()
263+
.setKeys(testEnv.operatorKey)
264+
.setContents(largeContents)
265+
.setTransactionMemo("HIP-1300 test with 0.0.2")
266+
.setMaxTransactionFee(new Hbar(20));
267+
268+
transaction.freezeWith(testEnv.client);
269+
270+
var serialized = transaction.toBytes();
271+
assertThat(serialized.length).isGreaterThan(SIZE_THRESHOLD_BYTES);
272+
assertThat(serialized.length).isLessThan(EXTENDED_SIZE_LIMIT_BYTES);
273+
274+
var receipt = transaction.execute(testEnv.client).getReceipt(testEnv.client);
275+
assertThat(receipt.fileId).isNotNull();
276+
277+
new FileDeleteTransaction()
278+
.setFileId(Objects.requireNonNull(receipt.fileId))
279+
.execute(testEnv.client)
280+
.getReceipt(testEnv.client);
281+
}
282+
}
283+
284+
@Test
285+
@DisplayName("Privileged system payer can create account with large KeyList (180 keys)")
286+
void privilegedSystemAccountCanCreateAccountWithLargeKeyList() throws Exception {
287+
try (var testEnv = createSystemAccountTestEnv()) {
288+
// Generate 180 key pairs to ensure transaction size exceeds 6KB
289+
// With 100 keys we got ~3709 bytes, so 180 keys should give us ~6676 bytes
290+
var numberOfKeys = 180;
291+
var privateKeys = new PrivateKey[numberOfKeys];
292+
var publicKeys = new PublicKey[numberOfKeys];
293+
294+
for (int i = 0; i < numberOfKeys; i++) {
295+
var key = PrivateKey.generateED25519();
296+
privateKeys[i] = key;
297+
publicKeys[i] = key.getPublicKey();
298+
}
299+
300+
// Create a KeyList with all public keys
301+
var keyList = new KeyList();
302+
for (var publicKey : publicKeys) {
303+
keyList.add(publicKey);
304+
}
305+
306+
var transaction = new AccountCreateTransaction()
307+
.setKeyWithoutAlias(keyList)
308+
.setInitialBalance(new Hbar(1))
309+
.setTransactionMemo("HIP-1300 create account with large KeyList")
310+
.setMaxTransactionFee(new Hbar(20));
311+
312+
transaction.freezeWith(testEnv.client);
313+
314+
var serialized = transaction.toBytes();
315+
assertThat(serialized.length).isGreaterThan(SIZE_THRESHOLD_BYTES);
316+
assertThat(serialized.length).isLessThan(EXTENDED_SIZE_LIMIT_BYTES);
317+
318+
var receipt = transaction.execute(testEnv.client).getReceipt(testEnv.client);
319+
var accountId = Objects.requireNonNull(receipt.accountId);
320+
assertThat(accountId).isNotNull();
321+
}
322+
}
323+
324+
private IntegrationTestEnv createSystemAccountTestEnv() throws Exception {
325+
var testEnv = new IntegrationTestEnv(1);
326+
327+
if (!isPrivilegedSystemAccount(testEnv.operatorId)) {
328+
var systemAccountId = System.getProperty("SYSTEM_ACCOUNT_ID");
329+
var systemAccountKey = System.getProperty("SYSTEM_ACCOUNT_KEY");
330+
331+
if (systemAccountId != null
332+
&& !systemAccountId.isBlank()
333+
&& systemAccountKey != null
334+
&& !systemAccountKey.isBlank()) {
335+
var accountId = AccountId.fromString(systemAccountId);
336+
var privateKey = PrivateKey.fromString(systemAccountKey);
337+
testEnv.client.setOperator(accountId, privateKey);
338+
testEnv.operatorId = accountId;
339+
testEnv.operatorKey = privateKey.getPublicKey();
340+
}
341+
}
342+
343+
Assumptions.assumeTrue(
344+
isPrivilegedSystemAccount(testEnv.operatorId),
345+
"System account credentials (0.0.2 or 0.0.50) are required for large transaction tests");
346+
347+
return testEnv;
348+
}
349+
350+
private boolean isPrivilegedSystemAccount(AccountId accountId) {
351+
return accountId != null
352+
&& accountId.shard == 0
353+
&& accountId.realm == 0
354+
&& (accountId.num == 2 || accountId.num == 50);
355+
}
356+
}

0 commit comments

Comments
 (0)