use alloy::primitives::{Address, Bytes, FixedBytes, B256, U256};
use chrono::{DateTime, Utc};
use eigensdk::{crypto_bls::Signature, types::operator::OperatorId};
use newton_core::{
newton_prover_task_manager::{INewtonPolicy::PolicyConfig as BindingPolicyConfig, NewtonMessage},
r#newton_prover_task_manager::INewtonProverTaskManager::TaskResponse as BindingTaskResponse,
TaskId,
};
use serde::{Deserialize, Serialize};
#[cfg(feature = "threshold")]
use curve25519_dalek::{edwards::CompressedEdwardsY, scalar::Scalar};
pub type PublicShare = (u32, Vec<u8>);
pub type ThresholdConfig = (u32, u32);
#[derive(Clone, Serialize, Deserialize)]
pub struct TaskResponse {
pub task_id: B256,
pub policy_client: Address,
pub policy_id: B256,
pub policy_address: Address,
pub intent: NewtonMessage::Intent,
pub intent_signature: Bytes,
pub evaluation_result: Vec<u8>,
pub policy_task_data: NewtonMessage::PolicyTaskData,
pub policy_config: BindingPolicyConfig,
pub initialization_timestamp: U256,
}
impl std::fmt::Debug for TaskResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TaskResponse")
.field("task_id", &self.task_id)
.field("policy_client", &self.policy_client)
.field("policy_id", &self.policy_id)
.field("policy_address", &self.policy_address)
.field("evaluation_result", &self.evaluation_result)
.finish_non_exhaustive()
}
}
impl From<BindingTaskResponse> for TaskResponse {
fn from(binding: BindingTaskResponse) -> Self {
Self {
task_id: binding.taskId,
policy_client: binding.policyClient,
policy_id: binding.policyId,
policy_address: binding.policyAddress,
intent: binding.intent,
intent_signature: binding.intentSignature,
evaluation_result: binding.evaluationResult.to_vec(),
policy_task_data: binding.policyTaskData,
policy_config: binding.policyConfig,
initialization_timestamp: binding.initializationTimestamp,
}
}
}
impl From<TaskResponse> for BindingTaskResponse {
fn from(task_response: TaskResponse) -> Self {
Self {
taskId: task_response.task_id,
policyClient: task_response.policy_client,
policyId: task_response.policy_id,
policyAddress: task_response.policy_address,
intent: task_response.intent,
intentSignature: task_response.intent_signature,
evaluationResult: task_response.evaluation_result.into(),
policyTaskData: task_response.policy_task_data,
policyConfig: task_response.policy_config,
initializationTimestamp: task_response.initialization_timestamp,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SignedTaskResponse {
pub task_response: TaskResponse,
pub task_id: TaskId,
signature: Signature,
operator_id: OperatorId,
}
impl SignedTaskResponse {
pub fn new(
task_id: TaskId,
task_response: TaskResponse,
bls_signature: Signature,
operator_id: OperatorId,
) -> Self {
Self {
task_id,
task_response,
signature: bls_signature,
operator_id,
}
}
pub fn signature(&self) -> Signature {
self.signature.clone()
}
pub fn operator_id(&self) -> OperatorId {
self.operator_id
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OperatorErrorResponse {
pub task_id: TaskId,
pub operator_id: OperatorId,
pub error_code: i32,
pub error_message: String,
pub timestamp: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConsensusPrepareRequest {
pub task_id: TaskId,
pub policy_client: Address,
pub intent: NewtonMessage::Intent,
pub intent_signature: Bytes,
pub wasm_args: Bytes,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub enc_point: Option<Vec<u8>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ephemeral_enc_points: Option<Vec<Vec<u8>>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub peer_enclave_pubkeys: Option<Vec<(OperatorId, Vec<u8>)>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PreparePhaseChainContext {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub chain_rpc_url: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub target_chain_id: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub target_task_manager_addr: Option<Address>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PreparePhaseFetchRequest {
pub task: newton_core::newton_prover_task_manager::INewtonProverTaskManager::Task,
pub signature: Bytes,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub chain_context: Option<PreparePhaseChainContext>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub enc_point: Option<Vec<u8>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ephemeral_enc_points: Option<Vec<Vec<u8>>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub identity_registry: Option<Address>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub confidential_data_registry: Option<Address>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub policy_client: Option<Address>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub intent: Option<newton_core::newton_prover_task_manager::NewtonMessage::Intent>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub intent_signature: Option<Bytes>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub peer_enclave_pubkeys: Option<Vec<(OperatorId, Vec<u8>)>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PartialDecryptionData {
pub partial_point: Vec<u8>,
pub public_share: Vec<u8>,
pub dleq_challenge: Vec<u8>,
pub dleq_response: Vec<u8>,
pub operator_index: u32,
}
#[cfg(feature = "threshold")]
impl From<newton_core::dkg::PartialDecryption> for PartialDecryptionData {
fn from(pd: newton_core::dkg::PartialDecryption) -> Self {
Self {
partial_point: pd.partial.compress().as_bytes().to_vec(),
public_share: Vec::new(), dleq_challenge: pd.proof.challenge.as_bytes().to_vec(),
dleq_response: pd.proof.response.as_bytes().to_vec(),
operator_index: pd.index,
}
}
}
#[cfg(feature = "threshold")]
impl TryFrom<&PartialDecryptionData> for newton_core::dkg::PartialDecryption {
type Error = String;
fn try_from(wire: &PartialDecryptionData) -> Result<Self, Self::Error> {
let partial_bytes: [u8; 32] = wire
.partial_point
.as_slice()
.try_into()
.map_err(|_| format!("partial_point must be 32 bytes, got {}", wire.partial_point.len()))?;
let partial = CompressedEdwardsY(partial_bytes)
.decompress()
.ok_or("invalid partial_point: not on Edwards curve")?;
let challenge_bytes: [u8; 32] = wire
.dleq_challenge
.as_slice()
.try_into()
.map_err(|_| format!("dleq_challenge must be 32 bytes, got {}", wire.dleq_challenge.len()))?;
let challenge = Scalar::from_canonical_bytes(challenge_bytes)
.into_option()
.ok_or("invalid dleq_challenge scalar")?;
let response_bytes: [u8; 32] = wire
.dleq_response
.as_slice()
.try_into()
.map_err(|_| format!("dleq_response must be 32 bytes, got {}", wire.dleq_response.len()))?;
let response = Scalar::from_canonical_bytes(response_bytes)
.into_option()
.ok_or("invalid dleq_response scalar")?;
Ok(newton_core::dkg::PartialDecryption {
index: wire.operator_index,
partial,
proof: newton_core::dkg::DleqProof { challenge, response },
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConsensusPrepareResponse {
pub task_id: TaskId,
pub operator_id: OperatorId,
pub policy_task_data: NewtonMessage::PolicyTaskData,
pub data_hash: FixedBytes<32>,
pub timestamp: DateTime<Utc>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub encrypted_partials: Option<Vec<newton_core::crypto::EncryptedPartialDH>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub partial_decryption: Option<PartialDecryptionData>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ephemeral_partial_decryptions: Option<Vec<PartialDecryptionData>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub identity_partial_decryptions: Option<Vec<PartialDecryptionData>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub confidential_partial_decryptions: Option<Vec<PartialDecryptionData>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub identity_data_ref_ids: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub confidential_data_ref_ids: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConsensusCommitRequest {
pub task_id: TaskId,
pub consensus_policy_task_data: NewtonMessage::PolicyTaskData,
pub intent: NewtonMessage::Intent,
pub intent_signature: Bytes,
pub policy_client: Address,
pub task_created_block: u32,
pub quorum_numbers: Bytes,
pub quorum_threshold_percentage: u32,
pub initialization_timestamp: u64,
pub wasm_args: Bytes,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub chain_rpc_url: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub target_chain_id: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub target_task_manager_addr: Option<Address>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ephemeral_envelopes: Option<Vec<newton_core::crypto::SecureEnvelope>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub threshold_partial_decryptions: Option<Vec<Vec<PartialDecryptionData>>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub threshold_public_shares: Option<Vec<PublicShare>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub threshold_config: Option<ThresholdConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub proof_cid: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub identity_data_ref_ids: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub identity_threshold_partials: Option<Vec<Vec<PartialDecryptionData>>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub confidential_data_ref_ids: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub confidential_threshold_partials: Option<Vec<Vec<PartialDecryptionData>>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub encrypted_peer_partials: Option<Vec<newton_core::crypto::EncryptedPartialDH>>,
#[serde(default)]
pub encrypted_partials_ephemeral_count: usize,
#[serde(default)]
pub encrypted_partials_identity_count: usize,
#[serde(default)]
pub encrypted_partials_confidential_count: usize,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub task_signature: Option<Bytes>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConsensusCommitResponse {
pub task_id: TaskId,
pub evaluation_result: Vec<u8>,
pub task_response: TaskResponse,
pub signature: Signature,
pub operator_id: OperatorId,
#[serde(default)]
pub attestation_data: alloy::primitives::Bytes,
}
#[derive(Debug, Clone)]
pub struct ConsensusData {
pub policy_task_data: NewtonMessage::PolicyTaskData,
pub data_hash: FixedBytes<32>,
pub field_adjustments: Vec<FieldAdjustment>,
}
#[derive(Debug, Clone)]
pub struct FieldAdjustment {
pub field_path: String,
pub original_values: Vec<f64>,
pub median_value: f64,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn consensus_commit_request_proof_cid_is_backward_compatible() {
let json_without_proof = json!({
"task_id": "0x0000000000000000000000000000000000000000000000000000000000000001",
"consensus_policy_task_data": {
"policyId": "0x0000000000000000000000000000000000000000000000000000000000000000",
"policyAddress": "0x0000000000000000000000000000000000000000",
"policy": "0x",
"policyData": []
},
"policy_config": {
"policyParams": "0x",
"expireAfter": 100
},
"intent": {
"from": "0x0000000000000000000000000000000000000000",
"to": "0x0000000000000000000000000000000000000000",
"value": "0x0",
"data": "0x",
"chainId": "0x1",
"functionSignature": "0x"
},
"intent_signature": "0x",
"policy_client": "0x0000000000000000000000000000000000000000",
"task_created_block": 100,
"quorum_numbers": "0x00",
"quorum_threshold_percentage": 40,
"initialization_timestamp": 123,
"wasm_args": "0x"
});
let request: ConsensusCommitRequest = serde_json::from_value(json_without_proof).unwrap();
assert!(request.proof_cid.is_none());
let json_with_proof = json!({
"task_id": "0x0000000000000000000000000000000000000000000000000000000000000001",
"consensus_policy_task_data": {
"policyId": "0x0000000000000000000000000000000000000000000000000000000000000000",
"policyAddress": "0x0000000000000000000000000000000000000000",
"policy": "0x",
"policyData": []
},
"policy_config": {
"policyParams": "0x",
"expireAfter": 100
},
"intent": {
"from": "0x0000000000000000000000000000000000000000",
"to": "0x0000000000000000000000000000000000000000",
"value": "0x0",
"data": "0x",
"chainId": "0x1",
"functionSignature": "0x"
},
"intent_signature": "0x",
"policy_client": "0x0000000000000000000000000000000000000000",
"task_created_block": 100,
"quorum_numbers": "0x00",
"quorum_threshold_percentage": 40,
"initialization_timestamp": 123,
"wasm_args": "0x",
"proof_cid": "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"
});
let request_with_proof: ConsensusCommitRequest = serde_json::from_value(json_with_proof).unwrap();
assert_eq!(
request_with_proof.proof_cid.as_deref(),
Some("bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi")
);
}
}