Skip to content
Merged
6 changes: 4 additions & 2 deletions packages/proto/Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ tasks:

install:submodules:
vars:
commit: "7ca2f4e457a68fbc092edd17552bc5db7e032f6d" # v0.65.0
commit: "18cc64cd7d93fb99be73920d65966bdcbb69b8a9" # hiero-hooks commit on main
cmds:
- git submodule update --init --recursive --remote
- |
Expand All @@ -40,7 +40,9 @@ tasks:
- rm -rf src/proto/streams
- mv src/services/hapi/hedera-protobuf-java-api/src/main/proto/services src/proto/
- mv src/services/hapi/hedera-protobuf-java-api/src/main/proto/streams src/proto/
- mv src/services/hapi/hedera-protobuf-java-api/src/main/proto/platform/event/* src/proto/services/
- mv src/services/hapi/hedera-protobuf-java-api/src/main/proto/platform/event/* src/proto/
- chmod +x scripts/remove-duplicate-protobuf.sh
- ./scripts/remove-duplicate-protobuf.sh
- echo "Protobufs moved successfully!"
- task "delete:submodule"

Expand Down
56 changes: 28 additions & 28 deletions packages/proto/scripts/flatten-protos.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,6 @@ echo "Flattening proto directory structure..."
# Create temporary directory
mkdir -p "$TEMP_DIR"

# Check if files are already flattened (no subdirectories exist)
if [ $(find "$PROTO_DIR" -mindepth 2 -name "*.proto" -type f | wc -l) -eq 0 ]; then
echo " - Files are already flattened, skipping flattening process"
exit 0
fi

echo " - Collecting all proto files..."

# First, copy all files from subdirectories (mindepth 2) with prefixed names
Expand Down Expand Up @@ -71,30 +65,36 @@ update_imports_in_file() {
# Extract the filename from the import statement
import_file=$(echo "$import_line" | sed 's/import "\([^"]*\)";/\1/')

# Skip google imports and other external imports
if [[ "$import_file" == google/* ]] || [[ "$import_file" == */* ]]; then
continue
fi

