From c7dd6e6c944b99a8ba0cafd5e637d3dc18123822 Mon Sep 17 00:00:00 2001 From: Miroslav Gatsanoga Date: Sun, 24 Dec 2023 23:30:24 +0200 Subject: [PATCH] fix: conditional records of hollow account creation via internal transfer with `EthereumTransaction` (#10539) Signed-off-by: Miroslav Gatsanoga Signed-off-by: Michael Tinker Co-authored-by: Michael Tinker --- .../app/workflows/handle/HandleWorkflow.java | 18 ++++++-- .../scope/HandleHederaNativeOperations.java | 17 +++++++ .../HandleHederaNativeOperationsTest.java | 45 ++++++++++++++++--- .../record-snapshots/LeakyCryptoTests.json | 1 + .../bdd/spec/utilops/domain/ParsedItem.java | 12 +++++ .../spec/utilops/domain/RecordSnapshot.java | 5 ++- .../spec/utilops/records/SnapshotModeOp.java | 13 ++++-- .../suites/leaky/LeakyCryptoTestsSuite.java | 4 ++ 8 files changed, 103 insertions(+), 12 deletions(-) create mode 100644 hedera-node/test-clients/record-snapshots/LeakyCryptoTests.json diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/HandleWorkflow.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/HandleWorkflow.java index 0da10d5c58bb..6d32033e85b6 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/HandleWorkflow.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/HandleWorkflow.java @@ -100,6 +100,7 @@ import com.swirlds.platform.system.transaction.ConsensusTransaction; import edu.umd.cs.findbugs.annotations.NonNull; import java.time.Instant; +import java.util.EnumSet; import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -113,6 +114,10 @@ public class HandleWorkflow { private static final Logger logger = LogManager.getLogger(HandleWorkflow.class); + private static final Set DISPATCHING_CONTRACT_TRANSACTIONS = EnumSet.of( + HederaFunctionality.CONTRACT_CREATE, + HederaFunctionality.CONTRACT_CALL, + HederaFunctionality.ETHEREUM_TRANSACTION); private final NetworkInfo networkInfo; private final PreHandleWorkflow preHandleWorkflow; @@ -437,8 +442,14 @@ private void handleUserTransaction( // Dispatch the transaction to the handler dispatcher.dispatchHandle(context); - // Possibly charge assessed fees for preceding child transactions - if (!recordListBuilder.precedingRecordBuilders().isEmpty()) { + // Possibly charge assessed fees for preceding child transactions; but + // only if not a contract operation, since these dispatches were already + // charged using gas. [FUTURE - stop setting transactionFee in recordBuilder + // at the point of dispatch, so we no longer need this special case here.] + final var isContractOp = + DISPATCHING_CONTRACT_TRANSACTIONS.contains(transactionInfo.functionality()); + if (!isContractOp + && !recordListBuilder.precedingRecordBuilders().isEmpty()) { // We intentionally charge fees even if the transaction failed (may need to update // mono-service to this behavior?) final var childFees = recordListBuilder.precedingRecordBuilders().stream() @@ -685,8 +696,9 @@ private record ValidationResult( /** * Rolls back the stack and sets the status of the transaction in case of a failure. + * * @param rollbackStack whether to rollback the stack. Will be false when the failure is due to a - * {@link HandleException} that is due to a contract call revert. + * {@link HandleException} that is due to a contract call revert. * @param status the status to set * @param stack the save point stack to rollback * @param recordListBuilder the record list builder to revert diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/scope/HandleHederaNativeOperations.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/scope/HandleHederaNativeOperations.java index 5bba8ec06890..5b90688026e9 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/scope/HandleHederaNativeOperations.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/scope/HandleHederaNativeOperations.java @@ -23,8 +23,10 @@ import static java.util.Objects.requireNonNull; import com.hedera.hapi.node.base.AccountID; +import com.hedera.hapi.node.base.Key; import com.hedera.hapi.node.base.ResponseCodeEnum; import com.hedera.hapi.node.token.CryptoTransferTransactionBody; +import com.hedera.hapi.node.token.CryptoUpdateTransactionBody; import com.hedera.hapi.node.transaction.TransactionBody; import com.hedera.node.app.service.contract.impl.annotations.TransactionScope; import com.hedera.node.app.service.token.ReadableAccountStore; @@ -33,6 +35,7 @@ import com.hedera.node.app.service.token.ReadableTokenStore; import com.hedera.node.app.service.token.api.TokenServiceApi; import com.hedera.node.app.service.token.records.CryptoCreateRecordBuilder; +import com.hedera.node.app.spi.fees.Fees; import com.hedera.node.app.spi.workflows.HandleContext; import com.hedera.node.app.spi.workflows.HandleException; import com.hedera.pbj.runtime.io.buffer.Bytes; @@ -101,12 +104,18 @@ public void setNonce(final long contractNumber, final long nonce) { final var synthTxn = TransactionBody.newBuilder() .cryptoCreateAccount(synthHollowAccountCreation(evmAddress)) .build(); + // Note the use of the null "verification assistant" callback; we don't want any // signing requirements enforced for this synthetic transaction try { final var childRecordBuilder = context.dispatchRemovablePrecedingTransaction( synthTxn, CryptoCreateRecordBuilder.class, null, context.payer()); childRecordBuilder.memo(LAZY_CREATION_MEMO); + + final var lazyCreateFees = context.dispatchComputeFees(synthTxn, context.payer()); + final var finalizationFees = getLazyCreationFinalizationFees(); + childRecordBuilder.transactionFee(lazyCreateFees.totalFee() + finalizationFees.totalFee()); + return childRecordBuilder.status(); } catch (final HandleException e) { // It is critically important we don't let HandleExceptions propagate to the workflow because @@ -166,4 +175,12 @@ public boolean checkForCustomFees(@NonNull final CryptoTransferTransactionBody o final var tokenServiceApi = context.serviceApi(TokenServiceApi.class); return tokenServiceApi.checkForCustomFees(op); } + + private Fees getLazyCreationFinalizationFees() { + final var updateTxnBody = + CryptoUpdateTransactionBody.newBuilder().key(Key.newBuilder().ecdsaSecp256k1(Bytes.EMPTY)); + final var synthTxn = + TransactionBody.newBuilder().cryptoUpdateAccount(updateTxnBody).build(); + return context.dispatchComputeFees(synthTxn, context.payer()); + } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/scope/HandleHederaNativeOperationsTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/scope/HandleHederaNativeOperationsTest.java index f8871dfa84c8..e37a2d0ee123 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/scope/HandleHederaNativeOperationsTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/scope/HandleHederaNativeOperationsTest.java @@ -34,6 +34,7 @@ import static com.hedera.node.app.service.contract.impl.test.TestHelpers.PARANOID_SOMEBODY; import static com.hedera.node.app.service.contract.impl.test.TestHelpers.SOMEBODY; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.tuweniToPbjBytes; +import static com.hedera.node.app.service.contract.impl.utils.SynthTxnUtils.LAZY_CREATION_MEMO; import static com.hedera.node.app.service.contract.impl.utils.SynthTxnUtils.synthHollowAccountCreation; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @@ -51,6 +52,7 @@ import com.hedera.hapi.node.base.Key; import com.hedera.hapi.node.state.token.Account; import com.hedera.hapi.node.token.CryptoTransferTransactionBody; +import com.hedera.hapi.node.token.CryptoUpdateTransactionBody; import com.hedera.hapi.node.transaction.TransactionBody; import com.hedera.node.app.service.contract.impl.exec.scope.HandleHederaNativeOperations; import com.hedera.node.app.service.contract.impl.exec.scope.VerificationStrategy; @@ -60,7 +62,9 @@ import com.hedera.node.app.service.token.ReadableTokenStore; import com.hedera.node.app.service.token.api.TokenServiceApi; import com.hedera.node.app.service.token.records.CryptoCreateRecordBuilder; +import com.hedera.node.app.spi.fees.Fees; import com.hedera.node.app.spi.workflows.HandleContext; +import com.hedera.pbj.runtime.io.buffer.Bytes; import java.util.function.Predicate; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -147,33 +151,64 @@ void getTokenUsesStore() { @Test void createsHollowAccountByDispatching() { - final var synthTxn = TransactionBody.newBuilder() + final var synthLazyCreate = TransactionBody.newBuilder() .cryptoCreateAccount(synthHollowAccountCreation(CANONICAL_ALIAS)) .build(); given(context.payer()).willReturn(A_NEW_ACCOUNT_ID); + when(context.dispatchRemovablePrecedingTransaction( - eq(synthTxn), eq(CryptoCreateRecordBuilder.class), eq(null), eq(A_NEW_ACCOUNT_ID))) + eq(synthLazyCreate), eq(CryptoCreateRecordBuilder.class), eq(null), eq(A_NEW_ACCOUNT_ID))) .thenReturn(cryptoCreateRecordBuilder); + + final var synthLazyCreateFees = new Fees(1L, 2L, 3L); + given(context.dispatchComputeFees(synthLazyCreate, A_NEW_ACCOUNT_ID)).willReturn(synthLazyCreateFees); + + final var synthFinalizatonFees = new Fees(4L, 5L, 6L); + final var synthFinalizationTxn = TransactionBody.newBuilder() + .cryptoUpdateAccount(CryptoUpdateTransactionBody.newBuilder() + .key(Key.newBuilder().ecdsaSecp256k1(Bytes.EMPTY))) + .build(); + given(context.dispatchComputeFees(synthFinalizationTxn, A_NEW_ACCOUNT_ID)) + .willReturn(synthFinalizatonFees); + given(cryptoCreateRecordBuilder.status()).willReturn(OK); final var status = subject.createHollowAccount(CANONICAL_ALIAS); - assertEquals(OK, status); + + verify(cryptoCreateRecordBuilder).memo(LAZY_CREATION_MEMO); + verify(cryptoCreateRecordBuilder) + .transactionFee(synthLazyCreateFees.totalFee() + synthFinalizatonFees.totalFee()); } @Test void createsHollowAccountByDispatchingDoesNotThrowErrors() { - final var synthTxn = TransactionBody.newBuilder() + final var synthLazyCreate = TransactionBody.newBuilder() .cryptoCreateAccount(synthHollowAccountCreation(CANONICAL_ALIAS)) .build(); given(context.payer()).willReturn(A_NEW_ACCOUNT_ID); given(context.dispatchRemovablePrecedingTransaction( - eq(synthTxn), eq(CryptoCreateRecordBuilder.class), eq(null), eq(A_NEW_ACCOUNT_ID))) + eq(synthLazyCreate), eq(CryptoCreateRecordBuilder.class), eq(null), eq(A_NEW_ACCOUNT_ID))) .willReturn(cryptoCreateRecordBuilder); + + final var synthLazyCreateFees = new Fees(1L, 2L, 3L); + given(context.dispatchComputeFees(synthLazyCreate, A_NEW_ACCOUNT_ID)).willReturn(synthLazyCreateFees); + + final var synthFinalizatonFees = new Fees(4L, 5L, 6L); + final var synthFinalizationTxn = TransactionBody.newBuilder() + .cryptoUpdateAccount(CryptoUpdateTransactionBody.newBuilder() + .key(Key.newBuilder().ecdsaSecp256k1(Bytes.EMPTY))) + .build(); + given(context.dispatchComputeFees(synthFinalizationTxn, A_NEW_ACCOUNT_ID)) + .willReturn(synthFinalizatonFees); given(cryptoCreateRecordBuilder.status()).willReturn(MAX_ENTITIES_IN_PRICE_REGIME_HAVE_BEEN_CREATED); final var status = assertDoesNotThrow(() -> subject.createHollowAccount(CANONICAL_ALIAS)); assertThat(status).isEqualTo(MAX_ENTITIES_IN_PRICE_REGIME_HAVE_BEEN_CREATED); + + verify(cryptoCreateRecordBuilder).memo(LAZY_CREATION_MEMO); + verify(cryptoCreateRecordBuilder) + .transactionFee(synthLazyCreateFees.totalFee() + synthFinalizatonFees.totalFee()); } @Test diff --git a/hedera-node/test-clients/record-snapshots/LeakyCryptoTests.json b/hedera-node/test-clients/record-snapshots/LeakyCryptoTests.json new file mode 100644 index 000000000000..35263de2ef4d --- /dev/null +++ b/hedera-node/test-clients/record-snapshots/LeakyCryptoTests.json @@ -0,0 +1 @@ +{"specSnapshots":{"lazyCreateViaEthereumCryptoTransfer":{"placeholderNum":1155,"encodedItems":[{"b64Body":"Cg8KCQi20pGsBhDXCxICGAISAhgDIgIIeDIgw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo6aAboECgIYeRILCPKg7K8GEOjnyjAipgQKKwolaGVkZXJhLmFsbG93YW5jZXMubWF4VHJhbnNhY3Rpb25MaW1pdBICMjAKJgoWdXBncmFkZS5hcnRpZmFjdHMucGF0aBIMZGF0YS91cGdyYWRlCh4KFWNvbnRyYWN0cy5ldm0udmVyc2lvbhIFdjAuMzQKMAoibGVkZ2VyLmF1dG9SZW5ld1BlcmlvZC5tYXhEdXJhdGlvbhIKMTAwMDAwMDAwMAoiChpsZWRnZXIubWF4QXV0b0Fzc29jaWF0aW9ucxIENTAwMAowChdiYWxhbmNlcy5leHBvcnREaXIucGF0aBIVZGF0YS9hY2NvdW50QmFsYW5jZXMvCi0KImxlZGdlci5hdXRvUmVuZXdQZXJpb2QubWluRHVyYXRpb24SBzY5OTk5OTkKJQodY3J5cHRvQ3JlYXRlV2l0aEFsaWFzLmVuYWJsZWQSBHRydWUKKAofZW50aXRpZXMubGltaXRUb2tlbkFzc29jaWF0aW9ucxIFZmFsc2UKJwofY29udHJhY3RzLmFsbG93QXV0b0Fzc29jaWF0aW9ucxIEdHJ1ZQocChRsYXp5Q3JlYXRpb24uZW5hYmxlZBIEdHJ1ZQoYChFjb250cmFjdHMuY2hhaW5JZBIDMjk4ChwKFHRva2Vucy5tYXhQZXJBY2NvdW50EgQxMDAwCigKIWhlZGVyYS5hbGxvd2FuY2VzLm1heEFjY291bnRMaW1pdBIDMTAw","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIw2rDLxOtZFQMCwiwwfSMavBTm7BrtN3bTYYafYsDGuHq8RoreoeduC3knSFjZwkdiGgsI8tKRrAYQ4/jDOiIPCgkIttKRrAYQ1wsSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIA"},{"b64Body":"Cg8KCQi20pGsBhDZCxICGAISAhgDGPuV9hQiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlo0CiISILPgdPvcEq1GRU50fE06m4foLm8UokMXb5hIREEzPgeWEICA9pamtogBSgUIgM7aAw==","b64Record":"CiUIFhIDGIQJKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjApHQjwstYKfgRHyve/9nOWW2wX1ATd5OBFC+4KhGTdfERzc/e1kzdNlzFhyNEUi/IaDAjy0pGsBhCz2uS8AiIPCgkIttKRrAYQ2QsSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIfCg0KAhgCEP//663M7JACCg4KAxiECRCAgOytzOyQAg=="},{"b64Body":"ChEKCQi30pGsBhDbCxICGAIgAVpoCiM6IQOmvN/o1MoYnPcKEgZcSyVS9VermjAh8wgzWjBmS8DLKkoFCIDO2gNqFGF1dG8tY3JlYXRlZCBhY2NvdW50kgEjOiEDprzf6NTKGJz3ChIGXEslUvVXq5owIfMIM1owZkvAyyo=","b64Record":"CgcIFhIDGIUJEjCeFpR5KFMQCylcw5EPxd89pMA7iOuTUGeIfOeho0TvGE/3negPyU+KYkFcj6+Wvt0aCwjz0pGsBhD60IZEIhEKCQi30pGsBhDbCxICGAIgASoUYXV0by1jcmVhdGVkIGFjY291bnRSAKoBFMfR0783EfCTMbjvJQh2aGnnsRoF"},{"b64Body":"Cg8KCQi30pGsBhDbCxICGAISAhgDGKqQBSICCHgyIMODwq7DgsK3w4PCuXRGOMOCwq5Kw4PCi8ODwpDDg8KOcj0KOwoKCgIYAhD/j9/ASgotCiUiIzohA6a83+jUyhic9woSBlxLJVL1V6uaMCHzCDNaMGZLwMsqEICQ38BK","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIw4eB5WkIjZRsj0UfMgFe1YYHT5AgXHzn9GD16alQFM8g85Uee+0lXhEuaqHwVun0HGgsI89KRrAYQ+9CGRCIPCgkIt9KRrAYQ2wsSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIZCgoKAhgCEP+P38BKCgsKAxiFCRCAkN/ASg=="},{"b64Body":"ChAKCQi30pGsBhDhCxIDGIQJEgIYAxjChwUiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjpIDuQEKsAEC+K2CASqAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPooAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgwMNQJRT4IL57m1x4iLe1P/0fXXV3D++GIhFY5GCRPQAAIDAAaC/WZp3t1KVig1Y3fpJ7nrM5aiA88JjqB8TUAZkYtF67aB0wIlaxGUIDdqMdVswIeu1NMW5IjxLvNLYIyX039PYHBiAyrXuAQ==","b64Record":"CiAIHiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwsLvq1biaEzTAeS5Lrdq/g/ieQ4tAO5uDXpbt52626+BcJ3gu16HlPFi29KViWb9SGgwI89KRrAYQq/+DxgIiEAoJCLfSkawGEOELEgMYhAkqIMODwq7DgsK3w4PCuXRGOMOCwq5Kw4PCi8ODwpDDg8KOMILh5wY6JRoQSU5TVUZGSUNJRU5UX0dBUyjAmgxQwJoMWIDKte4BagMYhQlSKgoHCgIYAxCeLgoJCgIYYhD2ls4NCggKAxigBhDwfAoKCgMYhAkQg8LPDYoBIOvvt4J9sCBr2foAKul3j4hFfZbOQhenmWRuIPdyTefq"},{"b64Body":"ChIKCQi40pGsBhDjCxIDGIQJIAFaOAoCMgBKBQiAztoDahRsYXp5LWNyZWF0ZWQgYWNjb3VudJIBFFPggvnubXHiIt7U//R9ddXcP74Y","b64Record":"CgcIFhIDGIcJEjDioi9aG9jWrcsGf0y1obT1xEZ5Uaomg0Fm3e7AywL4WNb9pm9DrTwsgg1ifcoo4gYaCwj00pGsBhDiqdpJIhIKCQi40pGsBhDjCxIDGIQJIAEqFGxhenktY3JlYXRlZCBhY2NvdW50MInK5RJSAA=="},{"b64Body":"ChAKCQi40pGsBhDjCxIDGIQJEgIYAxjChwUiAgh4MiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjpIDuQEKsAEC+K2CASoBoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPooAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgx6EgJRT4IL57m1x4iLe1P/0fXXV3D++GIhFY5GCRPQAAIDAAaDCe+psti/ghMQLh5j3tzMFYW7ViXDEHynVTbKmNYfp9KB6BCgbe6r+R0soWNePmyDUisENdaLBImhdxATj/j7TNBiAyrXuAQ==","b64Record":"CiUIFiIDGIcJKhwKDAgBEAwaBgiArpmkDxIMCAEQDxoGCICumaQPEjD28eoGPGKreweEnp1faH5GW1kkWxivocVn47bYo9zxJEOxTbPJEAm807dh4bO84ngaCwj00pGsBhDjqdpJIhAKCQi40pGsBhDjCxIDGIQJKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjjDC05o2OpsCCgMYhwkigAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKIDUYVCAiXpYgMq17gFqAxiFCVJECgcKAhgDEJ4uCgkKAhhiEPb7s2wKCAoDGKAGEPB8CgoKAxiECRCDp7VsCgsKAxiFCRD/k+vcAwoLCgMYhwkQgJTr3AOKASCHGy7uUcFh6xS1ZqwjLOF8p9inQSRqES4CwNcEu8pi5A=="},{"b64Body":"Cg8KCQi50pGsBhCGDBICGAISAhgDIgIIeDIgw4PCrsOCwrfDg8K5dEY4w4LCrkrDg8KLw4PCkMODwo6aAboECgIYeRILCPWg7K8GENjtnzUipgQKKwolaGVkZXJhLmFsbG93YW5jZXMubWF4VHJhbnNhY3Rpb25MaW1pdBICMjAKJgoWdXBncmFkZS5hcnRpZmFjdHMucGF0aBIMZGF0YS91cGdyYWRlCh4KFWNvbnRyYWN0cy5ldm0udmVyc2lvbhIFdjAuMzgKMAoibGVkZ2VyLmF1dG9SZW5ld1BlcmlvZC5tYXhEdXJhdGlvbhIKMTAwMDAwMDAwMAoiChpsZWRnZXIubWF4QXV0b0Fzc29jaWF0aW9ucxIENTAwMAowChdiYWxhbmNlcy5leHBvcnREaXIucGF0aBIVZGF0YS9hY2NvdW50QmFsYW5jZXMvCi0KImxlZGdlci5hdXRvUmVuZXdQZXJpb2QubWluRHVyYXRpb24SBzY5OTk5OTkKJQodY3J5cHRvQ3JlYXRlV2l0aEFsaWFzLmVuYWJsZWQSBHRydWUKKAofZW50aXRpZXMubGltaXRUb2tlbkFzc29jaWF0aW9ucxIFZmFsc2UKJwofY29udHJhY3RzLmFsbG93QXV0b0Fzc29jaWF0aW9ucxIEdHJ1ZQocChRsYXp5Q3JlYXRpb24uZW5hYmxlZBIEdHJ1ZQoYChFjb250cmFjdHMuY2hhaW5JZBIDMjk4ChwKFHRva2Vucy5tYXhQZXJBY2NvdW50EgQxMDAwCigKIWhlZGVyYS5hbGxvd2FuY2VzLm1heEFjY291bnRMaW1pdBIDMTAw","b64Record":"CiAIFiocCgwIARAMGgYIgK6ZpA8SDAgBEA8aBgiArpmkDxIwtpaaLhWmJd20BeOJuuD1GOthZfZdheczN8KiHZeVOBpMskVKHeiK2gqhjeuLFlkSGgsI9dKRrAYQu6zUNSIPCgkIudKRrAYQhgwSAhgCKiDDg8Kuw4LCt8ODwrl0RjjDgsKuSsODwovDg8KQw4PCjlIA"}]}}} \ No newline at end of file diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/domain/ParsedItem.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/domain/ParsedItem.java index 6c95d1f5a940..df3602bf6cb2 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/domain/ParsedItem.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/domain/ParsedItem.java @@ -16,8 +16,11 @@ package com.hedera.services.bdd.spec.utilops.domain; +import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.SUCCESS; + import com.google.protobuf.InvalidProtocolBufferException; import com.hedera.services.stream.proto.RecordStreamItem; +import com.hederahashgraph.api.proto.java.FileID; import com.hederahashgraph.api.proto.java.SignedTransaction; import com.hederahashgraph.api.proto.java.TransactionBody; import com.hederahashgraph.api.proto.java.TransactionRecord; @@ -30,6 +33,9 @@ * @param itemRecord the transaction record */ public record ParsedItem(TransactionBody itemBody, TransactionRecord itemRecord) { + private static final FileID PROPERTIES_FILE_ID = + FileID.newBuilder().setFileNum(121).build(); + public static ParsedItem parse(final RecordStreamItem item) throws InvalidProtocolBufferException { final var txn = item.getTransaction(); final TransactionBody body; @@ -42,4 +48,10 @@ public static ParsedItem parse(final RecordStreamItem item) throws InvalidProtoc } return new ParsedItem(body, item.getRecord()); } + + public boolean isPropertyOverride() { + return itemRecord.getReceipt().getStatus() == SUCCESS + && itemBody.hasFileUpdate() + && PROPERTIES_FILE_ID.equals(itemBody.getFileUpdate().getFileID()); + } } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/domain/RecordSnapshot.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/domain/RecordSnapshot.java index 1b280d1aa40f..9579aff77b64 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/domain/RecordSnapshot.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/domain/RecordSnapshot.java @@ -56,6 +56,9 @@ public void setEncodedItems(@NonNull final List encodedItems) { } public List parsedItems() { - return encodedItems.stream().map(EncodedItem::asParsedItem).toList(); + return encodedItems.stream() + .map(EncodedItem::asParsedItem) + .filter(pi -> !pi.isPropertyOverride()) + .toList(); } } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/records/SnapshotModeOp.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/records/SnapshotModeOp.java index 4ac7508d66e2..8132eb36923d 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/records/SnapshotModeOp.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/records/SnapshotModeOp.java @@ -126,6 +126,9 @@ public class SnapshotModeOp extends UtilOp implements SnapshotOp { // Keys are also regenerated every test execution "ed25519", "ECDSA_secp256k1", + // Ethereum data depends on ECDSA keys + "ethereum_data", + "ethereum_hash", // Plus some other fields that we might prefer to make deterministic "symbol", // Bloom field in ContractCall result @@ -286,6 +289,10 @@ public void finishLifecycle(@NonNull final HapiSpec spec) { boolean placeholderFound = false; for (final var item : allItems) { final var parsedItem = ParsedItem.parse(item); + if (parsedItem.isPropertyOverride()) { + // Property overrides vary with the previous contents of 0.0.121 + continue; + } final var body = parsedItem.itemBody(); if (body.hasNodeStakeUpdate()) { // We cannot ever expect to match node stake update export sequencing @@ -426,11 +433,10 @@ private void fuzzyMatch( "Mismatched field names ('" + expectedName + "' vs '" + actualName + "' between expected " + expectedMessage + " and " + actualMessage + " - " + mismatchContext.get()); } + if (shouldSkip(expectedName, expectedField.getValue().getClass())) { - // System.out.println("YES"); continue; } - // System.out.println("NO"); matchValues( expectedName, expectedField.getValue(), @@ -580,7 +586,8 @@ private void matchSingleValues( "Amount '" + expected + "' and '" + actual + "' varied by more than " + maxVariation + " tinybar - " + mismatchContext.get()); - } else if ("accountNum".equals(fieldName) && matchModes.contains(ALLOW_SKIPPED_ENTITY_IDS)) { + } else if (("accountNum".equals(fieldName) || "contractNum".equals(fieldName)) + && matchModes.contains(ALLOW_SKIPPED_ENTITY_IDS)) { Assertions.assertTrue( (long) expected - (long) actual >= 0, "AccountNum '" + expected + "' was not greater than '" + actual + mismatchContext.get()); diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/leaky/LeakyCryptoTestsSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/leaky/LeakyCryptoTestsSuite.java index 838ebbed246c..8288c0e96c98 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/leaky/LeakyCryptoTestsSuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/leaky/LeakyCryptoTestsSuite.java @@ -74,10 +74,13 @@ import static com.hedera.services.bdd.spec.utilops.UtilVerbs.overridingTwo; import static com.hedera.services.bdd.spec.utilops.UtilVerbs.reduceFeeFor; import static com.hedera.services.bdd.spec.utilops.UtilVerbs.sleepFor; +import static com.hedera.services.bdd.spec.utilops.UtilVerbs.snapshotMode; import static com.hedera.services.bdd.spec.utilops.UtilVerbs.sourcing; import static com.hedera.services.bdd.spec.utilops.UtilVerbs.uploadDefaultFeeSchedules; import static com.hedera.services.bdd.spec.utilops.UtilVerbs.validateChargedUsd; import static com.hedera.services.bdd.spec.utilops.UtilVerbs.withOpContext; +import static com.hedera.services.bdd.spec.utilops.records.SnapshotMatchMode.ALLOW_SKIPPED_ENTITY_IDS; +import static com.hedera.services.bdd.spec.utilops.records.SnapshotMode.FUZZY_MATCH_AGAINST_HAPI_TEST_STREAMS; import static com.hedera.services.bdd.suites.contract.hapi.ContractCreateSuite.EMPTY_CONSTRUCTOR_CONTRACT; import static com.hedera.services.bdd.suites.crypto.AutoAccountCreationSuite.CRYPTO_TRANSFER_RECEIVER; import static com.hedera.services.bdd.suites.crypto.AutoAccountCreationSuite.FALSE; @@ -1001,6 +1004,7 @@ final HapiSpec lazyCreateViaEthereumCryptoTransfer() { return propertyPreservingHapiSpec("lazyCreateViaEthereumCryptoTransfer") .preserving(CHAIN_ID_PROP, LAZY_CREATE_PROPERTY_NAME, CONTRACTS_EVM_VERSION_PROP) .given( + snapshotMode(FUZZY_MATCH_AGAINST_HAPI_TEST_STREAMS, ALLOW_SKIPPED_ENTITY_IDS), overridingThree( CHAIN_ID_PROP, "298",