diff --git a/examples/src/main/java/com/hedera/hashgraph/sdk/examples/HighVolumeAccountCreateExample.java b/examples/src/main/java/com/hedera/hashgraph/sdk/examples/HighVolumeAccountCreateExample.java new file mode 100644 index 0000000000..7a4fb9d76a --- /dev/null +++ b/examples/src/main/java/com/hedera/hashgraph/sdk/examples/HighVolumeAccountCreateExample.java @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: Apache-2.0 +package com.hedera.hashgraph.sdk.examples; + +import com.hedera.hashgraph.sdk.*; +import com.hedera.hashgraph.sdk.logger.LogLevel; +import com.hedera.hashgraph.sdk.logger.Logger; +import io.github.cdimascio.dotenv.Dotenv; +import java.util.Objects; + +/** + * Create a Hedera account using high-volume throttles. + */ +class HighVolumeAccountCreateExample { + + /* + * See .env.sample in the examples folder root for how to specify values below + * or set environment variables with the same names. + */ + + /** + * Operator's account ID. + * Used to sign and pay for operations on Hedera. + */ + private static final AccountId OPERATOR_ID = + AccountId.fromString(Objects.requireNonNull(Dotenv.load().get("OPERATOR_ID"))); + + /** + * Operator's private key. + */ + private static final PrivateKey OPERATOR_KEY = + PrivateKey.fromString(Objects.requireNonNull(Dotenv.load().get("OPERATOR_KEY"))); + + /** + * HEDERA_NETWORK defaults to testnet if not specified in dotenv file. + * Network can be: localhost, testnet, previewnet or mainnet. + */ + private static final String HEDERA_NETWORK = Dotenv.load().get("HEDERA_NETWORK", "testnet"); + + /** + * SDK_LOG_LEVEL defaults to SILENT if not specified in dotenv file. + * Log levels can be: TRACE, DEBUG, INFO, WARN, ERROR, SILENT. + *

+ * Important pre-requisite: set simple logger log level to same level as the SDK_LOG_LEVEL, + * for example via VM options: -Dorg.slf4j.simpleLogger.log.org.hiero=trace + */ + private static final String SDK_LOG_LEVEL = Dotenv.load().get("SDK_LOG_LEVEL", "SILENT"); + + public static void main(String[] args) throws Exception { + System.out.println("High-Volume Account Create Example Start!"); + + /* + * Step 0: + * Create and configure the SDK Client. + */ + Client client = ClientHelper.forName(HEDERA_NETWORK); + // All generated transactions will be paid by this account and signed by this key. + client.setOperator(OPERATOR_ID, OPERATOR_KEY); + // Attach logger to the SDK Client. + client.setLogger(new Logger(LogLevel.valueOf(SDK_LOG_LEVEL))); + + /* + * Step 1: + * Generate ED25519 private and public key pair for the account. + */ + PrivateKey privateKey = PrivateKey.generateED25519(); + PublicKey publicKey = privateKey.getPublicKey(); + System.out.println("Future account private key: " + privateKey); + System.out.println("Future account public key: " + publicKey); + + /* + * Step 2: + * Create a new account using high-volume throttles and set a fee limit. + */ + System.out.println("Creating new account with high-volume throttles..."); + TransactionResponse accountCreateTxResponse = new AccountCreateTransaction() + .setKeyWithoutAlias(publicKey) + .setInitialBalance(Hbar.from(1)) + .setHighVolume(true) + .setMaxTransactionFee(Hbar.from(5)) + .execute(client); + + // This will wait for the receipt to become available. + TransactionReceipt accountCreateTxReceipt = accountCreateTxResponse.getReceipt(client); + AccountId newAccountId = accountCreateTxReceipt.accountId; + Objects.requireNonNull(newAccountId); + System.out.println("Created account with ID: " + newAccountId); + + /* + * Clean up: + * Delete created account. + */ + new AccountDeleteTransaction() + .setTransferAccountId(OPERATOR_ID) + .setAccountId(newAccountId) + .freezeWith(client) + .sign(privateKey) + .execute(client) + .getReceipt(client); + + client.close(); + + System.out.println("High-Volume Account Create Example Complete!"); + } +} diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/FeeDataType.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/FeeDataType.java index c3b23ac68b..ccf6221f3e 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/FeeDataType.java +++ b/sdk/src/main/java/com/hedera/hashgraph/sdk/FeeDataType.java @@ -52,7 +52,12 @@ public enum FeeDataType { * The resource cost for the transaction type includes a ConsensusSubmitMessage * for a topic with custom fees. */ - SUBMIT_MESSAGE_WITH_CUSTOM_FEES(SubType.SUBMIT_MESSAGE_WITH_CUSTOM_FEES); + SUBMIT_MESSAGE_WITH_CUSTOM_FEES(SubType.SUBMIT_MESSAGE_WITH_CUSTOM_FEES), + + /** + * The resource cost for the transaction type that includes a CryptoTransfer with hook invocations + */ + CRYPTO_TRANSFER_WITH_HOOKS(SubType.CRYPTO_TRANSFER_WITH_HOOKS); final SubType code; @@ -70,6 +75,7 @@ static FeeDataType valueOf(SubType code) { case SCHEDULE_CREATE_CONTRACT_CALL -> SCHEDULE_CREATE_CONTRACT_CALL; case TOPIC_CREATE_WITH_CUSTOM_FEES -> TOPIC_CREATE_WITH_CUSTOM_FEES; case SUBMIT_MESSAGE_WITH_CUSTOM_FEES -> SUBMIT_MESSAGE_WITH_CUSTOM_FEES; + case CRYPTO_TRANSFER_WITH_HOOKS -> CRYPTO_TRANSFER_WITH_HOOKS; default -> throw new IllegalStateException("(BUG) unhandled SubType (FeeDataType)"); }; } @@ -85,6 +91,7 @@ public String toString() { case SCHEDULE_CREATE_CONTRACT_CALL -> "SCHEDULE_CREATE_CONTRACT_CALL"; case TOPIC_CREATE_WITH_CUSTOM_FEES -> "TOPIC_CREATE_WITH_CUSTOM_FEES"; case SUBMIT_MESSAGE_WITH_CUSTOM_FEES -> "SUBMIT_MESSAGE_WITH_CUSTOM_FEES"; + case CRYPTO_TRANSFER_WITH_HOOKS -> "CRYPTO_TRANSFER_WITH_HOOKS"; }; } } diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/RequestType.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/RequestType.java index cbf939ad8a..7701d4e546 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/RequestType.java +++ b/sdk/src/main/java/com/hedera/hashgraph/sdk/RequestType.java @@ -507,22 +507,39 @@ public enum RequestType { ATOMIC_BATCH(HederaFunctionality.AtomicBatch), /** - * Update one or more storage slots in an lambda EVM hook. - * - * @deprecated Use {@link #HOOK_STORE} instead. + * (DEPRECATED) Remove once no production throttle assets reference it. */ - @Deprecated LAMBDA_S_STORE(HederaFunctionality.LambdaSStore), + /** + * (Internal-only) Dispatch a hook action. + */ + HOOK_DISPATCH(HederaFunctionality.HookDispatch), + /** * Update one or more storage slots in an EVM hook. */ HOOK_STORE(HederaFunctionality.HookStore), /** - * (Internal-only) Dispatch a hook action. + * (Internal-only) Publish a new ledger id and chain-of-trust key. + */ + LEDGER_ID_PUBLICATION(HederaFunctionality.LedgerIdPublication), + + /** + * Create a registered node + */ + REGISTERED_NODE_CREATE(HederaFunctionality.RegisteredNodeCreate), + + /** + * Update a registered node + */ + REGISTERED_NODE_UPDATE(HederaFunctionality.RegisteredNodeUpdate), + + /** + * Delete a registered node */ - HOOK_DISPATCH(HederaFunctionality.HookDispatch); + REGISTERED_NODE_DELETE(HederaFunctionality.RegisteredNodeDelete); final HederaFunctionality code; @@ -624,8 +641,12 @@ static RequestType valueOf(HederaFunctionality code) { case CrsPublication -> CRS_PUBLICATION; case AtomicBatch -> ATOMIC_BATCH; case LambdaSStore -> LAMBDA_S_STORE; - case HookStore -> HOOK_STORE; case HookDispatch -> HOOK_DISPATCH; + case HookStore -> HOOK_STORE; + case LedgerIdPublication -> LEDGER_ID_PUBLICATION; + case RegisteredNodeCreate -> REGISTERED_NODE_CREATE; + case RegisteredNodeUpdate -> REGISTERED_NODE_UPDATE; + case RegisteredNodeDelete -> REGISTERED_NODE_DELETE; default -> throw new IllegalStateException("(BUG) unhandled HederaFunctionality"); }; } @@ -725,8 +746,12 @@ public String toString() { case CRS_PUBLICATION -> "CRS_PUBLICATION"; case ATOMIC_BATCH -> "ATOMIC_BATCH"; case LAMBDA_S_STORE -> "LAMBDA_S_STORE"; - case HOOK_STORE -> "HOOK_STORE"; case HOOK_DISPATCH -> "HOOK_DISPATCH"; + case HOOK_STORE -> "HOOK_STORE"; + case LEDGER_ID_PUBLICATION -> "LEDGER_ID_PUBLICATION"; + case REGISTERED_NODE_CREATE -> "REGISTERED_NODE_CREATE"; + case REGISTERED_NODE_UPDATE -> "REGISTERED_NODE_UPDATE"; + case REGISTERED_NODE_DELETE -> "REGISTERED_NODE_DELETE"; }; } } diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/Status.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/Status.java index 8ecfe7ff3e..a589279228 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/Status.java +++ b/sdk/src/main/java/com/hedera/hashgraph/sdk/Status.java @@ -1921,6 +1921,11 @@ public enum Status { */ INVALID_SERIALIZED_TX_MESSAGE_HASH_ALGORITHM(ResponseCodeEnum.INVALID_SERIALIZED_TX_MESSAGE_HASH_ALGORITHM), + /** + * A HookStore referenced a valid entity number but with the wrong entity type. + */ + WRONG_HOOK_ENTITY_TYPE(ResponseCodeEnum.WRONG_HOOK_ENTITY_TYPE), + /** * An EVM hook execution was throttled due to high network gas utilization. */ @@ -1992,7 +1997,7 @@ public enum Status { /** * The HookStore tried to update too many storage slots in a single transaction. */ - TOO_MANY_LAMBDA_STORAGE_UPDATES(ResponseCodeEnum.TOO_MANY_LAMBDA_STORAGE_UPDATES), + TOO_MANY_EVM_HOOK_STORAGE_UPDATES(ResponseCodeEnum.TOO_MANY_EVM_HOOK_STORAGE_UPDATES), /** * An EVM hook mapping slot, storage key, or storage value failed to use the @@ -2046,11 +2051,43 @@ public enum Status { */ HOOKS_ARE_NOT_SUPPORTED_IN_AIRDROPS(ResponseCodeEnum.HOOKS_ARE_NOT_SUPPORTED_IN_AIRDROPS), - WRONG_HOOK_ENTITY_TYPE(ResponseCodeEnum.WRONG_HOOK_ENTITY_TYPE), + /** + * This operation cannot be completed because the target + * account is a "Node Account".
+ * This account is currently in use as the "Node Account" for a + * consensus node, and therefore the requested change is + * not permitted. The transaction may be resubmitted once the + * account is no longer in use as a "Node Account" for any + * consensus node. + */ ACCOUNT_IS_LINKED_TO_A_NODE(ResponseCodeEnum.ACCOUNT_IS_LINKED_TO_A_NODE), + + /** + * Hooks are not supported to be used in Batch transactions and Scheduled transactions. + * They are only supported in a top level CryptoTransfer transaction. + */ HOOKS_EXECUTIONS_REQUIRE_TOP_LEVEL_CRYPTO_TRANSFER( ResponseCodeEnum.HOOKS_EXECUTIONS_REQUIRE_TOP_LEVEL_CRYPTO_TRANSFER), - NODE_ACCOUNT_HAS_ZERO_BALANCE(ResponseCodeEnum.NODE_ACCOUNT_HAS_ZERO_BALANCE); + + /** + * This operation cannot be completed because the target + * account has a zero balance.
+ * Node accounts require a positive balance. The transaction may be + * resubmitted once the account has been funded. + */ + NODE_ACCOUNT_HAS_ZERO_BALANCE(ResponseCodeEnum.NODE_ACCOUNT_HAS_ZERO_BALANCE), + + /** + * This operation cannot be completed because the target + * account is a "Fee Collection Account".
+ * Any attempt to transfer to a fee collection account is not permitted. + */ + TRANSFER_TO_FEE_COLLECTION_ACCOUNT_NOT_ALLOWED(ResponseCodeEnum.TRANSFER_TO_FEE_COLLECTION_ACCOUNT_NOT_ALLOWED), + + /** + * The number of hook invocations exceeds the maximum allowed per transaction. + */ + TOO_MANY_HOOK_INVOCATIONS(ResponseCodeEnum.TOO_MANY_HOOK_INVOCATIONS); final ResponseCodeEnum code; @@ -2443,7 +2480,7 @@ static Status valueOf(ResponseCodeEnum code) { case HOOKS_NOT_ENABLED -> HOOKS_NOT_ENABLED; case HOOK_IS_NOT_AN_EVM_HOOK -> HOOK_IS_NOT_AN_EVM_HOOK; case HOOK_DELETED -> HOOK_DELETED; - case TOO_MANY_LAMBDA_STORAGE_UPDATES -> TOO_MANY_LAMBDA_STORAGE_UPDATES; + case TOO_MANY_EVM_HOOK_STORAGE_UPDATES -> TOO_MANY_EVM_HOOK_STORAGE_UPDATES; case HOOK_CREATION_BYTES_MUST_USE_MINIMAL_REPRESENTATION -> HOOK_CREATION_BYTES_MUST_USE_MINIMAL_REPRESENTATION; case HOOK_CREATION_BYTES_TOO_LONG -> HOOK_CREATION_BYTES_TOO_LONG; @@ -2459,6 +2496,8 @@ static Status valueOf(ResponseCodeEnum code) { case HOOKS_EXECUTIONS_REQUIRE_TOP_LEVEL_CRYPTO_TRANSFER -> HOOKS_EXECUTIONS_REQUIRE_TOP_LEVEL_CRYPTO_TRANSFER; case NODE_ACCOUNT_HAS_ZERO_BALANCE -> NODE_ACCOUNT_HAS_ZERO_BALANCE; + case TRANSFER_TO_FEE_COLLECTION_ACCOUNT_NOT_ALLOWED -> TRANSFER_TO_FEE_COLLECTION_ACCOUNT_NOT_ALLOWED; + case TOO_MANY_HOOK_INVOCATIONS -> TOO_MANY_HOOK_INVOCATIONS; case UNRECOGNIZED -> // NOTE: Protobuf deserialization will not give us the code on the wire throw new IllegalArgumentException( diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/Transaction.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/Transaction.java index dc66d5e6ae..570842f1ae 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/Transaction.java +++ b/sdk/src/main/java/com/hedera/hashgraph/sdk/Transaction.java @@ -132,6 +132,8 @@ public abstract class Transaction> private String memo = ""; + private boolean highVolume = false; + List customFeeLimits = new ArrayList<>(); private Key batchKey = null; @@ -156,6 +158,7 @@ public abstract class Transaction> setTransactionValidDuration(DEFAULT_TRANSACTION_VALID_DURATION); setMaxTransactionFee(Hbar.fromTinybars(txBody.getTransactionFee())); setTransactionMemo(txBody.getMemo()); + setHighVolume(txBody.getHighVolume()); sourceTransactionBody = txBody; } @@ -245,6 +248,7 @@ public abstract class Transaction> DurationConverter.fromProtobuf(sourceTransactionBody.getTransactionValidDuration())); setMaxTransactionFee(Hbar.fromTinybars(sourceTransactionBody.getTransactionFee())); setTransactionMemo(sourceTransactionBody.getMemo()); + setHighVolume(sourceTransactionBody.getHighVolume()); this.customFeeLimits = sourceTransactionBody.getMaxCustomFeesList().stream() .map(CustomFeeLimit::fromProtobuf) @@ -859,6 +863,30 @@ public final T setTransactionMemo(String memo) { return (T) this; } + /** + * Extract the high-volume flag. + * + * @return true if high-volume throttles are enabled, false otherwise + */ + public final boolean getHighVolume() { + return highVolume; + } + + /** + * If set to true, this transaction uses high-volume throttles and pricing + * for entity creation. It only affects supported transaction types; otherwise, + * it is ignored. + * + * @param highVolume true to enable high-volume throttles, false otherwise + * @return {@code this} + */ + public final T setHighVolume(boolean highVolume) { + requireNotFrozen(); + this.highVolume = highVolume; + // noinspection unchecked + return (T) this; + } + /** * batchify method is used to mark a transaction as part of a batch transaction or make it so-called inner transaction. * The Transaction will be frozen and signed by the operator of the client. @@ -1236,7 +1264,8 @@ protected TransactionBody.Builder spawnBodyBuilder(@Nullable Client client) { .setTransactionValidDuration(DurationConverter.toProtobuf(transactionValidDuration).toBuilder()) .addAllMaxCustomFees( customFeeLimits.stream().map(CustomFeeLimit::toProtobuf).collect(Collectors.toList())) - .setMemo(memo); + .setMemo(memo) + .setHighVolume(highVolume); if (batchKey != null) { builder.setBatchKey(batchKey.toProtobufKey()); } diff --git a/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionRecord.java b/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionRecord.java index b4b6775e89..543ba9021b 100644 --- a/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionRecord.java +++ b/sdk/src/main/java/com/hedera/hashgraph/sdk/TransactionRecord.java @@ -192,6 +192,17 @@ public final class TransactionRecord { */ public final List pendingAirdropRecords; + /** + * A pricing multiplier for high-volume entity creation. + *

+ * If the transaction that produced this record was flagged as + * high-volume, this field SHALL contain the pricing multiplier + * applied to the transaction fee.
+ * If the transaction was not flagged as high-volume, this field + * SHALL be zero. + */ + public final long highVolumePricingMultiplier; + TransactionRecord( TransactionReceipt transactionReceipt, ByteString transactionHash, @@ -216,7 +227,8 @@ public final class TransactionRecord { @Nullable ByteString prngBytes, @Nullable Integer prngNumber, ByteString evmAddress, - List pendingAirdropRecords) { + List pendingAirdropRecords, + long highVolumePricingMultiplier) { this.receipt = transactionReceipt; this.transactionHash = transactionHash; this.consensusTimestamp = consensusTimestamp; @@ -244,6 +256,7 @@ public final class TransactionRecord { this.prngBytes = prngBytes; this.prngNumber = prngNumber; this.evmAddress = evmAddress; + this.highVolumePricingMultiplier = highVolumePricingMultiplier; } /** @@ -345,7 +358,8 @@ static TransactionRecord fromProtobuf( transactionRecord.hasPrngBytes() ? transactionRecord.getPrngBytes() : null, transactionRecord.hasPrngNumber() ? transactionRecord.getPrngNumber() : null, transactionRecord.getEvmAddress(), - pendingAirdropRecords); + pendingAirdropRecords, + transactionRecord.getHighVolumePricingMultiplier()); } /** @@ -475,6 +489,8 @@ com.hedera.hashgraph.sdk.proto.TransactionRecord toProtobuf() { } } + transactionRecord.setHighVolumePricingMultiplier(highVolumePricingMultiplier); + return transactionRecord.build(); } @@ -504,6 +520,7 @@ public String toString() { .add("prngNumber", prngNumber) .add("evmAddress", Hex.toHexString(evmAddress.toByteArray())) .add("pendingAirdropRecords", pendingAirdropRecords.toString()) + .add("highVolumePricingMultiplier", highVolumePricingMultiplier) .toString(); } diff --git a/sdk/src/main/proto/address_book_service.proto b/sdk/src/main/proto/address_book_service.proto index 4372219309..5a88696246 100644 --- a/sdk/src/main/proto/address_book_service.proto +++ b/sdk/src/main/proto/address_book_service.proto @@ -134,4 +134,35 @@ service AddressBookService { * This transaction is authorized by the node operator */ rpc updateNode (proto.Transaction) returns (proto.TransactionResponse); + + /** + * A transaction to create a new registered node in the network + * address book. + *

+ * This transaction, once complete, SHALL add a new registered node to the + * network state.
+ * The new registered node SHALL be visible and discoverable upon + * completion of this transaction. + */ + rpc createRegisteredNode (proto.Transaction) returns (proto.TransactionResponse); + + /** + * A transaction to remove a registered node from the network address + * book. + *

+ * This transaction, once complete, SHALL remove the identified registered + * node from the network state.
+ * This transaction MUST be signed by the existing entry `admin_key` or + * authorized by the Hiero network governance structure. + */ + rpc deleteRegisteredNode (proto.Transaction) returns (proto.TransactionResponse); + + /** + * A transaction to update an existing registered node in the network + * address book. + *

+ * This transaction, once complete, SHALL modify the identified registered + * node state as requested. + */ + rpc updateRegisteredNode (proto.Transaction) returns (proto.TransactionResponse); } diff --git a/sdk/src/main/proto/basic_types.proto b/sdk/src/main/proto/basic_types.proto index 47a4dbda04..c2b8b7be32 100644 --- a/sdk/src/main/proto/basic_types.proto +++ b/sdk/src/main/proto/basic_types.proto @@ -867,6 +867,11 @@ enum SubType { * for a topic with custom fees. */ SUBMIT_MESSAGE_WITH_CUSTOM_FEES = 7; + + /** + * The resource cost for the transaction type that includes a CryptoTransfer with hook invocations + */ + CRYPTO_TRANSFER_WITH_HOOKS = 8; } /** @@ -1835,7 +1840,6 @@ enum HederaFunctionality { AtomicBatch = 108; /** - * Update one or more storage slots in an lambda EVM hook. * (DEPRECATED) Remove once no production throttle assets reference it. */ LambdaSStore = 109; @@ -1849,6 +1853,26 @@ enum HederaFunctionality { * Update one or more storage slots in an EVM hook. */ HookStore = 111; + + /** + * (Internal-only) Publish a new ledger id and chain-of-trust key. + */ + LedgerIdPublication = 112; + + /** + * Create a registered node + */ + RegisteredNodeCreate = 113; + + /** + * Update a registered node + */ + RegisteredNodeUpdate = 114; + + /** + * Delete a registered node + */ + RegisteredNodeDelete = 115; } /** diff --git a/sdk/src/main/proto/block_stream_info.proto b/sdk/src/main/proto/block_stream_info.proto index ffe33bf4aa..99120a5a6e 100644 --- a/sdk/src/main/proto/block_stream_info.proto +++ b/sdk/src/main/proto/block_stream_info.proto @@ -26,9 +26,9 @@ option java_multiple_files = true; * A message stored in state to maintain block stream parameters.
* Nodes use this information for three purposes. * 1. To maintain hash chain continuity at restart and reconnect boundaries. - * 1. To store historical hashes for implementation of the EVM `BLOCKHASH` + * 2. To store historical hashes for implementation of the EVM `BLOCKHASH` * and `PREVRANDAO` opcodes. - * 1. To track the amount of consensus time that has passed between blocks. + * 3. To track the amount of consensus time that has passed between blocks. * * This value MUST be updated for every block.
* This value MUST be transmitted in the "state changes" section of @@ -48,7 +48,8 @@ message BlockStreamInfo { /** * A consensus time for the current block.
* This is the consensus time of the first round in the current block, - * and is used to determine if this block was the first across an + * which equates to the first contained transaction's consensus time. + * It is used to determine if this block was the first across an * important boundary in consensus time, such as UTC midnight. * This may also be used to purge entities expiring between the last * block time and this time. @@ -83,7 +84,7 @@ message BlockStreamInfo { /** * A SHA2-384 hash value.
- * This is the hash of the "input" subtree for this block. + * This is the final hash of the "input" subtree for this block. */ bytes input_tree_root_hash = 5; @@ -99,7 +100,7 @@ message BlockStreamInfo { * This SHALL count the number of output block items that _precede_ * the state change that updates this singleton. */ - uint32 num_preceding_state_changes_items = 7; + uint64 num_preceding_state_changes_items = 7; /** * A concatenation of SHA2-384 hash values.
@@ -150,20 +151,33 @@ message BlockStreamInfo { proto.Timestamp last_handle_time = 13; /** - * A SHA2-384 hash value.
- * This is the hash of the "consensus headers" subtree for this block. - */ - bytes consensus_header_tree_root_hash = 14; + * A SHA2-384 hash value.
+ * This is the final hash of the "consensus headers" subtree for this block. + */ + bytes consensus_header_root_hash = 14; + + /** + * A SHA2-384 hash value.
+ * This is the final hash of the "output" subtree for this block. + */ + bytes output_item_root_hash = 15; /** - * A SHA2-384 hash value.
- * This is the hash of the "trace data" subtree for this block. - */ - bytes trace_data_tree_root_hash = 15; + * A SHA2-384 hash value.
+ * This is the final hash of the "trace data" subtree for this block. + */ + bytes trace_data_root_hash = 16; + + /** + * The intermediate hashes needed for subroot 2 in the block merkle + * tree structure. These hashes SHALL include the minimum required + * block root hashes needed to construct subroot 2's final state at + * the end of the previous block. + */ + repeated bytes intermediate_previous_block_root_hashes = 17; /** - * A SHA2-384 hash value.
- * This is the hash of the "output" subtree for this block. - */ - bytes output_tree_root_hash = 16; + * The number of leaves in the intermediate block roots subtree. + */ + uint64 intermediate_block_roots_leaf_count = 18; } diff --git a/sdk/src/main/proto/contract_types.proto b/sdk/src/main/proto/contract_types.proto index 54502109a6..77e620ed4b 100644 --- a/sdk/src/main/proto/contract_types.proto +++ b/sdk/src/main/proto/contract_types.proto @@ -80,6 +80,25 @@ message EvmTransactionResult { * internal call producing this result. */ InternalCallContext internal_call_context = 6; + + /** + * If set, the id of the executed hook. + */ + proto.HookId executed_hook_id = 7; + + /** + * A list of contract account nonce values.
+ * This list SHALL contain a nonce value for each contract account modified + * as a result of this contract call. These nonce values SHALL be the value + * after the contract call is completed. + */ + repeated ContractNonceInfo contract_nonces = 8; + + /** + * In an EthereumTransaction, the nonce of the signer account at the end of + * the transaction.
+ */ + google.protobuf.Int64Value signer_nonce = 9; } /** diff --git a/sdk/src/main/proto/evm_hook_state.proto b/sdk/src/main/proto/evm_hook_state.proto index e4dff0c84f..36c911fd2c 100644 --- a/sdk/src/main/proto/evm_hook_state.proto +++ b/sdk/src/main/proto/evm_hook_state.proto @@ -31,7 +31,7 @@ message EvmHookState { proto.HookId hook_id = 1; /** - * The type of the hook. + * The type of the EVM hook. */ HookType type = 2; @@ -47,8 +47,7 @@ message EvmHookState { /** * If set, a key that that can be used to remove or replace the hook; or (if - * applicable, as with an EVM hook) perform transactions that customize - * the hook. + * applicable, as with an EVM hook) perform transactions that customize the hook. */ proto.Key admin_key = 5; @@ -91,6 +90,7 @@ enum HookType { /** * The key of an EVM hook's storage slot. + * * For each EVM hook, its storage is a mapping of 256-bit keys (or "words") * to 256-bit values. */ diff --git a/sdk/src/main/proto/hints_key_publication.proto b/sdk/src/main/proto/hints_key_publication.proto index b61899ae74..e44db66bc1 100644 --- a/sdk/src/main/proto/hints_key_publication.proto +++ b/sdk/src/main/proto/hints_key_publication.proto @@ -13,8 +13,6 @@ syntax = "proto3"; package com.hedera.hapi.services.auxiliary.hints; // SPDX-License-Identifier: Apache-2.0 -import "hints_types.proto"; - option java_package = "com.hedera.hapi.services.auxiliary.hints.legacy"; // <<>> This comment is special code for setting PBJ Compiler java package diff --git a/sdk/src/main/proto/history_proof_key_publication.proto b/sdk/src/main/proto/history_proof_key_publication.proto index f90f3003d1..8ad6260159 100644 --- a/sdk/src/main/proto/history_proof_key_publication.proto +++ b/sdk/src/main/proto/history_proof_key_publication.proto @@ -12,6 +12,9 @@ syntax = "proto3"; package com.hedera.hapi.services.auxiliary.history; +// SPDX-License-Identifier: Apache-2.0 +import "history_types.proto"; + // SPDX-License-Identifier: Apache-2.0 option java_package = "com.hedera.hapi.services.auxiliary.history.legacy"; // <<>> This comment is special code for setting PBJ Compiler java package @@ -19,13 +22,32 @@ option java_package = "com.hedera.hapi.services.auxiliary.history.legacy"; option java_multiple_files = true; /** - * A transaction body to publish a node's history proof key. + * A transaction body to publish a node's history proof key and + * WRAPS messages. */ message HistoryProofKeyPublicationTransactionBody { + oneof data { + /** + * The proof key the submitting node intends to use when + * contributing WRAPS messages for use in proving history + * belongs to the chain of trust for the ledger id. + */ + bytes proof_key = 1; + + /** + * A WRAPS message from the submitting node. + */ + bytes wraps_message = 2; + } + + /** + * The id of the proof construction this publication is for. + */ + uint64 construction_id = 3; + /** - * The proof key the submitting node intends to use when - * contributing signatures for use in proving history - * belongs to the chain of trust for the ledger id. + * The phase of the proof construction this key is being + * published for. */ - bytes proof_key = 1; + com.hedera.hapi.node.state.history.WrapsPhase phase = 4; } diff --git a/sdk/src/main/proto/history_types.proto b/sdk/src/main/proto/history_types.proto index bf8b6c0a66..f79678d225 100644 --- a/sdk/src/main/proto/history_types.proto +++ b/sdk/src/main/proto/history_types.proto @@ -67,28 +67,31 @@ message History { } /** - * A proof that some address book history belongs to the ledger id's - * chain of trust. + * A proof that some address book history belongs to the ledger id's chain of trust. */ message HistoryProof { - /** - * The hash of the source address book. - */ - bytes source_address_book_hash = 1; /** * The proof keys for the target address book, needed to keep * constructing proofs after adopting the target address book's * roster at a handoff. */ - repeated ProofKey target_proof_keys = 2; + repeated ProofKey target_proof_keys = 1; /** * The target history of the proof. */ - History target_history = 3; + History target_history = 2; + /** + * The proof of chain of trust from the ledger id to the target + * history's metadata. May be switched from AggregatedNodeSignatures + * to a recursive proof when one becomes available. + */ + ChainOfTrustProof chain_of_trust_proof = 3; /** - * The proof of chain of trust from the ledger id. + * If set, the uncompressed proof of chain of trust from the ledger id to + * the target address book; the uncompressed version of the WRAPS proof is + * required to keep extending the chain of trust. */ - bytes proof = 4; + bytes uncompressed_wraps_proof = 4; } /** @@ -97,6 +100,8 @@ message HistoryProof { * weight to enact its own constructions. */ message HistoryProofConstruction { + reserved 3; + /** * The construction id. */ @@ -106,12 +111,6 @@ message HistoryProofConstruction { * certain thresholds are during construction. */ bytes source_roster_hash = 2; - /** - * If set, the proof that the address book of the source roster belongs - * to the the ledger id's chain of trust; if not set, the source roster's - * address book must *be* the ledger id. - */ - HistoryProof source_proof = 3; /** * The hash of the roster whose weights are used to assess progress * toward obtaining proof keys for parties that hold at least a @@ -123,7 +122,7 @@ message HistoryProofConstruction { /** * If the network is still gathering proof keys for this * construction, the next time at which nodes should stop waiting - * for tardy proof keys and assembly the history to be proven as + * for tardy proof keys and assemble the history to be proven as * soon as it has the associated metadata and proof keys for nodes * with >2/3 weight in the target roster. */ @@ -135,17 +134,42 @@ message HistoryProofConstruction { */ proto.Timestamp assembly_start_time = 6; /** - * When this construction is complete, the recursive proof that - * the target roster's address book and associated metadata belong - * to the ledger id's chain of trust. + * When this construction is complete, the proof that the target + * roster's address book and associated metadata belong to the + * ledger id's chain of trust. */ HistoryProof target_proof = 7; - /** * If set, the reason the construction failed. */ string failure_reason = 8; + /** + * If set, the state of the WRAPS signing protocol for this construction. + */ + WrapsSigningState wraps_signing_state = 9; } + + /** + * The number of times the network has had to restart this construction. + */ + uint32 wraps_retry_count = 10; +} + +/** + * The state of an ongoing WRAPS signature protocol. + */ +message WrapsSigningState { + /** + * The phase of the WRAPS protocol. + */ + WrapsPhase phase = 1; + + /** + * If the network is still gathering WRAPS messages for the R2 or R3 + * protocol phase, the next time at which nodes should stop waiting + * for tardy messages from R1 participants and start over. + */ + proto.Timestamp grace_period_end_time = 2; } /** @@ -210,3 +234,97 @@ message RecordedHistorySignature { HistorySignature history_signature = 2; } + +/** + * The phase of a WRAPS proof construction. + */ +enum WrapsPhase { + /** + * Nodes are publishing their proof keys and R1 messages. + */ + R1 = 0; + /** + * Nodes are publishing their R2 messages. + */ + R2 = 1; + /** + * Nodes are publishing their R3 messages. + */ + R3 = 2; + /** + * Nodes are aggregating all messages to produce a final signature. + */ + AGGREGATE = 3; + /** + * Nodes are doing work that must occur in a different event loop + * than the one used for the above phases. + */ + POST_AGGREGATION = 4; +} + +/** + * A message published by a node during a WRAPS proof construction. + */ +message WrapsMessageDetails { + /** + * The time at which the message was published. + */ + proto.Timestamp publication_time = 1; + /** + * The phase of the construction the message applies to. + */ + WrapsPhase phase = 2; + /** + * The message itself. + */ + bytes message = 3; +} + +/** + * A history of messages published during a WRAPS proof construction. + */ +message WrapsMessageHistory { + /* + * The published messages. + */ + repeated WrapsMessageDetails messages = 1; +} + +message ChainOfTrustProof { + oneof proof { + /** + * If there is not yet a SNARK proving the chain of trust from ledger id to + * the hinTS verification key, an aggregation of Schnorr signatures on + * the concatenation of the ledger id and genesis hinTS verification key + * that serve as witnesses for the SNARK prover algorithm. + */ + AggregatedNodeSignatures aggregated_node_signatures = 1; + /** + * If known, a ZK-compressed SNARK proof proving the chain of trust from + * the ledger id to this hinTS verification key. + */ + bytes wraps_proof = 2; + } +} + +/** + * An aggregation of node signatures on some data. + *

+ * Can be used to prove the genesis hinTS verification key in a block proof; but + * not succinct and not recursive; hence in normal operations with TSS, used only + * until the first recursive proof is available. + */ +message AggregatedNodeSignatures { + /** + * The aggregated signature. + */ + bytes aggregated_signature = 1; + /** + * In ascending order, the ids of the nodes that contributed signatures. + */ + repeated uint64 signing_node_ids = 2; + /** + * The hinTS key that this signature witnesses. + */ + bytes verification_key = 3; +} diff --git a/sdk/src/main/proto/hook_types.proto b/sdk/src/main/proto/hook_types.proto index 7a76a3a474..fbc3a1d22a 100644 --- a/sdk/src/main/proto/hook_types.proto +++ b/sdk/src/main/proto/hook_types.proto @@ -59,17 +59,16 @@ message HookCreationDetails { * The hook implementation. */ oneof hook { - /** - * A general-purpose hook programmed in EVM bytecode that may access state - * or interact with external contracts. - */ + /** + * A general-purpose hook programmed in EVM bytecode that may access state + * or interact with external contracts. + */ EvmHook evm_hook = 3; } /** * If set, a key that that can be used to remove or replace the hook; or (if - * applicable, as with an EVM hook) perform transactions that customize - * the hook. + * applicable, as with an EVM hook) do transactions that customize the hook. */ proto.Key admin_key = 4; } @@ -108,8 +107,6 @@ message EvmHookSpec { * Specifies a key/value pair in the storage of an EVM hook, either by the explicit storage * slot contents; or by a combination of a Solidity mapping's slot key and the key into * that mapping. - *

- * Either the hook owner's key, or its admin key, must sign the transaction. */ message EvmHookStorageUpdate { oneof update { @@ -152,7 +149,7 @@ message EvmHookMappingEntries { /** * An entry in a Solidity mapping. Very helpful for protocols that apply * `HookStore` to manage the entries of a hook contract's mapping instead - * of its raw storage slots. + * its raw storage slots. *

* This is especially attractive when the mapping value itself fits in a single * word; for more complicated value storage layouts it becomes necessary to diff --git a/sdk/src/main/proto/ledger_id_publication.proto b/sdk/src/main/proto/ledger_id_publication.proto new file mode 100644 index 0000000000..927fe08612 --- /dev/null +++ b/sdk/src/main/proto/ledger_id_publication.proto @@ -0,0 +1,74 @@ +/** + * # Ledger Id Publication + * A system-initiated (i.e. internal) transaction to externalize a new + * ledger id and the verification key to use in verifying recursive + * chain-of-trust proofs from the new ledger id. + * + * ### Keywords + * The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + * "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + * document are to be interpreted as described in + * [RFC2119](https://www.ietf.org/rfc/rfc2119) and clarified in + * [RFC8174](https://www.ietf.org/rfc/rfc8174). + */ +syntax = "proto3"; + +package com.hedera.hapi.node.tss; + +// SPDX-License-Identifier: Apache-2.0 +option java_package = "com.hedera.hapi.node.tss.legacy"; +// <<>> This comment is special code for setting PBJ Compiler java package +option java_multiple_files = true; + +/** + * A system initiated transaction to externalize the ledger id and verification + * key for recursive chain-of-trust proofs extending from the new ledger id. + * + * This transaction SHALL be issued whenever the ledger id changes.
+ * This transaction MUST NOT be sent by a client and SHALL be rejected if + * received by any node.
+ * This transaction SHALL be present in the record stream or block stream. + * + * ### Block Stream Effects + * None + */ +message LedgerIdPublicationTransactionBody { + /** + * The new ledger id. + */ + bytes ledger_id = 1; + + /** + * The key to use when verifying recursive chain-of-trust proofs that extend + * from this ledger id. + */ + bytes history_proof_verification_key = 2; + + /** + * The node-specific contributions made to the new ledger id. + */ + repeated LedgerIdNodeContribution node_contributions = 3; +} + +/** + * A record of the proof key a node had in a particular address + * book. Necessary to keep at each point history so that nodes + * can verify the correct key was used to sign in transitions + * starting from the current address book; no matter how keys + * have been rotated from the time the address book was created. + */ +message LedgerIdNodeContribution { + /** + * The node id. + */ + uint64 node_id = 1; + /** + * The node's consensus weight when the ledger id was published. + */ + uint64 weight = 2; + /** + * The public key the node used to contribute to history proofs when the + * ledger id was published. + */ + bytes history_proof_key = 3; +} diff --git a/sdk/src/main/proto/node.proto b/sdk/src/main/proto/node.proto index c1b5c98d9e..df9c39eafd 100644 --- a/sdk/src/main/proto/node.proto +++ b/sdk/src/main/proto/node.proto @@ -158,4 +158,17 @@ message Node { * This field SHALL enable frontend clients to avoid hard-coded proxy endpoints. */ proto.ServiceEndpoint grpc_proxy_endpoint = 12; + + /** + * A list of registered nodes operated by the same entity as this node.
+ * This value may contain a list of "registered nodes" (as described in + * HIP-1137) that are operated by the same entity that operates this + * consensus node. + *

+ * This field is OPTIONAL and MAY be empty.
+ * This field MUST NOT contain more than twenty(20) entries.
+ * Every entry in this list MUST be a valid `registered_node_id` for a + * current registered node. + */ + repeated uint64 associated_registered_node = 13; } diff --git a/sdk/src/main/proto/node_create.proto b/sdk/src/main/proto/node_create.proto index 4792a0a2cc..c4d461287c 100644 --- a/sdk/src/main/proto/node_create.proto +++ b/sdk/src/main/proto/node_create.proto @@ -147,4 +147,17 @@ message NodeCreateTransactionBody { * This field SHALL enable frontend clients to avoid hard-coded proxy endpoints. */ proto.ServiceEndpoint grpc_proxy_endpoint = 9; + + /** + * A list of registered nodes operated by the same entity as this node.
+ * This value may contain a list of "registered nodes" (as described in + * HIP-1137) that are operated by the same entity that operates this + * consensus node. + *

+ * This field is OPTIONAL and MAY be empty.
+ * This field MUST NOT contain more than twenty(20) entries.
+ * Every entry in this list MUST be a valid `registered_node_id` for a + * current registered node. + */ + repeated uint64 associated_registered_node = 10; } diff --git a/sdk/src/main/proto/node_payments.proto b/sdk/src/main/proto/node_payments.proto new file mode 100644 index 0000000000..8b7591c02d --- /dev/null +++ b/sdk/src/main/proto/node_payments.proto @@ -0,0 +1,50 @@ +/** + * # Node Payments + * This is a Singleton that stores node fees accumulated in the current staking period. + * + * ### Keywords + * The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + * "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + * document are to be interpreted as described in [RFC2119](https://www.ietf.org/rfc/rfc2119) + * and clarified in [RFC8174](https://www.ietf.org/rfc/rfc8174). + */ +syntax = "proto3"; + +package com.hedera.hapi.node.state.token; + +// SPDX-License-Identifier: Apache-2.0 +option java_package = "com.hedera.hapi.node.state.token.legacy"; +// <<>> This comment is special code for setting PBJ Compiler java package +option java_multiple_files = true; + +import "timestamp.proto"; +import "basic_types.proto"; + +/** + * A singleton state object that accumulates node fees for distribution
+ * This is used to record the total amount of fees due to each node for distribution in current staking period + */ +message NodePayments { + /** + * The time when the last node fees were distributed + */ + proto.Timestamp last_node_fee_distribution_time = 1; + /** + * A list of node account id to the total amount of fees due to each node in the current staking period + */ + repeated NodePayment payments = 2; +} + +/** + * A record of the total amount of fees due to a node + */ +message NodePayment { + /** + * The node account id + */ + proto.AccountID node_account_id = 1; + /** + * The total amount of fees due to the node in tinybars + */ + uint64 fees = 2; +} diff --git a/sdk/src/main/proto/node_update.proto b/sdk/src/main/proto/node_update.proto index b6091ac0f9..738ce99428 100644 --- a/sdk/src/main/proto/node_update.proto +++ b/sdk/src/main/proto/node_update.proto @@ -167,4 +167,36 @@ message NodeUpdateTransactionBody { * web proxy. */ proto.ServiceEndpoint grpc_proxy_endpoint = 10; + + /** + * A list of registered nodes operated by the same entity as this node.
+ * This value may contain a list of "registered nodes" (as described in + * HIP-1137) that are operated by the same entity that operates this + * consensus node. + *

+ * This field is OPTIONAL.
+ * If this field is not set, the current list SHALL NOT change.
+ * If this field is set, but contains an empty list, any existing + * associated registered nodes SHALL be removed.
+ * This field MUST NOT contain more than twenty(20) entries.
+ * Every entry in this list MUST be a valid `registered_node_id` for a + * current registered node. + */ + AssociatedRegisteredNodeList associated_registered_node_list = 11; +} + +/** + * A wrapper around a list of associated registered node identifiers.
+ * This wrapper exists to enable an update transaction to differentiate + * between a field that is not set and an empty list of values. + *

+ * An _unset_ field of this type SHALL NOT modify existing values.
+ * A _set_ field of this type with an empty list SHALL remove any + * existing values. + */ +message AssociatedRegisteredNodeList { + /** + * A list of registered node identifiers. + */ + repeated uint64 associated_registered_node = 1; } diff --git a/sdk/src/main/proto/registered_node.proto b/sdk/src/main/proto/registered_node.proto new file mode 100644 index 0000000000..7bdd96ba08 --- /dev/null +++ b/sdk/src/main/proto/registered_node.proto @@ -0,0 +1,67 @@ +syntax = "proto3"; + +package com.hedera.hapi.node.state.addressbook; + +// SPDX-License-Identifier: Apache-2.0 +import "basic_types.proto"; +import "registered_service_endpoint.proto"; + +option java_package = "com.hedera.hashgraph.sdk.proto"; +// <<>> This comment is special code for setting PBJ Compiler java package +option java_multiple_files = true; + +/** + * A single registered node in the network state. + * + * Each registered node in the network state SHALL represent a single + * non-consensus node that is registered on the network. + * + * Registered node identifiers SHALL only be unique within a single + * realm and shard combination. + */ +message RegisteredNode { + /** + * A registered node identifier. + *

+ * This value SHALL be set.
+ * This value SHALL be unique within a given network.
+ * This value SHALL NOT match any consensus node ID in the same network. + *

+ * A given value for `registered_node_id` MUST be unique within a given + * shard or realm.
+ * A given value for `registered_node_id` MUST NOT be reused, even if the + * corresponding entry is later deleted. + */ + uint64 registered_node_id = 1; + + /** + * An administrative key controlled by the node operator. + *

+ * This key MUST sign each transaction to update this node.
+ * This field SHALL contain a valid `Key` value.
+ * This field SHALL NOT be set to an empty `KeyList`.
+ * It is RECOMMENDED that this key be composed of one or more unique public + * keys that are not associated with any network account.
+ * This key MAY be a complex key containing `KeyList` or `ThresholdKey` + * elements, but SHOULD NOT be a contract ID key. + */ + proto.Key admin_key = 2; + + /** + * A short description of the node. + *

+ * This value SHALL NOT exceed 100 bytes when encoded as UTF-8. + */ + string description = 3; + + /** + * A list of service endpoints for client calls. + *

+ * These endpoints SHALL represent the published endpoints to which + * clients may submit requests.
+ * One Registered Node MAY expose endpoints for multiple service types.
+ * This list SHALL NOT be empty.
+ * This list SHALL NOT contain more than `50` entries. + */ + repeated com.hedera.hapi.node.addressbook.RegisteredServiceEndpoint service_endpoint = 4; +} diff --git a/sdk/src/main/proto/registered_node_create.proto b/sdk/src/main/proto/registered_node_create.proto new file mode 100644 index 0000000000..37861550be --- /dev/null +++ b/sdk/src/main/proto/registered_node_create.proto @@ -0,0 +1,61 @@ +syntax = "proto3"; + +package com.hedera.hapi.node.addressbook; + +// SPDX-License-Identifier: Apache-2.0 +option java_package = "com.hedera.hashgraph.sdk.proto"; +// <<>> This comment is special code for setting PBJ Compiler java package +option java_multiple_files = true; + +import "basic_types.proto"; +import "registered_service_endpoint.proto"; + +/** + * A transaction body to create a new registered node in the network + * address book. + * + * This transaction, once complete, SHALL add a new registered node to the + * network state. + * The new registered node SHALL be visible and discoverable upon + * completion of this transaction. + * + * ### Block Stream Effects + * None. + */ +message RegisteredNodeCreateTransactionBody { + /** + * An administrative key controlled by the node operator. + *

+ * This key MUST sign this transaction.
+ * This key MUST sign each transaction to update this node.
+ * This field MUST contain a valid `Key` value.
+ * This field is REQUIRED and MUST NOT be set to an empty `KeyList`.
+ * It is RECOMMENDED that this key be composed of one or more unique public + * keys that are not associated with any network account.
+ * This key MAY be a complex key containing `KeyList` or `ThresholdKey` + * elements, but SHOULD NOT be a contract ID key. + */ + proto.Key admin_key = 1; + + /** + * A short description of the node. + *

+ * This value, if set, MUST NOT exceed 100 bytes when encoded as UTF-8.
+ * This field is OPTIONAL. + */ + string description = 2; + + /** + * A list of service endpoints for client calls. + *

+ * These endpoints SHALL represent the published endpoints to which + * clients may submit requests.
+ * Endpoints in this list MAY supply either IP address or FQDN, but MUST + * NOT supply both values for the same endpoint.
+ * Multiple endpoints in this list MAY resolve to the same interface.
+ * One Registered Node MAY expose endpoints for multiple service types.
+ * This list MUST NOT be empty.
+ * This list MUST NOT contain more than `50` entries. + */ + repeated RegisteredServiceEndpoint service_endpoint = 3; +} diff --git a/sdk/src/main/proto/registered_node_delete.proto b/sdk/src/main/proto/registered_node_delete.proto new file mode 100644 index 0000000000..2fd91e6a27 --- /dev/null +++ b/sdk/src/main/proto/registered_node_delete.proto @@ -0,0 +1,36 @@ +syntax = "proto3"; + +package com.hedera.hapi.node.addressbook; + +// SPDX-License-Identifier: Apache-2.0 +option java_package = "com.hedera.hashgraph.sdk.proto"; +// <<>> This comment is special code for setting PBJ Compiler java package +option java_multiple_files = true; + +/** + * A transaction body to delete a registered node from the network + * address book. + * + * This transaction, once complete, SHALL remove the identified registered + * node from the network state. + * This transaction MUST be signed by the existing entry `admin_key` or + * authorized by the Hiero network governance structure. + * + * ### Block Stream Effects + * None. + */ +message RegisteredNodeDeleteTransactionBody { + /** + * A registered node identifier in the network state. + *

+ * The node identified MUST exist in the registered address book.
+ * The node identified MUST NOT be deleted.
+ * This value is REQUIRED. + *

+ * A given value for `registered_node_id` SHALL be unique within a given + * shard or realm.
+ * A given value for `registered_node_id` SHALL NOT be reused, even if the + * corresponding entry is deleted. + */ + uint64 registered_node_id = 1; +} diff --git a/sdk/src/main/proto/registered_node_update.proto b/sdk/src/main/proto/registered_node_update.proto new file mode 100644 index 0000000000..b579b7cb64 --- /dev/null +++ b/sdk/src/main/proto/registered_node_update.proto @@ -0,0 +1,78 @@ +syntax = "proto3"; + +package com.hedera.hapi.node.addressbook; + +// SPDX-License-Identifier: Apache-2.0 +option java_package = "com.hedera.hashgraph.sdk.proto"; +// <<>> This comment is special code for setting PBJ Compiler java package +option java_multiple_files = true; + +import "google/protobuf/wrappers.proto"; +import "basic_types.proto"; +import "registered_service_endpoint.proto"; + +/** + * A transaction body to update an existing registered node in the network + * address book. + * + * This transaction, once complete, SHALL modify the identified registered + * node state as requested. + * + * ### Block Stream Effects + * None. + */ +message RegisteredNodeUpdateTransactionBody { + /** + * A registered node identifier in the network state. + *

+ * The node identified MUST exist in the registered address book.
+ * The node identified MUST NOT be deleted.
+ * This value is REQUIRED. + *

+ * A given value for `registered_node_id` SHALL be unique within a given + * shard or realm.
+ * A given value for `registered_node_id` SHALL NOT be reused, even if the + * corresponding entry is deleted. + */ + uint64 registered_node_id = 1; + + /** + * An administrative key controlled by the node operator. + *

+ * This field is OPTIONAL.
+ * If set, this key MUST sign this transaction.
+ * If set, this key MUST sign each subsequent transaction to + * update this node.
+ * If set, this field MUST contain a valid `Key` value.
+ * If set, this field MUST NOT be set to an empty `KeyList`.
+ * It is RECOMMENDED that this key be composed of one or more unique public + * keys that are not associated with any network account.
+ * This key MAY be a complex key containing `KeyList` or `ThresholdKey` + * elements, but SHOULD NOT be a contract ID key. + */ + proto.Key admin_key = 2; + + /** + * A short description of the node. + *

+ * This field is OPTIONAL.
+ * This value, if set, MUST NOT exceed 100 bytes when encoded as UTF-8.
+ * If set, this value SHALL replace the previous value. + */ + google.protobuf.StringValue description = 3; + + /** + * A list of service endpoints for client calls. + *

+ * This field is OPTIONAL.
+ * These endpoints SHALL represent the published endpoints to which + * clients may submit requests.
+ * Endpoints in this list MAY supply either IP address or FQDN, but MUST + * NOT supply both values for the same endpoint.
+ * One Registered Node MAY expose endpoints for multiple service types.
+ * If set, this list MUST NOT be empty.
+ * If set, this list MUST NOT contain more than `50` entries.
+ * If set, this list SHALL _replace_ the previous list. + */ + repeated RegisteredServiceEndpoint service_endpoint = 4; +} diff --git a/sdk/src/main/proto/registered_service_endpoint.proto b/sdk/src/main/proto/registered_service_endpoint.proto new file mode 100644 index 0000000000..6bdbabf36c --- /dev/null +++ b/sdk/src/main/proto/registered_service_endpoint.proto @@ -0,0 +1,160 @@ +syntax = "proto3"; + +package com.hedera.hapi.node.addressbook; + +// SPDX-License-Identifier: Apache-2.0 +option java_package = "com.hedera.hashgraph.sdk.proto"; +// <<>> This comment is special code for setting PBJ Compiler java package +option java_multiple_files = true; + +/** + * A registered network node endpoint.
+ * Each registered network node in the global address book publishes one or + * more endpoints which enable the nodes to communicate both with other + * nodes and with clients to receive requests. + * + * This message supports a TCP/UDP port and IP address (V4 or V6), + * or MAY include a FQDN _instead of_ an IP address.
+ */ +message RegisteredServiceEndpoint { + /** + * An IP address or fully qualified domain name. + *

+ * This oneof is REQUIRED. + */ + oneof address { + /** + * A 32-bit IPv4 address or 128-bit IPv6 address.
+ * This is the address of the endpoint, encoded in pure "big-endian" + * (i.e. left to right) order (e.g. IPv4 address `127.0.0.1` has + * hex bytes in the order `7F`, `00`, `00`, `01`.
+ * IPv6 address `::1` has hex bytes `00`, `00`, `00`, `00`, `00`, `00`, + * `00`, `00`, `00`, `00`, `00`, `00`, `00`, `00`, `00`, `01`). + */ + bytes ip_address = 1; + + /** + * A node domain name. + *

+ * This MUST be the fully qualified domain name of the node.
+ * This value MUST NOT exceed 250 ASCII characters.
+ * This value MUST meet all other DNS name requirements. + */ + string domain_name = 2; + } + + /** + * A network port to use. + *

+ * This value MUST be between 0 and 65535, inclusive.
+ * This value is REQUIRED. + */ + uint32 port = 3; + + /** + * A flag indicating if this endpoint requires TLS. + *

+ * If this value is set true, then connections to this endpoint MUST + * enable TLS. + *

+ * TLS endpoints MAY use self-signed certificates for this purpose, + * but use of self-signed certificates SHOULD be limited to testing and + * development environments to ensure production environments meet all + * expected characteristics for transport layer security. + */ + bool requires_tls = 4; + + /** + * An endpoint type.
+ * This declares the type of registered node endpoint and includes any + * type-specific fields. + *

+ * This oneof is REQUIRED. + */ + oneof endpoint_type { + /** + * A Block Node.
+ * A Block Node stores the block chain, provides content proof services, + * and delivers the block stream to other clients. + */ + BlockNodeEndpoint block_node = 5; + + /** + * A Mirror Node.
+ * A Mirror Node is an advanced indexing and query service that provides + * fast and flexible access to query the block chain and transaction + * history. A Mirror Node typically stores all recent blockchain data, + * and some store the entire history of the network. + */ + MirrorNodeEndpoint mirror_node = 6; + + /** + * A RPC Relay.
+ * A RPC Relay is a proxy and translator between EVM tooling and a + * Hiero consensus network. + */ + RpcRelayEndpoint rpc_relay = 7; + } + + /** + * A message indicating this endpoint is a Block Node endpoint.
+ * Block Node endpoints offer one of several block node APIs, so this + * endpoint entry also declares which well-known Block Node API is + * supported by this endpoint, or "OTHER" for less common APIs. + */ + message BlockNodeEndpoint { + /** + * An indicator of what API this endpoint supports. + *

+ * This field is REQUIRED. + */ + BlockNodeApi endpoint_api = 1; + + /** + * An enumeration of well-known block node endpoint APIs, and a + * catch-all option for otherwise unknown endpoint APIs. + */ + enum BlockNodeApi { + /** + * Any other API type associated with a block node.
+ * The caller must consult local documentation to determine the + * correct call semantics.
+ * It is RECOMMENDED to call the detail status endpoint for further + * information before using this endpoint. + */ + OTHER = 0; + + /** + * The Block Node Status API. + */ + STATUS = 1; + + /** + * The Block Node Publish API. + */ + PUBLISH = 2; + + /** + * The Block Node Subscribe Stream API. + */ + SUBSCRIBE_STREAM = 3; + + /** + * The Block Node State Proof API. + */ + STATE_PROOF = 4; + } + } + + /** + * A message indicating this endpoint is a Mirror Node endpoint. + */ + message MirrorNodeEndpoint { + } + + /** + * A message indicating this endpoint is a RPC Relay endpoint. + */ + message RpcRelayEndpoint { + } +} \ No newline at end of file diff --git a/sdk/src/main/proto/response_code.proto b/sdk/src/main/proto/response_code.proto index 6137aa1b30..a48d14dd5d 100644 --- a/sdk/src/main/proto/response_code.proto +++ b/sdk/src/main/proto/response_code.proto @@ -1846,7 +1846,7 @@ enum ResponseCodeEnum { /** * The HookStore tried to update too many storage slots in a single transaction. */ - TOO_MANY_LAMBDA_STORAGE_UPDATES = 513; + TOO_MANY_EVM_HOOK_STORAGE_UPDATES = 513; /** * An EVM hook mapping slot, storage key, or storage value failed to use the @@ -1855,7 +1855,7 @@ enum ResponseCodeEnum { HOOK_CREATION_BYTES_MUST_USE_MINIMAL_REPRESENTATION = 514; /** - * An EVM hook mapping slot, storage key, or storage value exceeded 32 bytes. + * A EVM hook mapping slot, storage key, or storage value exceeded 32 bytes. */ HOOK_CREATION_BYTES_TOO_LONG = 515; @@ -1923,4 +1923,16 @@ enum ResponseCodeEnum { * resubmitted once the account has been funded. */ NODE_ACCOUNT_HAS_ZERO_BALANCE = 526; + + /** + * This operation cannot be completed because the target + * account is a "Fee Collection Account".
+ * Any attempt to transfer to a fee collection account is not permitted. + */ + TRANSFER_TO_FEE_COLLECTION_ACCOUNT_NOT_ALLOWED = 527; + + /** + * The number of hook invocations exceeds the maximum allowed per transaction. + */ + TOO_MANY_HOOK_INVOCATIONS = 528; } diff --git a/sdk/src/main/proto/schedulable_transaction_body.proto b/sdk/src/main/proto/schedulable_transaction_body.proto index 39ec4895c9..6e398f0945 100644 --- a/sdk/src/main/proto/schedulable_transaction_body.proto +++ b/sdk/src/main/proto/schedulable_transaction_body.proto @@ -76,6 +76,10 @@ import "node_create.proto"; import "node_update.proto"; import "node_delete.proto"; +import "registered_node_create.proto"; +import "registered_node_update.proto"; +import "registered_node_delete.proto"; + /** * A schedulable transaction. * @@ -403,6 +407,21 @@ message SchedulableTransactionBody { * other recipients receive the "airdropped" tokens immediately. */ TokenAirdropTransactionBody tokenAirdrop = 48; + + /** + * Create a new registered node in the network address book. + */ + com.hedera.hapi.node.addressbook.RegisteredNodeCreateTransactionBody registeredNodeCreate = 49; + + /** + * Update a registered node in the network address book. + */ + com.hedera.hapi.node.addressbook.RegisteredNodeUpdateTransactionBody registeredNodeUpdate = 50; + + /** + * Delete a registered node from the network address book. + */ + com.hedera.hapi.node.addressbook.RegisteredNodeDeleteTransactionBody registeredNodeDelete = 51; } /** diff --git a/sdk/src/main/proto/smart_contract_service.proto b/sdk/src/main/proto/smart_contract_service.proto index 246a471fcb..cb6e32e86b 100644 --- a/sdk/src/main/proto/smart_contract_service.proto +++ b/sdk/src/main/proto/smart_contract_service.proto @@ -172,11 +172,6 @@ service SmartContractService { */ rpc callEthereum (Transaction) returns (TransactionResponse); - /** - * Update zero or more slots of a lambda. - */ - rpc lambdaSStore (Transaction) returns (TransactionResponse); - /** * Update zero or more slots of an EVM hook's storage. */ diff --git a/sdk/src/main/proto/throttle_definitions.proto b/sdk/src/main/proto/throttle_definitions.proto index 31f9868e63..2cecf1806e 100644 --- a/sdk/src/main/proto/throttle_definitions.proto +++ b/sdk/src/main/proto/throttle_definitions.proto @@ -109,6 +109,11 @@ message ThrottleBucket { * This list MUST contain at least one entry. */ repeated ThrottleGroup throttleGroups = 3; + + /** + * If set to true, this bucket is used for high-volume throttles. + */ + bool high_volume = 4; } /** diff --git a/sdk/src/main/proto/transaction.proto b/sdk/src/main/proto/transaction.proto index 63a2a1b709..d291a836a0 100644 --- a/sdk/src/main/proto/transaction.proto +++ b/sdk/src/main/proto/transaction.proto @@ -98,9 +98,15 @@ import "history_proof_signature.proto"; import "history_proof_key_publication.proto"; import "history_proof_vote.proto"; +import "ledger_id_publication.proto"; + import "hook_store.proto"; import "hook_dispatch.proto"; +import "registered_node_create.proto"; +import "registered_node_update.proto"; +import "registered_node_delete.proto"; + /** * A wrapper around signed transaction bytes.
* This was originally a transaction with body, signatures, and/or bytes, @@ -661,6 +667,35 @@ message TransactionBody { * An internal-only transaction body for dispatching a hook CRUD operation. */ com.hedera.hapi.node.hooks.HookDispatchTransactionBody hook_dispatch = 76; + + /** + * An internal-only transaction body for publishing the ledger id. + */ + com.hedera.hapi.node.tss.LedgerIdPublicationTransactionBody ledger_id_publication = 77; + + /** + * Create a new registered node in the network address book. + *

+ * This transaction SHALL create a new registered node record and add + * that record to the network state. + */ + com.hedera.hapi.node.addressbook.RegisteredNodeCreateTransactionBody registeredNodeCreate = 78; + + /** + * Update a registered node in the network address book. + *

+ * This transaction SHALL update an existing registered node record in + * the network state. + */ + com.hedera.hapi.node.addressbook.RegisteredNodeUpdateTransactionBody registeredNodeUpdate = 79; + + /** + * Delete a registered node from the network address book. + *

+ * This transaction SHALL mark an existing registered node record as + * deleted. + */ + com.hedera.hapi.node.addressbook.RegisteredNodeDeleteTransactionBody registeredNodeDelete = 80; } /** @@ -671,6 +706,13 @@ message TransactionBody { * If used with a transaction type that does not support custom fee limits, the transaction will fail. */ repeated CustomFeeLimit max_custom_fees = 1001; + + /** + * If set to true, this transaction uses high-volume throttles and pricing + * for entity creation. It only affects supported transaction types; otherwise, + * it is ignored. + */ + bool high_volume = 1002; } /** diff --git a/sdk/src/main/proto/transaction_receipt.proto b/sdk/src/main/proto/transaction_receipt.proto index 008aef6d1f..2d1422e457 100644 --- a/sdk/src/main/proto/transaction_receipt.proto +++ b/sdk/src/main/proto/transaction_receipt.proto @@ -194,4 +194,14 @@ message TransactionReceipt { * This value SHALL NOT be set following any other transaction. */ uint64 node_id = 15; + + /** + * The identifier of a newly created RegisteredNode. + *

+ * This value SHALL be set following a `createRegisteredNode` + * transaction.
+ * This value SHALL NOT be set following any other transaction.
+ * This value SHALL be unique within a given network. + */ + uint64 registered_node_id = 16; } diff --git a/sdk/src/main/proto/transaction_record.proto b/sdk/src/main/proto/transaction_record.proto index e0baef1c08..1be541ff71 100644 --- a/sdk/src/main/proto/transaction_record.proto +++ b/sdk/src/main/proto/transaction_record.proto @@ -208,6 +208,15 @@ message TransactionRecord { * or when the recipient has set `receiver_sig_required`. */ repeated PendingAirdropRecord new_pending_airdrops = 22; + + /** + * A high volume pricing multiplier. + *

+ * This SHALL be the multiplier that is applied to the transaction + * fees charged for this transaction if the high volume flag is set. + * This is scaled by 1000. + */ + uint64 high_volume_pricing_multiplier = 23; } /** diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/StatusTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/StatusTest.java index 67045a950a..63f3110a4a 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/StatusTest.java +++ b/sdk/src/test/java/com/hedera/hashgraph/sdk/StatusTest.java @@ -57,7 +57,7 @@ void newHookAndLambdaStatusesRoundTrip() { {Status.HOOKS_NOT_ENABLED, ResponseCodeEnum.HOOKS_NOT_ENABLED}, {Status.HOOK_IS_NOT_AN_EVM_HOOK, ResponseCodeEnum.HOOK_IS_NOT_AN_EVM_HOOK}, {Status.HOOK_DELETED, ResponseCodeEnum.HOOK_DELETED}, - {Status.TOO_MANY_LAMBDA_STORAGE_UPDATES, ResponseCodeEnum.TOO_MANY_LAMBDA_STORAGE_UPDATES}, + {Status.TOO_MANY_EVM_HOOK_STORAGE_UPDATES, ResponseCodeEnum.TOO_MANY_EVM_HOOK_STORAGE_UPDATES}, { Status.HOOK_CREATION_BYTES_MUST_USE_MINIMAL_REPRESENTATION, ResponseCodeEnum.HOOK_CREATION_BYTES_MUST_USE_MINIMAL_REPRESENTATION diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionRecordTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionRecordTest.java index 828c37f9b6..59324ebfd1 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionRecordTest.java +++ b/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionRecordTest.java @@ -88,7 +88,8 @@ private static TransactionRecord spawnRecordExample(@Nullable ByteString prngByt AccountId.fromString("0.0.123"), AccountId.fromString("0.0.124"), TokenId.fromString("0.0.12345")), - 123))); + 123)), + 1000L); } @Test diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionRecordTest.snap b/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionRecordTest.snap index e95c8f4570..c3ccf38f4a 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionRecordTest.snap +++ b/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionRecordTest.snap @@ -1,8 +1,8 @@ com.hedera.hashgraph.sdk.TransactionRecordTest.shouldSerialize2=[ - "TransactionRecord{receipt=TransactionReceipt{transactionId=null, status=SCHEDULE_ALREADY_DELETED, exchangeRate=ExchangeRate{hbars=3, cents=4, expirationTime=2019-04-01T22:42:22Z, exchangeRateInCents=1.3333333333333333}, nextExchangeRate=ExchangeRate{hbars=3, cents=4, expirationTime=2019-04-01T22:42:22Z, exchangeRateInCents=1.3333333333333333}, accountId=1.2.3, fileId=4.5.6, contractId=3.2.1, topicId=9.8.7, tokenId=6.5.4, topicSequenceNumber=3, topicRunningHash=[54, 56, 54, 102, 55, 55, 50, 48, 54, 101, 54, 102, 55, 55, 50, 48, 54, 50, 55, 50, 54, 102, 55, 55, 54, 101, 50, 48, 54, 51, 54, 102, 55, 55], totalSupply=30, scheduleId=1.1.1, scheduledTransactionId=3.3.3@1554158542.000000000, serials=[1, 2, 3], nodeId=1, duplicates=[], children=[]}, transactionHash=68656c6c6f, consensusTimestamp=2019-04-01T22:42:22Z, transactionId=3.3.3@1554158542.000000000, transactionMemo=memo, transactionFee=3000 tℏ, contractFunctionResult=ContractFunctionResult{contractId=1.2.3, evmAddress=1.2.98329e006610472e6b372c080833f6d79ed833cf, errorMessage=null, bloom=, gasUsed=0, logs=[], createdContractIds=[], stateChanges=[], gas=0, hbarAmount=0 tℏ, contractFunctionparametersBytes=, rawResult=00000000000000000000000000000000000000000000000000000000ffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000011223344556677889900aabbccddeeff00112233ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001448656c6c6f2c20776f726c642c20616761696e21000000000000000000000000, senderAccountId=1.2.3, contractNonces=[], signerNonce=0}, transfers=[Transfer{accountId=4.4.4, amount=5 ℏ}], tokenTransfers={6.6.6={1.1.1=4}}, tokenNftTransfers={4.4.4=[TokenNftTransfer{tokenId=4.4.4, sender=1.2.3, receiver=3.2.1, serial=4, isApproved=true}]}, scheduleRef=3.3.3, assessedCustomFees=[AssessedCustomFee{amount=4, tokenId=4.5.6, feeCollectorAccountId=8.6.5, payerAccountIdList=[3.3.3]}], automaticTokenAssociations=[TokenAssociation{tokenId=5.4.3, accountId=8.7.6}], aliasKey=3036301006072a8648ce3d020106052b8104000a03220002703a9370b0443be6ae7c507b0aec81a55e94e4a863b9655360bd65358caa6588, children=[], duplicates=[], parentConsensusTimestamp=2019-04-01T22:42:22Z, ethereumHash=536f6d652068617368, paidStakingRewards=[Transfer{accountId=1.2.3, amount=8 ℏ}], prngBytes=null, prngNumber=4, evmAddress=30783030, pendingAirdropRecords=[PendingAirdropRecord{pendingAirdropId=PendingAirdropId{sender=0.0.123, receiver=0.0.124, tokenId=null, nftId=0.0.5005/1234}, pendingAirdropAmount=123}, PendingAirdropRecord{pendingAirdropId=PendingAirdropId{sender=0.0.123, receiver=0.0.124, tokenId=0.0.12345, nftId=null}, pendingAirdropAmount=123}]}" + "TransactionRecord{receipt=TransactionReceipt{transactionId=null, status=SCHEDULE_ALREADY_DELETED, exchangeRate=ExchangeRate{hbars=3, cents=4, expirationTime=2019-04-01T22:42:22Z, exchangeRateInCents=1.3333333333333333}, nextExchangeRate=ExchangeRate{hbars=3, cents=4, expirationTime=2019-04-01T22:42:22Z, exchangeRateInCents=1.3333333333333333}, accountId=1.2.3, fileId=4.5.6, contractId=3.2.1, topicId=9.8.7, tokenId=6.5.4, topicSequenceNumber=3, topicRunningHash=[54, 56, 54, 102, 55, 55, 50, 48, 54, 101, 54, 102, 55, 55, 50, 48, 54, 50, 55, 50, 54, 102, 55, 55, 54, 101, 50, 48, 54, 51, 54, 102, 55, 55], totalSupply=30, scheduleId=1.1.1, scheduledTransactionId=3.3.3@1554158542.000000000, serials=[1, 2, 3], nodeId=1, duplicates=[], children=[]}, transactionHash=68656c6c6f, consensusTimestamp=2019-04-01T22:42:22Z, transactionId=3.3.3@1554158542.000000000, transactionMemo=memo, transactionFee=3000 tℏ, contractFunctionResult=ContractFunctionResult{contractId=1.2.3, evmAddress=1.2.98329e006610472e6b372c080833f6d79ed833cf, errorMessage=null, bloom=, gasUsed=0, logs=[], createdContractIds=[], stateChanges=[], gas=0, hbarAmount=0 tℏ, contractFunctionparametersBytes=, rawResult=00000000000000000000000000000000000000000000000000000000ffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000011223344556677889900aabbccddeeff00112233ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001448656c6c6f2c20776f726c642c20616761696e21000000000000000000000000, senderAccountId=1.2.3, contractNonces=[], signerNonce=0}, transfers=[Transfer{accountId=4.4.4, amount=5 ℏ}], tokenTransfers={6.6.6={1.1.1=4}}, tokenNftTransfers={4.4.4=[TokenNftTransfer{tokenId=4.4.4, sender=1.2.3, receiver=3.2.1, serial=4, isApproved=true}]}, scheduleRef=3.3.3, assessedCustomFees=[AssessedCustomFee{amount=4, tokenId=4.5.6, feeCollectorAccountId=8.6.5, payerAccountIdList=[3.3.3]}], automaticTokenAssociations=[TokenAssociation{tokenId=5.4.3, accountId=8.7.6}], aliasKey=3036301006072a8648ce3d020106052b8104000a03220002703a9370b0443be6ae7c507b0aec81a55e94e4a863b9655360bd65358caa6588, children=[], duplicates=[], parentConsensusTimestamp=2019-04-01T22:42:22Z, ethereumHash=536f6d652068617368, paidStakingRewards=[Transfer{accountId=1.2.3, amount=8 ℏ}], prngBytes=null, prngNumber=4, evmAddress=30783030, pendingAirdropRecords=[PendingAirdropRecord{pendingAirdropId=PendingAirdropId{sender=0.0.123, receiver=0.0.124, tokenId=null, nftId=0.0.5005/1234}, pendingAirdropAmount=123}, PendingAirdropRecord{pendingAirdropId=PendingAirdropId{sender=0.0.123, receiver=0.0.124, tokenId=0.0.12345, nftId=null}, pendingAirdropAmount=123}], highVolumePricingMultiplier=1000}" ] com.hedera.hashgraph.sdk.TransactionRecordTest.shouldSerialize=[ - "TransactionRecord{receipt=TransactionReceipt{transactionId=null, status=SCHEDULE_ALREADY_DELETED, exchangeRate=ExchangeRate{hbars=3, cents=4, expirationTime=2019-04-01T22:42:22Z, exchangeRateInCents=1.3333333333333333}, nextExchangeRate=ExchangeRate{hbars=3, cents=4, expirationTime=2019-04-01T22:42:22Z, exchangeRateInCents=1.3333333333333333}, accountId=1.2.3, fileId=4.5.6, contractId=3.2.1, topicId=9.8.7, tokenId=6.5.4, topicSequenceNumber=3, topicRunningHash=[54, 56, 54, 102, 55, 55, 50, 48, 54, 101, 54, 102, 55, 55, 50, 48, 54, 50, 55, 50, 54, 102, 55, 55, 54, 101, 50, 48, 54, 51, 54, 102, 55, 55], totalSupply=30, scheduleId=1.1.1, scheduledTransactionId=3.3.3@1554158542.000000000, serials=[1, 2, 3], nodeId=1, duplicates=[], children=[]}, transactionHash=68656c6c6f, consensusTimestamp=2019-04-01T22:42:22Z, transactionId=3.3.3@1554158542.000000000, transactionMemo=memo, transactionFee=3000 tℏ, contractFunctionResult=ContractFunctionResult{contractId=1.2.3, evmAddress=1.2.98329e006610472e6b372c080833f6d79ed833cf, errorMessage=null, bloom=, gasUsed=0, logs=[], createdContractIds=[], stateChanges=[], gas=0, hbarAmount=0 tℏ, contractFunctionparametersBytes=, rawResult=00000000000000000000000000000000000000000000000000000000ffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000011223344556677889900aabbccddeeff00112233ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001448656c6c6f2c20776f726c642c20616761696e21000000000000000000000000, senderAccountId=1.2.3, contractNonces=[], signerNonce=0}, transfers=[Transfer{accountId=4.4.4, amount=5 ℏ}], tokenTransfers={6.6.6={1.1.1=4}}, tokenNftTransfers={4.4.4=[TokenNftTransfer{tokenId=4.4.4, sender=1.2.3, receiver=3.2.1, serial=4, isApproved=true}]}, scheduleRef=3.3.3, assessedCustomFees=[AssessedCustomFee{amount=4, tokenId=4.5.6, feeCollectorAccountId=8.6.5, payerAccountIdList=[3.3.3]}], automaticTokenAssociations=[TokenAssociation{tokenId=5.4.3, accountId=8.7.6}], aliasKey=3036301006072a8648ce3d020106052b8104000a03220002703a9370b0443be6ae7c507b0aec81a55e94e4a863b9655360bd65358caa6588, children=[], duplicates=[], parentConsensusTimestamp=2019-04-01T22:42:22Z, ethereumHash=536f6d652068617368, paidStakingRewards=[Transfer{accountId=1.2.3, amount=8 ℏ}], prngBytes=766572792072616e646f6d206279746573, prngNumber=null, evmAddress=30783030, pendingAirdropRecords=[PendingAirdropRecord{pendingAirdropId=PendingAirdropId{sender=0.0.123, receiver=0.0.124, tokenId=null, nftId=0.0.5005/1234}, pendingAirdropAmount=123}, PendingAirdropRecord{pendingAirdropId=PendingAirdropId{sender=0.0.123, receiver=0.0.124, tokenId=0.0.12345, nftId=null}, pendingAirdropAmount=123}]}" -] + "TransactionRecord{receipt=TransactionReceipt{transactionId=null, status=SCHEDULE_ALREADY_DELETED, exchangeRate=ExchangeRate{hbars=3, cents=4, expirationTime=2019-04-01T22:42:22Z, exchangeRateInCents=1.3333333333333333}, nextExchangeRate=ExchangeRate{hbars=3, cents=4, expirationTime=2019-04-01T22:42:22Z, exchangeRateInCents=1.3333333333333333}, accountId=1.2.3, fileId=4.5.6, contractId=3.2.1, topicId=9.8.7, tokenId=6.5.4, topicSequenceNumber=3, topicRunningHash=[54, 56, 54, 102, 55, 55, 50, 48, 54, 101, 54, 102, 55, 55, 50, 48, 54, 50, 55, 50, 54, 102, 55, 55, 54, 101, 50, 48, 54, 51, 54, 102, 55, 55], totalSupply=30, scheduleId=1.1.1, scheduledTransactionId=3.3.3@1554158542.000000000, serials=[1, 2, 3], nodeId=1, duplicates=[], children=[]}, transactionHash=68656c6c6f, consensusTimestamp=2019-04-01T22:42:22Z, transactionId=3.3.3@1554158542.000000000, transactionMemo=memo, transactionFee=3000 tℏ, contractFunctionResult=ContractFunctionResult{contractId=1.2.3, evmAddress=1.2.98329e006610472e6b372c080833f6d79ed833cf, errorMessage=null, bloom=, gasUsed=0, logs=[], createdContractIds=[], stateChanges=[], gas=0, hbarAmount=0 tℏ, contractFunctionparametersBytes=, rawResult=00000000000000000000000000000000000000000000000000000000ffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000011223344556677889900aabbccddeeff00112233ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001448656c6c6f2c20776f726c642c20616761696e21000000000000000000000000, senderAccountId=1.2.3, contractNonces=[], signerNonce=0}, transfers=[Transfer{accountId=4.4.4, amount=5 ℏ}], tokenTransfers={6.6.6={1.1.1=4}}, tokenNftTransfers={4.4.4=[TokenNftTransfer{tokenId=4.4.4, sender=1.2.3, receiver=3.2.1, serial=4, isApproved=true}]}, scheduleRef=3.3.3, assessedCustomFees=[AssessedCustomFee{amount=4, tokenId=4.5.6, feeCollectorAccountId=8.6.5, payerAccountIdList=[3.3.3]}], automaticTokenAssociations=[TokenAssociation{tokenId=5.4.3, accountId=8.7.6}], aliasKey=3036301006072a8648ce3d020106052b8104000a03220002703a9370b0443be6ae7c507b0aec81a55e94e4a863b9655360bd65358caa6588, children=[], duplicates=[], parentConsensusTimestamp=2019-04-01T22:42:22Z, ethereumHash=536f6d652068617368, paidStakingRewards=[Transfer{accountId=1.2.3, amount=8 ℏ}], prngBytes=766572792072616e646f6d206279746573, prngNumber=null, evmAddress=30783030, pendingAirdropRecords=[PendingAirdropRecord{pendingAirdropId=PendingAirdropId{sender=0.0.123, receiver=0.0.124, tokenId=null, nftId=0.0.5005/1234}, pendingAirdropAmount=123}, PendingAirdropRecord{pendingAirdropId=PendingAirdropId{sender=0.0.123, receiver=0.0.124, tokenId=0.0.12345, nftId=null}, pendingAirdropAmount=123}], highVolumePricingMultiplier=1000}" +] \ No newline at end of file diff --git a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionTest.java b/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionTest.java index 65947937f7..debb3d1047 100644 --- a/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionTest.java +++ b/sdk/src/test/java/com/hedera/hashgraph/sdk/TransactionTest.java @@ -681,4 +681,66 @@ void testGetSignableNodeBodyBytesListFileAppendMultipleChunks() throws InvalidPr } } } + + @Test + void highVolumeDefaultsToFalse() { + var transaction = new AccountCreateTransaction(); + + assertThat(transaction.getHighVolume()).isFalse(); + } + + @Test + void highVolumeCanBeSerialized() throws InvalidProtocolBufferException { + var transaction = new AccountCreateTransaction() + .setKey(PrivateKey.generateED25519()) + .setHighVolume(true); + + var transactionFromBytes = Transaction.fromBytes(transaction.toBytes()); + + assertThat(transactionFromBytes).isInstanceOf(AccountCreateTransaction.class); + assertThat(((AccountCreateTransaction) transactionFromBytes).getHighVolume()) + .isTrue(); + } + + @Test + void highVolumeCannotChangeAfterFreeze() { + var transaction = new AccountCreateTransaction() + .setKey(PrivateKey.generateED25519()) + .setTransactionId(testTransactionID) + .setNodeAccountIds(testNodeAccountIds) + .freeze(); + + assertThrows(IllegalStateException.class, () -> transaction.setHighVolume(true)); + } + + @Test + void highVolumeIsIncludedInProtobufOutput() throws InvalidProtocolBufferException { + var transaction = new AccountCreateTransaction() + .setKey(PrivateKey.generateED25519()) + .setTransactionId(testTransactionID) + .setNodeAccountIds(testNodeAccountIds) + .setHighVolume(true) + .freeze(); + + List signableBodies = transaction.getSignableNodeBodyBytesList(); + assertThat(signableBodies).isNotEmpty(); + + // Parse the first body and verify high_volume is set + TransactionBody body = TransactionBody.parseFrom(signableBodies.get(0).getBody()); + assertThat(body.getHighVolume()).isTrue(); + + // Test with highVolume set to false + var transactionFalse = new AccountCreateTransaction() + .setKey(PrivateKey.generateED25519()) + .setTransactionId(testTransactionID) + .setNodeAccountIds(testNodeAccountIds) + .setHighVolume(false) + .freeze(); + + List signableBodiesFalse = + transactionFalse.getSignableNodeBodyBytesList(); + TransactionBody bodyFalse = + TransactionBody.parseFrom(signableBodiesFalse.get(0).getBody()); + assertThat(bodyFalse.getHighVolume()).isFalse(); + } } diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountCreateIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountCreateIntegrationTest.java index 724ddd7ee9..788b841f28 100644 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountCreateIntegrationTest.java +++ b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/AccountCreateIntegrationTest.java @@ -548,6 +548,59 @@ void createAccountUsingSetKeyWithAliasWithED25519KeyAndPublicECDSAKeyShouldHaveE } } + @Test + @DisplayName("Can create account with high-volume throttles enabled") + void canCreateAccountWithHighVolume() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKeyWithoutAlias(key) + .setInitialBalance(new Hbar(1)) + .setHighVolume(true) + .setMaxTransactionFee(Hbar.from(10)) + .execute(testEnv.client); + + var receipt = response.getReceipt(testEnv.client); + var accountId = Objects.requireNonNull(receipt.accountId); + + var record = response.getRecord(testEnv.client); + + assertThat(record.highVolumePricingMultiplier).isGreaterThanOrEqualTo(1000); + + var info = new AccountInfoQuery().setAccountId(accountId).execute(testEnv.client); + + assertThat(info.accountId).isEqualTo(accountId); + assertThat(info.isDeleted).isFalse(); + assertThat(info.key.toString()).isEqualTo(key.getPublicKey().toString()); + assertThat(info.balance).isEqualTo(new Hbar(1)); + } + } + + @Test + @DisplayName("Can create account with high-volume throttles and valid max transaction fee") + void canCreateAccountWithHighVolumeAndValidMaxFee() throws Exception { + try (var testEnv = new IntegrationTestEnv(1)) { + var key = PrivateKey.generateED25519(); + + var response = new AccountCreateTransaction() + .setKeyWithoutAlias(key) + .setInitialBalance(new Hbar(1)) + .setHighVolume(true) + .setMaxTransactionFee(Hbar.from(5)) + .execute(testEnv.client); + + var receipt = response.getReceipt(testEnv.client); + var accountId = Objects.requireNonNull(receipt.accountId); + + assertThat(receipt.status).isEqualTo(Status.SUCCESS); + assertThat(accountId).isNotNull(); + + var info = new AccountInfoQuery().setAccountId(accountId).execute(testEnv.client); + assertThat(info.accountId).isEqualTo(accountId); + } + } + private boolean isLongZeroAddress(byte[] address) { for (int i = 0; i < 12; i++) { if (address[i] != 0) { diff --git a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/HookStoreTransactionIntegrationTest.java b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/HookStoreTransactionIntegrationTest.java index 9ebd3f3294..f7f079c84c 100644 --- a/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/HookStoreTransactionIntegrationTest.java +++ b/sdk/src/testIntegration/java/com/hedera/hashgraph/sdk/test/integration/HookStoreTransactionIntegrationTest.java @@ -209,7 +209,7 @@ void lambdaSStoreTooManyStorageUpdatesFails() throws Exception { // Expect TOO_MANY_LAMBDA_STORAGE_UPDATES assertThatExceptionOfType(ReceiptStatusException.class) .isThrownBy(() -> tx.execute(testEnv.client).getReceipt(testEnv.client)) - .withMessageContaining(Status.TOO_MANY_LAMBDA_STORAGE_UPDATES.toString()); + .withMessageContaining(Status.TOO_MANY_EVM_HOOK_STORAGE_UPDATES.toString()); } }