newton-core 0.4.16

newton protocol core sdk
//! Digest computation utilities for BLS signature consensus.
//!
//! Provides functions to compute digests for TaskResponse used in BLS
//! signature aggregation and on-chain verification.

use alloy::{
    primitives::{keccak256, FixedBytes},
    sol_types::SolValue,
};

use crate::newton_prover_task_manager::INewtonProverTaskManager::TaskResponse;

/// Computes the consensus digest for a TaskResponse.
///
/// This is the digest that operators BLS-sign. Must match the on-chain
/// `TaskLib.computeConsensusDigest` exactly.
pub fn compute_consensus_digest(task_response: &TaskResponse) -> FixedBytes<32> {
    keccak256(TaskResponse::abi_encode(task_response))
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::newton_prover_task_manager::{INewtonPolicy, NewtonMessage};
    use alloy::primitives::{Address, Bytes, B256, U256};

    fn create_test_task_response() -> TaskResponse {
        TaskResponse {
            taskId: B256::ZERO,
            policyClient: Address::ZERO,
            policyId: B256::ZERO,
            policyAddress: Address::ZERO,
            intent: NewtonMessage::Intent {
                from: Address::ZERO,
                to: Address::ZERO,
                value: U256::ZERO,
                data: Bytes::new(),
                chainId: U256::from(1),
                functionSignature: Bytes::new(),
            },
            intentSignature: Bytes::new(),
            evaluationResult: Bytes::from(vec![1]),
            policyTaskData: NewtonMessage::PolicyTaskData {
                policyId: B256::ZERO,
                policyAddress: Address::ZERO,
                policy: Bytes::new(),
                policyData: vec![
                    NewtonMessage::PolicyData {
                        wasmArgs: Bytes::new(),
                        data: Bytes::from(b"{\"price\": 100}".to_vec()),

                        policyDataAddress: Address::ZERO,
                        expireBlock: 1000,
                    },
                    NewtonMessage::PolicyData {
                        wasmArgs: Bytes::new(),
                        data: Bytes::from(b"{\"balance\": 50}".to_vec()),

                        policyDataAddress: Address::ZERO,
                        expireBlock: 1000,
                    },
                ],
            },
            policyConfig: INewtonPolicy::PolicyConfig {
                policyParams: Bytes::new(),
                expireAfter: 0,
            },
            initializationTimestamp: U256::ZERO,
        }
    }

    #[test]
    fn consensus_digest_differs_when_data_differs() {
        let response1 = create_test_task_response();
        let mut response2 = response1.clone();
        response2.policyTaskData.policyData[0].data = Bytes::from(b"{\"price\": 200}".to_vec());

        let digest1 = compute_consensus_digest(&response1);
        let digest2 = compute_consensus_digest(&response2);

        assert_ne!(digest1, digest2, "Digests should differ when policy data differs");
    }

    #[test]
    fn digest_is_deterministic() {
        let response = create_test_task_response();

        let digest1 = compute_consensus_digest(&response);
        let digest2 = compute_consensus_digest(&response);

        assert_eq!(digest1, digest2, "Same input should produce same digest");
    }

    #[test]
    fn digest_handles_empty_policy_data() {
        let response = TaskResponse {
            taskId: B256::ZERO,
            policyClient: Address::ZERO,
            policyId: B256::ZERO,
            policyAddress: Address::ZERO,
            intent: NewtonMessage::Intent {
                from: Address::ZERO,
                to: Address::ZERO,
                value: U256::ZERO,
                data: Bytes::new(),
                chainId: U256::from(1),
                functionSignature: Bytes::new(),
            },
            intentSignature: Bytes::new(),
            evaluationResult: Bytes::new(),
            policyTaskData: NewtonMessage::PolicyTaskData {
                policyId: B256::ZERO,
                policyAddress: Address::ZERO,
                policy: Bytes::new(),
                policyData: vec![],
            },
            policyConfig: INewtonPolicy::PolicyConfig {
                policyParams: Bytes::new(),
                expireAfter: 0,
            },
            initializationTimestamp: U256::ZERO,
        };

        let _ = compute_consensus_digest(&response);
    }
}