Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 58 additions & 2 deletions .github/workflows/flow-rust-ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ jobs:
uses: hiero-ledger/hiero-solo-action@6a1a77601cf3e69661fb6880530a4edf656b40d5 # v0.14.0
with:
installMirrorNode: true
hieroVersion: v0.65.0

- name: Create env file
run: |
Expand All @@ -129,4 +128,61 @@ jobs:
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
. $HOME/.cargo/env
cargo test --workspace
cargo test --workspace -- --skip node::update
dab-tests:
needs: ['check']
runs-on: hiero-client-sdk-linux-medium
steps:
- name: Harden Runner
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
with:
egress-policy: audit

- name: Checkout Code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
submodules: 'recursive'

- name: Setup Rust
uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # v1
with:
toolchain: 1.88.0

- name: Setup GCC and OpenSSL
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends gcc libc6-dev libc-dev libssl-dev pkg-config openssl

- name: Install Protoc
uses: step-security/setup-protoc@f6eb248a6510dbb851209febc1bd7981604a52e3 # v3.0.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}

- name: Setup NodeJS
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version: ${{ env.NODE_VERSION }}

- name: Prepare Hiero Solo for DAB Tests
id: solo-dab
uses: hiero-ledger/hiero-solo-action@9471711c98a56179def6123e1040ab6c2e668881 # branch: 75-add-support-for-multiple-consensus-nodes
with:
hieroVersion: v0.68.0-rc.1
installMirrorNode: true
mirrorNodeVersion: v0.142.0
dualMode: true

- name: Create env file for DAB Tests
run: |
touch .env
echo TEST_OPERATOR_KEY="${{ steps.solo-dab.outputs.privateKey }}" >> .env
echo TEST_OPERATOR_ID="${{ steps.solo-dab.outputs.accountId }}" >> .env
echo TEST_NETWORK_NAME="localhost" >> .env
echo TEST_RUN_NONFREE="1" >> .env
cat .env

- name: Run DAB Tests
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
. $HOME/.cargo/env
cargo test --test e2e node::update -- --test-threads=1
51 changes: 41 additions & 10 deletions protobufs/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,37 @@
const DERIVE_EQ_HASH: &str = "#[derive(Eq, Hash)]";
const SERVICES_FOLDER: &str = "./services/hapi/hedera-protobuf-java-api/src/main/proto/services";

// Recursively find all .proto files, excluding state/ and auxiliary/ subdirectories
fn find_proto_files(dir: &Path) -> anyhow::Result<Vec<std::path::PathBuf>> {

Check warning on line 18 in protobufs/build.rs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

protobufs/build.rs#L18

Method find_proto_files has a cyclomatic complexity of 11 (limit is 8)
let mut files = Vec::new();
for entry in read_dir(dir)? {
let entry = entry?;
let path = entry.path();

// Skip state/ directory (internal node state, not for SDK)
// Include auxiliary/tss/ but skip other auxiliary/ subdirectories
if path.is_dir() {
let dir_name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");

if dir_name == "state" {
continue; // Skip state directory entirely
} else if dir_name == "auxiliary" {
// Only include auxiliary/tss files
let tss_path = path.join("tss");
if tss_path.is_dir() {
files.extend(find_proto_files(&tss_path)?);
}
continue;
}

files.extend(find_proto_files(&path)?);
} else if path.extension().and_then(|s| s.to_str()) == Some("proto") {
files.push(path);
}
}
Ok(files)
}

fn main() -> anyhow::Result<()> {
// services is the "base" module for the hedera protobufs
// in the beginning, there was only services and it was named "protos"
Expand Down Expand Up @@ -46,14 +77,7 @@
)?;
fs::rename(out_path.join("services"), &services_tmp_path)?;

let services: Vec<_> = read_dir(&services_tmp_path)?
.chain(read_dir(&services_tmp_path.join("auxiliary").join("tss"))?)
.filter_map(|entry| {
let entry = entry.ok()?;

entry.file_type().ok()?.is_file().then(|| entry.path())
})
.collect();
let services = find_proto_files(&services_tmp_path)?;

// iterate through each file
let re_package = RegexBuilder::new(r"^package (.*);$").multi_line(true).build()?;
Expand All @@ -67,6 +91,7 @@
let contents = contents.replace("com.hedera.hapi.services.auxiliary.history.", "");
let contents = contents.replace("com.hedera.hapi.services.auxiliary.tss.", "");
let contents = contents.replace("com.hedera.hapi.platform.event.", "");
let contents = contents.replace("com.hedera.hapi.node.hooks.", "");

let contents = remove_unused_types(&contents);

Expand All @@ -93,7 +118,6 @@
.type_attribute("proto.ContractID.contract", DERIVE_EQ_HASH)
.type_attribute("proto.TransactionID", DERIVE_EQ_HASH)
.type_attribute("proto.Timestamp", DERIVE_EQ_HASH)
.type_attribute("proto.NftTransfer", DERIVE_EQ_HASH)
.type_attribute("proto.Fraction", DERIVE_EQ_HASH)
.type_attribute("proto.TopicID", DERIVE_EQ_HASH)
.type_attribute("proto.TokenID", DERIVE_EQ_HASH)
Expand All @@ -112,7 +136,14 @@
.type_attribute("proto.TokenAllowance", DERIVE_EQ_HASH)
.type_attribute("proto.GrantedCryptoAllowance", DERIVE_EQ_HASH)
.type_attribute("proto.GrantedTokenAllowance", DERIVE_EQ_HASH)
.type_attribute("proto.Duration", DERIVE_EQ_HASH);
.type_attribute("proto.Duration", DERIVE_EQ_HASH)
.type_attribute("proto.HookCall", DERIVE_EQ_HASH)
.type_attribute("proto.HookCall.call_spec", DERIVE_EQ_HASH)
.type_attribute("proto.HookCall.id", DERIVE_EQ_HASH)
.type_attribute("proto.HookId", DERIVE_EQ_HASH)
.type_attribute("proto.HookEntityId", DERIVE_EQ_HASH)
.type_attribute("proto.HookEntityId.entity_id", DERIVE_EQ_HASH)
.type_attribute("proto.EvmHookCall", DERIVE_EQ_HASH);