# Find the new prefixed filename
new_filename=""
for proto_file in "$PROTO_DIR"/*.proto; do
if [[ -f "$proto_file" ]]; then
basename_file=$(basename "$proto_file")
if [[ "$basename_file" == *"_$import_file" ]] || [[ "$basename_file" == "$import_file" ]]; then
new_filename="$basename_file"
break
fi
fi
done

# Update the import if we found a new filename
if [[ -n "$new_filename" ]] && [[ "$new_filename" != "$import_file" ]]; then
sed -i.tmp "s|import \"$import_file\";|import \"$new_filename\";|g" "$temp_file"
rm -f "$temp_file.tmp"
echo " Updated import: $import_file -> $new_filename"
# Skip only google imports
if [[ "$import_file" == google/* ]]; then
continue
fi

# Candidate flattened name for subdir imports
flat_candidate="${import_file//\//_}"

new_filename=""
# Prefer exact flattened candidate if present
if [[ -f "$PROTO_DIR/$flat_candidate" ]]; then
new_filename="$flat_candidate"
else
# Fallback: search for a file that ends with _basename or exact basename
basename_file=$(basename "$import_file")
for proto_file in "$PROTO_DIR"/*.proto; do
f="$(basename "$proto_file")"
if [[ "$f" == *"_$basename_file" || "$f" == "$basename_file" ]]; then
new_filename="$f"
break
fi
done
fi

if [[ -n "$new_filename" && "$new_filename" != "$import_file" ]]; then
sed -i.tmp "s|import \"$import_file\";|import \"$new_filename\";|g" "$temp_file"
rm -f "$temp_file.tmp"
echo " Updated import: $import_file -> $new_filename"
fi
done

# Replace the original file with the updated one
mv "$temp_file" "$file"
Expand Down
107 changes: 107 additions & 0 deletions packages/proto/scripts/remove-duplicate-protobuf.sh.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/usr/bin/env bash
set -euo pipefail

# =============================================================================
# PROTO DUPLICATE REMOVAL SCRIPT
# =============================================================================
#
# PROBLEM: This script fixes duplicate proto files that cause compilation errors
# like "duplicate name 'EventCore' in Namespace .com.hedera.hapi.platform.event"
#
# ROOT CAUSE: The proto flattening process in Taskfile.yml creates multiple
# copies of the same files with different prefixes:
#
# 1. Original source: platform/event/event_core.proto
# 2. After move: services/event_core.proto (Taskfile.yml line 43 moves platform/event/* to services/)
# 3. After flatten:
# - services_event_core.proto (from services/event_core.proto)
# - platform_event_event_core.proto (from platform/event/event_core.proto)
# - event_core.proto (from root level, if it exists)
#
# The flattening script (flatten-protos.sh) replaces path separators with underscores:
# - services/event/event_descriptor.proto → services_event_event_descriptor.proto
# - platform/event/event_core.proto → platform_event_event_core.proto
#
# This creates files with duplicate "event_" segments and multiple copies of the
# same message definitions in the same namespace, causing protobuf compilation errors.
#
# SOLUTION: Remove duplicate files, keeping only the services_* versions since
# they represent the canonical location after the move operation.
# =============================================================================


SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Go up one level to get the proto package root
PROTO_PACKAGE_ROOT="$(dirname "$SCRIPT_DIR")"
# Set the proto files directory
ROOT="$PROTO_PACKAGE_ROOT/src/proto"

echo "Removing duplicate proto files from: $ROOT"

# Remove auxiliary_* files if a services_* counterpart exists
shopt -s nullglob
for aux in "$ROOT"/auxiliary_*.proto; do
base="$(basename "$aux")"
tail="${base#auxiliary_}"
svc="$ROOT/services_$tail"

if [[ -f "$svc" ]]; then
echo "Removing duplicate: $aux (services version exists: $(basename "$svc"))"
rm -f "$aux"
fi
done

# Remove platform_event_event_* files if services_event_* counterpart exists
# These are created when platform/event/* files get flattened after being moved to services/
for platform in "$ROOT"/platform_event_event_*.proto; do
base="$(basename "$platform")"
tail="${base#platform_event_event_}"
svc="$ROOT/services_event_$tail"

if [[ -f "$svc" ]]; then
echo "Removing duplicate: $platform (services version exists: $(basename "$svc"))"
rm -f "$platform"
fi
done

# Remove root-level event_* files if services_event_* counterpart exists
# These are leftover files that weren't properly moved during the flattening process
for event in "$ROOT"/event_*.proto; do
base="$(basename "$event")"
svc="$ROOT/services_$base"

if [[ -f "$svc" ]]; then
echo "Removing duplicate: $event (services version exists: $(basename "$svc"))"
rm -f "$event"
fi
done

# Remove root-level gossip_event.proto if services_gossip_event.proto exists
# This handles the specific case where gossip_event.proto exists at root level
if [[ -f "$ROOT/gossip_event.proto" && -f "$ROOT/services_gossip_event.proto" ]]; then
echo "Removing duplicate: gossip_event.proto (services version exists: services_gossip_event.proto)"
rm -f "$ROOT/gossip_event.proto"
fi

# Remove any duplicate state signature transaction files
# This handles case sensitivity issues in CI where different case variations might exist
find "$ROOT" -name "*state*signature*transaction*.proto" -type f | while read -r file; do
if [[ "$file" != "$ROOT/services_state_signature_transaction.proto" ]]; then
echo "Removing potential duplicate: $file (keeping services_state_signature_transaction.proto)"
rm -f "$file"
fi
done

shopt -u nullglob

# Show remaining definitions for sanity
echo
echo "Remaining definitions of EventCore:"
grep -rn "message[[:space:]]\+EventCore\b" "$ROOT" || true

echo
echo "Remaining definitions of StateSignatureTransaction:"
grep -rn "message[[:space:]]\+StateSignatureTransaction\b" "$ROOT" || true

echo
echo "Done."
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ option java_package = "com.hederahashgraph.service.proto.java";
// <<<pbj.java_package = "com.hedera.hapi.node.addressbook">>> This comment is special code for setting PBJ Compiler java package

import "services_transaction_response.proto";
import "services_ethereum_transaction.proto";
import "services_transaction.proto";

/**
* The Address Book service provides the ability for Hedera network node
Expand Down
158 changes: 158 additions & 0 deletions packages/proto/src/proto/services_basic_types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,87 @@ message TransactionID {
int32 nonce = 4;
}

/**
* Once a hook is created, its full id.
* <p>
* A composite of its creating entity's id and an arbitrary 64-bit hook id
* (which need not be sequential).
*/
message HookId {
/**
* The hook's creating entity id.
*/
HookEntityId entity_id = 1;

/**
* An arbitrary 64-bit identifier.
*/
int64 hook_id = 2;
}

/**
* The id of an entity using a hook.
*/
message HookEntityId {
oneof entity_id {
/**
* An account using a hook.
*/
AccountID account_id = 1;
/**
* A contract using a hook.
*/
ContractID contract_id = 2;
}
}

