From ba0436e2834f9f657b002a4865c3796f05b5c06b Mon Sep 17 00:00:00 2001 From: atiwari-circle Date: Tue, 7 Apr 2026 13:36:45 +0000 Subject: [PATCH 1/2] feat: arcup curl error handling, EVM blocklist load fix, ProcessSyncedValue error reply, quake storage config - arcup: redirect info/warn/error to stderr, fail on HTTP errors with curl -f, surface update_arcup curl errors - arcup install: respect ARC_DIR in env file, fail on HTTP errors - evm: remove can_load_account indirection, propagate load_account errors directly - consensus: send reply before propagating error in ProcessSyncedValue (CCHAIN-1498), add error path tests - quake: add legacy_state_root and storage.v2 manifest fields - docs: improve installation cargo install instructions Co-Authored-By: Claude Opus 4.6 (1M context) --- arcup/arcup | 19 ++-- arcup/install | 14 +-- crates/evm/src/handler.rs | 10 +-- .../src/handlers/process_synced_value.rs | 87 ++++++++++++++++++- crates/quake/src/manifest.rs | 12 +++ crates/quake/src/manifest/generate.rs | 2 + docs/installation.md | 8 +- 7 files changed, 125 insertions(+), 27 deletions(-) diff --git a/arcup/arcup b/arcup/arcup index e1710aa..bbe3e83 100755 --- a/arcup/arcup +++ b/arcup/arcup @@ -26,15 +26,15 @@ YELLOW='\033[1;33m' NC='\033[0m' # No Color info() { - echo -e "${GREEN}info${NC}: $1" + echo -e "${GREEN}info${NC}: $1" >&2 } warn() { - echo -e "${YELLOW}warn${NC}: $1" + echo -e "${YELLOW}warn${NC}: $1" >&2 } error() { - echo -e "${RED}error${NC}: $1" + echo -e "${RED}error${NC}: $1" >&2 exit 1 } @@ -130,7 +130,7 @@ update_arcup() { trap "rm -f $tmp_file" EXIT info "Downloading latest arcup..." - if ! curl -fsSL "$ARCUP_BIN_URL" -o "$tmp_file" 2>/dev/null; then + if ! curl -fsSL "$ARCUP_BIN_URL" -o "$tmp_file"; then error "Failed to download arcup update" fi @@ -271,12 +271,16 @@ get_latest_version() { fi local api_url="https://api.github.com/repos/$REPO/releases/latest" + local response + if ! response=$(curl -fsSL "$api_url" 2>&1); then + error "Failed to fetch latest version from GitHub: $response" + fi + local version - version=$(curl -sSL "$api_url" 2>/dev/null | \ - grep '"tag_name":' | head -n 1 | sed 's/.*"tag_name": *"\([^"]*\)".*/\1/') + version=$(echo "$response" | grep '"tag_name":' | head -n 1 | sed 's/.*"tag_name": *"\([^"]*\)".*/\1/') if [[ -z "$version" ]]; then - error "Failed to fetch latest version from GitHub" + error "Failed to parse version from GitHub API response" fi echo "$version" @@ -331,7 +335,6 @@ main() { fi VERSION_TAG="$VERSION" - VERSION_NUMBER="${VERSION#v}" info "Installing arc-node $VERSION_TAG" diff --git a/arcup/install b/arcup/install index eb1f398..cba29d4 100755 --- a/arcup/install +++ b/arcup/install @@ -15,11 +15,11 @@ YELLOW='\033[1;33m' NC='\033[0m' info() { - echo -e "${GREEN}info${NC}: $1" + echo -e "${GREEN}info${NC}: $1" >&2 } warn() { - echo -e "${YELLOW}warn${NC}: $1" + echo -e "${YELLOW}warn${NC}: $1" >&2 } # Install a file to BIN_DIR, using sudo if necessary @@ -53,7 +53,7 @@ main() { info "Downloading arcup script..." if command -v curl >/dev/null 2>&1; then - curl -# -L "$ARCUP_URL" -o "$tmp_file" + curl -#fL "$ARCUP_URL" -o "$tmp_file" elif command -v wget >/dev/null 2>&1; then wget --show-progress -q -O "$tmp_file" "$ARCUP_URL" else @@ -66,15 +66,15 @@ main() { info "Arcup installed to $BIN_DIR/arcup" # Create env file that can be sourced to set PATH (POSIX shells) - cat > "$ENV_FILE" <<'ENVEOF' + cat > "$ENV_FILE" < "$ENV_FILE.fish" <<'ENVEOF' + cat > "$ENV_FILE.fish" <, ERROR: EvmTrError, { - fn can_load_account(&self, evm: &mut EVM, address: Address) -> bool { - evm.ctx_mut().journal_mut().load_account(address).is_ok() - } - fn check_blocklist( &self, evm: &mut EVM, diff --git a/crates/malachite-app/src/handlers/process_synced_value.rs b/crates/malachite-app/src/handlers/process_synced_value.rs index 84714da..2edeab4 100644 --- a/crates/malachite-app/src/handlers/process_synced_value.rs +++ b/crates/malachite-app/src/handlers/process_synced_value.rs @@ -52,7 +52,7 @@ pub async fn handle( value_bytes: Bytes, reply: Reply>>, ) -> Result<(), eyre::Error> { - let proposal = on_process_synced_value( + let proposal = match on_process_synced_value( EnginePayloadValidator::new(engine, state.metrics()), state.store(), state.store(), @@ -61,7 +61,17 @@ pub async fn handle( proposer, value_bytes, ) - .await?; + .await + { + Ok(proposal) => proposal, + Err(e) => { + error!(%height, %round, %proposer, "ProcessSyncedValue failed: {e:#}"); + if let Err(send_err) = reply.send(None) { + error!("🔴 ProcessSyncedValue: Failed to send error reply: {send_err:?}"); + } + return Err(e); + } + }; // Mark this height as synced for proposal monitoring if let Some(p) = &proposal @@ -278,6 +288,79 @@ mod tests { assert!(proposal.is_none()); } + // These two tests cover error paths in `on_process_synced_value` that were + // previously untested. They do NOT directly test the CCHAIN-1498 fix in + // `handle()`, which requires `State` and `Engine` — concrete types with no + // test builder. A `State` test harness would benefit all handler tests but + // is out of scope here. + #[tokio::test] + async fn on_process_synced_value_engine_validation_error() { + let mut u = Unstructured::new(&[0u8; 512]); + + let height = Height::new(1); + let round = Round::new(0); + let proposer = Address::new([0u8; 20]); + let payload = ExecutionPayloadV3::arbitrary(&mut u).unwrap(); + let value_bytes = Bytes::from(payload.as_ssz_bytes()); + + let mut engine = MockPayloadValidator::new(); + engine + .expect_validate_payload() + .returning(|_| Err(io::Error::other("Simulated engine error").into())); + + let mut undecided = MockUndecidedBlocksRepository::new(); + undecided.expect_store().times(0); + + let mut invalid = MockInvalidPayloadsRepository::new(); + invalid.expect_append().times(0); + + let result = on_process_synced_value( + engine, + undecided, + invalid, + height, + round, + proposer, + value_bytes, + ) + .await; + + assert!(result.is_err()); + } + + #[tokio::test] + async fn on_process_synced_value_invalid_payload_store_fails() { + let height = Height::new(1); + let round = Round::new(0); + let proposer = Address::new([0u8; 20]); + let value_bytes = Bytes::from(vec![0u8; 10]); // garbage bytes trigger SSZ decode failure + + let mut engine = MockPayloadValidator::new(); + engine.expect_validate_payload().times(0); + + let mut undecided = MockUndecidedBlocksRepository::new(); + undecided.expect_store().times(0); + + let mut invalid = MockInvalidPayloadsRepository::new(); + invalid + .expect_append() + .times(1) + .returning(|_| Err(io::Error::other("Simulated invalid payload store error"))); + + let result = on_process_synced_value( + engine, + undecided, + invalid, + height, + round, + proposer, + value_bytes, + ) + .await; + + assert!(result.is_err()); + } + #[tokio::test] async fn test_on_process_synced_value_store_error() { let mut u = Unstructured::new(&[0u8; 512]); diff --git a/crates/quake/src/manifest.rs b/crates/quake/src/manifest.rs index 3ff66c1..cbc8286 100644 --- a/crates/quake/src/manifest.rs +++ b/crates/quake/src/manifest.rs @@ -157,6 +157,16 @@ pub struct ElEngineConfig { pub cross_block_cache_size: Option, pub persistence_threshold: u64, pub memory_block_buffer_target: u64, + pub legacy_state_root: Option, +} + +/// Execution layer (Reth) storage configuration overrides. +/// +/// Fields mirror reth's storage CLI flags and correspond to `--storage.*` flags. +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Default)] +#[serde(deny_unknown_fields, default)] +pub struct ElStorageConfig { + pub v2: Option, } /// Execution layer (Reth) gas price oracle configuration overrides. @@ -383,6 +393,7 @@ pub struct ElConfigOverride { pub tx_propagation_policy: Option, pub arc: ElArcConfig, pub prune: ElPruneConfig, + pub storage: ElStorageConfig, } impl Default for ElConfigOverride { @@ -403,6 +414,7 @@ impl Default for ElConfigOverride { tx_propagation_policy: Option::default(), arc: ElArcConfig::default(), prune: ElPruneConfig::default(), + storage: ElStorageConfig::default(), } } } diff --git a/crates/quake/src/manifest/generate.rs b/crates/quake/src/manifest/generate.rs index 2d55728..676f82c 100644 --- a/crates/quake/src/manifest/generate.rs +++ b/crates/quake/src/manifest/generate.rs @@ -347,6 +347,7 @@ impl Manifest { engine: ElEngineConfig { disable_state_cache: Some(rng.gen_bool(0.1)), cross_block_cache_size: Some(rng.gen_range(2048..=8192)), + legacy_state_root: Some(rng.gen_bool(0.1)), ..ElEngineConfig::default() }, ..ElConfigOverride::default() @@ -959,6 +960,7 @@ mod tests { (2048..=8192).contains(&cache), "cross-block-cache-size = {cache}" ); + assert!(g.engine.legacy_state_root.is_some()); } } diff --git a/docs/installation.md b/docs/installation.md index 7bb1cd8..3f045cd 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -57,10 +57,12 @@ git checkout v0.6.0 **3. Build and install:** +By default, `cargo install` places binaries in `~/.cargo/bin`, which is added to `PATH` by the Rust installer. Pass `--root ` to install into `/bin` instead (e.g. `--root /usr/local`). + ```sh -cargo install --path crates/node --root /usr/local -cargo install --path crates/malachite-app --root /usr/local -cargo install --path crates/snapshots --root /usr/local +cargo install --path crates/node +cargo install --path crates/malachite-app +cargo install --path crates/snapshots ``` Verify: From 56300163ba8dc4f9f50121e53c3d8630e556ab79 Mon Sep 17 00:00:00 2001 From: atiwari-circle Date: Tue, 7 Apr 2026 13:40:14 +0000 Subject: [PATCH 2/2] fix: bump trivy-action to v0.35.0 (compromised v0.34.2) Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/build-docker.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-docker.yaml b/.github/workflows/build-docker.yaml index 237c997..3dc3537 100644 --- a/.github/workflows/build-docker.yaml +++ b/.github/workflows/build-docker.yaml @@ -77,7 +77,7 @@ jobs: GIT_SHORT_HASH: ${{ steps.vars.outputs.short_hash }} - name: Trivy vulnerability scan - uses: aquasecurity/trivy-action@97e0b3872f55f89b95b2f65b3dbab56962816478 # v0.34.2 + uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0 with: input: /tmp/image format: sarif