// the ResponseCodeEnum should be marked as #[non_exhaustive] so
// adding variants does not trigger a breaking change
Expand Down
2 changes: 1 addition & 1 deletion protobufs/services
Submodule services updated 2901 files
52 changes: 52 additions & 0 deletions src/account/account_create_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ use hedera_proto::services::crypto_service_client::CryptoServiceClient;
use time::Duration;
use tonic::transport::Channel;

use crate::hooks::{
HookCreationDetails,
LambdaEvmHook,
};
use crate::ledger_id::RefLedgerId;
use crate::protobuf::{
FromProtobuf,
Expand Down Expand Up @@ -76,6 +80,9 @@ pub struct AccountCreateTransactionData {

/// If true, the account declines receiving a staking reward. The default value is false.
decline_staking_reward: bool,

/// Hooks to add immediately after creating this account.
hooks: Vec<HookCreationDetails>,
}

impl Default for AccountCreateTransactionData {
Expand All @@ -91,6 +98,7 @@ impl Default for AccountCreateTransactionData {
alias: None,
staked_id: None,
decline_staking_reward: false,
hooks: Vec::new(),
}
}
}
Expand Down Expand Up @@ -293,6 +301,29 @@ impl AccountCreateTransaction {
self.data_mut().decline_staking_reward = decline;
self
}

pub fn add_hook(&mut self, hook: HookCreationDetails) -> &mut Self {
self.data_mut().hooks.push(hook);
self
}

pub fn add_lambda_evm_hook(&mut self, hook: LambdaEvmHook) -> &mut Self {
// Helper to add a Lambda EVM hook with default extension point and hook ID
use crate::hooks::HookExtensionPoint;
let details =
HookCreationDetails::new(HookExtensionPoint::AccountAllowanceHook, 1, Some(hook));
self.data_mut().hooks.push(details);
self
}

pub fn set_hooks(&mut self, hooks: Vec<HookCreationDetails>) -> &mut Self {
self.data_mut().hooks = hooks;
self
}

pub fn get_hooks(&self) -> &[HookCreationDetails] {
&self.data().hooks
}
}

impl TransactionData for AccountCreateTransactionData {}
Expand Down Expand Up @@ -353,6 +384,11 @@ impl FromProtobuf<services::CryptoCreateTransactionBody> for AccountCreateTransa
alias,
staked_id: Option::from_protobuf(pb.staked_id)?,
decline_staking_reward: pb.decline_reward,
hooks: pb
.hook_creation_details
.into_iter()
.map(HookCreationDetails::from_protobuf)
.collect::<Result<Vec<_>, _>>()?,
})
}
}
Expand Down Expand Up @@ -391,6 +427,7 @@ impl ToProtobuf for AccountCreateTransactionData {
alias: self.alias.map_or(vec![], |it| it.to_bytes().to_vec()),
decline_reward: self.decline_staking_reward,
staked_id,
hook_creation_details: self.hooks.iter().map(|h| h.to_protobuf()).collect(),
}
}
}
Expand All @@ -417,8 +454,13 @@ mod tests {
AccountCreateTransaction,
AccountId,
AnyTransaction,
ContractId,
EvmAddress,
EvmHookSpec,
Hbar,
HookCreationDetails,
HookExtensionPoint,
LambdaEvmHook,
PublicKey,
};

Expand Down Expand Up @@ -560,6 +602,7 @@ mod tests {
20,
23,
],
hook_creation_details: [],
staked_id: Some(
StakedAccountId(
AccountId {
Expand Down Expand Up @@ -683,6 +726,7 @@ mod tests {
20,
23,
],
hook_creation_details: [],
staked_id: Some(
StakedNodeId(
4,
Expand Down Expand Up @@ -710,6 +754,13 @@ mod tests {
#[test]
fn from_proto_body() {
#[allow(deprecated)]
let contract_id = ContractId::new(0, 0, 1);
let hooks = vec![HookCreationDetails::new(
HookExtensionPoint::AccountAllowanceHook,
0,
Some(LambdaEvmHook::new(EvmHookSpec::new(Some(contract_id)), vec![])),
)];

let tx = services::CryptoCreateTransactionBody {
key: Some(key().to_protobuf()),
initial_balance: INITIAL_BALANCE.to_tinybars() as u64,
Expand All @@ -728,6 +779,7 @@ mod tests {
staked_id: Some(services::crypto_create_transaction_body::StakedId::StakedAccountId(
STAKED_ACCOUNT_ID.to_protobuf(),
)),
hook_creation_details: hooks.iter().map(|h| h.to_protobuf()).collect(),
};

let tx = AccountCreateTransactionData::from_protobuf(tx).unwrap();
Expand Down
Loading
Loading