/**
* Specifies a call to a hook from within a transaction.
* <p>
* Often the hook's entity is implied by the nature of the call site. For example, when using an account allowance hook
* inside a crypto transfer, the hook's entity is necessarily the account whose authorization is required.
* <p>
* For future extension points where the hook owner is not forced by the context, we include the option to fully
* specify the hook id for the call.
*/
message HookCall {
oneof id {
/**
* The full id of the hook to call, when the owning entity is not forced by the call site.
*/
HookId full_hook_id = 1;
/**
* The numeric id of the hook to call, when the owning entity is forced by the call site.
*/
int64 hook_id = 2;
}

/**
* Specifies details of the call.
*/
oneof call_spec {
/**
* Specification of how to call an EVM hook.
*/
EvmHookCall evm_hook_call = 3;
}
}

/**
* Specifies details of a call to an EVM hook.
*/
message EvmHookCall {
/**
* Call data to pass to the hook via the IHieroHook.HookContext#data field.
*/
bytes data = 1;

/**
* The gas limit to use.
*/
uint64 gas_limit = 2;
}

/**
* An account, and the amount that it sends or receives during a token transfer.
*
Expand Down Expand Up @@ -467,6 +548,28 @@ message AccountAmount {
* The default value SHALL be false (unset).
*/
bool is_approval = 3;

/**
* If set, a call to a hook of type `ACCOUNT_ALLOWANCE_HOOK` on scoped
* account; the hook's invoked methods must not revert and must return
* true for the containing CryptoTransfer to succeed.
* <p>
* Cannot be set if `is_approval` is true.
*/
oneof hook_call {
/**
* A single call made before attempting the CryptoTransfer, to a
* method with logical signature allow(HookContext, ProposedTransfers)
*/
HookCall pre_tx_allowance_hook = 4;
/**
* Two calls, the first call before attempting the CryptoTransfer, to a
* method with logical signature allowPre(HookContext, ProposedTransfers);
* and the second call after attempting the CryptoTransfer, to a method
* with logical signature allowPost(HookContext, ProposedTransfers).
*/
HookCall pre_post_tx_allowance_hook = 5;
}
}

/**
Expand Down Expand Up @@ -525,6 +628,51 @@ message NftTransfer {
* The default value SHALL be false (unset).
*/
bool is_approval = 4;

/**
* If set, a call to a hook of type `ACCOUNT_ALLOWANCE_HOOK` installed on
* senderAccountID that must succeed for the transaction to occur.
* <p>
* Cannot be set if `is_approval` is true.
*/
oneof sender_allowance_hook_call {
/**
* A single call made before attempting the CryptoTransfer, to a
* method with logical signature allow(HookContext, ProposedTransfers)
*/
HookCall pre_tx_sender_allowance_hook = 5;
/**
* Two calls, the first call before attempting the CryptoTransfer, to a
* method with logical signature allowPre(HookContext, ProposedTransfers);
* and the second call after attempting the CryptoTransfer, to a method
* with logical signature allowPost(HookContext, ProposedTransfers).
*/
HookCall pre_post_tx_sender_allowance_hook = 6;
}

/**
* If set, a call to a hook of type `ACCOUNT_ALLOWANCE_HOOK` installed on
* receiverAccountID that must succeed for the transaction to occur.
* <p>
* May be set even if `is_approval` is true. In this case, the approval applies
* to the sender authorization, and the hook applies to the receiver authorization
* (if needed, e.g. because of a fallback royalty fee or receiver signature
* requirement).
*/
oneof receiver_allowance_hook_call {
/**
* A single call made before attempting the CryptoTransfer, to a
* method with logical signature allow(HookContext, ProposedTransfers)
*/
HookCall pre_tx_receiver_allowance_hook = 7;
/**
* Two calls, the first call before attempting the CryptoTransfer, to a
* method with logical signature allowPre(HookContext, ProposedTransfers);
* and the second call after attempting the CryptoTransfer, to a method
* with logical signature allowPost(HookContext, ProposedTransfers).
*/
HookCall pre_post_tx_receiver_allowance_hook = 8;
}
}

/**
Expand Down Expand Up @@ -1689,6 +1837,16 @@ enum HederaFunctionality {
* Submit a batch of transactions to run atomically
*/
AtomicBatch = 108;

/**
* Update one or more storage slots in an lambda EVM hook.
*/
LambdaSStore = 109;

/**
* (Internal-only) Dispatch a hook action.
*/
HookDispatch = 110;
}

/**
Expand Down
Loading
Loading