Skip to content

Commit eded008

Browse files
authored
HIP-1056 Left trim contract storage value read and value written (#12290)
- Left trim contract storage value read and value written Signed-off-by: Xin Li <xin@hashgraph.com>
1 parent e67cf81 commit eded008

6 files changed

Lines changed: 66 additions & 16 deletions

File tree

common/src/main/java/org/hiero/mirror/common/domain/transaction/StateChangeContext.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,10 +233,11 @@ private void processContractStorageChange(MapUpdateChange mapUpdate) {
233233

234234
private void processContractStorageChange(SlotKey slotKey, BytesValue valueWritten) {
235235
slotKey = normalize(slotKey);
236-
contractStorageChanges.put(slotKey, valueWritten);
236+
final var trimmed = DomainUtils.trim(valueWritten);
237+
contractStorageChanges.put(slotKey, trimmed);
237238
contractStorageChangesIndexed
238239
.computeIfAbsent(slotKey.getContractID(), c -> new ArrayList<>())
239-
.add(new SlotValue(slotKey.getKey(), valueWritten));
240+
.add(new SlotValue(slotKey.getKey(), trimmed));
240241
}
241242

242243
private void processNodeStateChange(MapUpdateChange mapUpdate) {

common/src/main/java/org/hiero/mirror/common/util/DomainUtils.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.google.common.primitives.Longs;
88
import com.google.protobuf.ByteOutput;
99
import com.google.protobuf.ByteString;
10+
import com.google.protobuf.BytesValue;
1011
import com.google.protobuf.Internal;
1112
import com.google.protobuf.UnsafeByteOperations;
1213
import com.hedera.services.stream.proto.HashObject;
@@ -339,6 +340,28 @@ public static byte[] trim(final byte[] data) {
339340
return ArrayUtils.subarray(data, i, data.length);
340341
}
341342

343+
public static ByteString trim(final ByteString data) {
344+
if (data == null || data.isEmpty() || data.byteAt(0) != 0) {
345+
return data;
346+
}
347+
348+
final byte[] value = DomainUtils.toBytes(data);
349+
final byte[] trimmed = trim(value);
350+
351+
return trimmed == value ? data : DomainUtils.fromBytes(trimmed);
352+
}
353+
354+
public static BytesValue trim(final BytesValue data) {
355+
if (data == null) {
356+
return null;
357+
}
358+
359+
final var value = data.getValue();
360+
final var trimmed = trim(value);
361+
362+
return trimmed == value ? data : BytesValue.of(trimmed);
363+
}
364+
342365
public static byte[] toEvmAddress(ContractID contractId) {
343366
if (contractId == null || ContractID.getDefaultInstance().equals(contractId)) {
344367
throw new InvalidEntityException("Invalid ContractID");

common/src/test/java/org/hiero/mirror/common/util/DomainUtilsTest.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import static org.junit.jupiter.params.provider.Arguments.arguments;
1313

1414
import com.google.protobuf.ByteString;
15+
import com.google.protobuf.BytesValue;
1516
import com.google.protobuf.Internal;
1617
import com.google.protobuf.UnsafeByteOperations;
1718
import com.hedera.services.stream.proto.HashObject;
@@ -43,7 +44,7 @@
4344
import org.junit.jupiter.params.provider.NullAndEmptySource;
4445
import org.junit.jupiter.params.provider.ValueSource;
4546

46-
class DomainUtilsTest {
47+
final class DomainUtilsTest {
4748

4849
private static final String ED25519_KEY = "60beee88a761e079f71b03b5fe041979369e96450a7455b203a2578c8a7d4852";
4950
private static final String ECDSA_384_KEY = "0000001365636473612d736861322d6e69737470333834000000086e697374703338"
@@ -501,6 +502,29 @@ void toSnakeCaseReturnsInputAsIsForBlankOrNull(final String input) {
501502
assertThat(DomainUtils.toSnakeCase(input)).isEqualTo(input);
502503
}
503504

505+
@Test
506+
void trimByteString() {
507+
assertThat(DomainUtils.trim((ByteString) null)).isNull();
508+
assertThat(DomainUtils.trim(ByteString.EMPTY)).isSameAs(ByteString.EMPTY);
509+
final var shouldNotTrim = ByteString.copyFrom(new byte[] {0x1, 0x2});
510+
assertThat(DomainUtils.trim(shouldNotTrim)).isSameAs(shouldNotTrim);
511+
final var shouldTrim = ByteString.copyFrom(new byte[] {0x0, 0x0, 0x1, 0x0, 0x2});
512+
final var expected = ByteString.copyFrom(new byte[] {0x1, 0x0, 0x2});
513+
assertThat(DomainUtils.trim(shouldTrim)).isEqualTo(expected);
514+
}
515+
516+
@Test
517+
void trimBytesValue() {
518+
assertThat(DomainUtils.trim((BytesValue) null)).isNull();
519+
final var emptyValue = BytesValue.of(ByteString.EMPTY);
520+
assertThat(DomainUtils.trim(emptyValue)).isSameAs(emptyValue);
521+
final var shouldNotTrim = BytesValue.of(ByteString.copyFrom(new byte[] {0x1, 0x2}));
522+
assertThat(DomainUtils.trim(shouldNotTrim)).isSameAs(shouldNotTrim);
523+
final var shouldTrim = BytesValue.of(ByteString.copyFrom(new byte[] {0x0, 0x0, 0x1, 0x0, 0x2}));
524+
final var expected = BytesValue.of(ByteString.copyFrom(new byte[] {0x1, 0x0, 0x2}));
525+
assertThat(DomainUtils.trim(shouldTrim)).isEqualTo(expected);
526+
}
527+
504528
@ParameterizedTest
505529
@MethodSource("provideByteArraysForTrim")
506530
void testTrim(byte[] input, byte[] expected) {

importer/src/main/java/org/hiero/mirror/importer/downloader/block/transformer/AbstractBlockTransactionTransformer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ private void transformContractSlotUsage(
271271

272272
for (var slotRead : contractSlotUsage.getSlotReadsList()) {
273273
ByteString slot = null;
274-
var valueRead = slotRead.getReadValue();
274+
var valueRead = DomainUtils.trim(slotRead.getReadValue());
275275
var storageChangeBuilder = StorageChange.newBuilder().setValueRead(valueRead);
276276
if (slotRead.getIdentifierCase() == SlotRead.IdentifierCase.INDEX) {
277277
int index = slotRead.getIndex();

importer/src/main/java/org/hiero/mirror/importer/downloader/block/transformer/Utils.java

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,6 @@ static LogsBloomFilter bloomForAll(Collection<LogsBloomFilter> bloomFilters) {
5858
}
5959

6060
static byte[] leftPad32(ByteString topic) {
61-
byte[] bytes = DomainUtils.toBytes(topic);
62-
int length = bytes.length;
63-
if (length < TOPIC_SIZE_BYTES) {
64-
byte[] padded = new byte[TOPIC_SIZE_BYTES];
65-
System.arraycopy(bytes, 0, padded, TOPIC_SIZE_BYTES - length, length);
66-
return padded;
67-
}
68-
69-
return bytes;
61+
return DomainUtils.leftPadBytes(DomainUtils.toBytes(topic), TOPIC_SIZE_BYTES);
7062
}
7163
}

importer/src/test/java/org/hiero/mirror/importer/parser/domain/BlockTransactionBuilder.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
import com.hederahashgraph.api.proto.java.TransactionBody;
7373
import com.hederahashgraph.api.proto.java.TransactionRecord;
7474
import jakarta.inject.Named;
75+
import java.security.SecureRandom;
7576
import java.time.Instant;
7677
import java.util.ArrayList;
7778
import java.util.Collections;
@@ -104,6 +105,7 @@
104105
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
105106
public class BlockTransactionBuilder {
106107

108+
private final SecureRandom random = new SecureRandom();
107109
private final RecordItemBuilder recordItemBuilder;
108110

109111
private static StateChanges buildFileIdStateChanges(RecordItem recordItem) {
@@ -799,7 +801,7 @@ private void convertContractStateChanges(
799801
// only read
800802
slotUsage.addSlotReads(SlotRead.newBuilder()
801803
.setKey(storageChange.getSlot())
802-
.setReadValue(storageChange.getValueRead())
804+
.setReadValue(leftPad(storageChange.getValueRead()))
803805
.build());
804806
} else {
805807
int count = contractStorageSlotCounts.compute(contractId, (k, v) -> v == null ? 1 : v + 1);
@@ -820,8 +822,8 @@ private void convertContractStateChanges(
820822
.setMapUpdate(MapUpdateChange.newBuilder()
821823
.setKey(mapChangeKey)
822824
.setValue(MapChangeValue.newBuilder()
823-
.setSlotValueValue(
824-
SlotValue.newBuilder().setValue(valueWritten.getValue())))
825+
.setSlotValueValue(SlotValue.newBuilder()
826+
.setValue(leftPad(valueWritten.getValue()))))
825827
.build()));
826828
} else {
827829
// deleted
@@ -882,6 +884,14 @@ private ByteString getEvmAddress(ContractID contractId) {
882884
return ByteString.EMPTY;
883885
}
884886

887+
private ByteString leftPad(ByteString value) {
888+
if (random.nextBoolean()) {
889+
return value;
890+
}
891+
892+
return DomainUtils.fromBytes(DomainUtils.leftPadBytes(DomainUtils.toBytes(value), 32));
893+
}
894+
885895
private Timestamp timestamp(long consensusTimestamp) {
886896
var instant = Instant.ofEpochSecond(0, consensusTimestamp);
887897
return Utility.instantToTimestamp(instant);

0 commit comments

Comments
 (0)