Skip to content
Open
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
1 change: 1 addition & 0 deletions examples/solidity/bindings/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod r#auction;
pub mod r#fast_types;
pub mod r#pi2;
pub mod r#ranked_feed;
pub mod r#recover;
pub mod r#test_mint_balance_precompile;
pub mod r#time;
pub mod r#voting;
487 changes: 487 additions & 0 deletions examples/solidity/bindings/src/recover.rs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Generated by the following Solidity interface...
```solidity
interface TestMintBalancePrecompile {
function mint(uint256 value) external;
function mint(uint256 value) external view;
}
```

Expand All @@ -21,7 +21,7 @@ interface TestMintBalancePrecompile {
}
],
"outputs": [],
"stateMutability": "nonpayable"
"stateMutability": "view"
}
]
```*/
Expand Down Expand Up @@ -59,7 +59,7 @@ pub mod TestMintBalancePrecompile {
#[derive(Default, Debug, PartialEq, Eq, Hash)]
/**Function with signature `mint(uint256)` and selector `0xa0712d68`.
```solidity
function mint(uint256 value) external;
function mint(uint256 value) external view;
```*/
#[allow(non_camel_case_types, non_snake_case, clippy::pub_underscore_fields)]
#[derive(Clone)]
Expand Down
5 changes: 5 additions & 0 deletions examples/solidity/src/Recover.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pragma solidity ^0.8.26;

contract Recover {
function recover(bytes calldata fromTx) public {}
}
2 changes: 1 addition & 1 deletion examples/solidity/src/TestMintBalancePrecompile.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity ^0.8.26;
contract TestMintBalancePrecompile {
address constant PRECOMPILE = address(uint160(uint256(keccak256("POD_MINT_BALANCE"))));

function mint(uint256 value) public {
function mint(uint256 value) public view {
(bool success,) = PRECOMPILE.staticcall(abi.encode(value));
require(success, "Precompile call failed");
}
Expand Down
2 changes: 2 additions & 0 deletions rust-sdk/src/network/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ impl PodReceiptResponse {
.iter()
.map(|a| a.signature)
.collect(),
committee.quorum_size,
)?;

committee.verify_aggregate_attestation(
Expand All @@ -354,6 +355,7 @@ impl PodReceiptResponse {
.iter()
.map(|a| a.signature)
.collect(),
committee.quorum_size,
)?;

Ok(())
Expand Down
35 changes: 33 additions & 2 deletions types/src/consensus/attestation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,17 +133,24 @@ impl<T> From<Indexed<Attestation<T>>> for TimestampedHeadlessAttestation {

mod sol {
alloy_sol_types::sol! {
#[derive(Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[derive(Copy, Debug, std::hash::Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
struct AttestedTx {
bytes32 tx_hash;
bool success;
uint64 committee_epoch;
}

#[derive(Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
struct AttestedBot {
uint64 nonce;
address signer;
}
}
}

pub use sol::AttestedTx;
pub use sol::{AttestedBot, AttestedTx};

impl AttestedTx {
pub fn success(tx_hash: Hash, committee_epoch: u64) -> Self {
Expand All @@ -153,6 +160,14 @@ impl AttestedTx {
committee_epoch,
}
}

pub fn failure(tx_hash: Hash, committee_epoch: u64) -> Self {
Self {
tx_hash,
success: false,
committee_epoch,
}
}
}

impl Hashable for AttestedTx {
Expand All @@ -175,3 +190,19 @@ impl Merkleizable for AttestedTx {
);
}
}

impl AttestedBot {
pub fn new(nonce: u64, signer: Address) -> Self {
Self { nonce, signer }
}
}

impl Hashable for AttestedBot {
fn hash_custom(&self) -> Hash {
self.eip712_signing_hash(&alloy_sol_types::eip712_domain! {
name: "attest_bot",
version: "1",
chain_id: 0x50d,
})
}
}
52 changes: 43 additions & 9 deletions types/src/consensus/committee.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::{Attestation, Certificate};
use crate::cryptography::hash::{Hash, Hashable};
use alloy_primitives::{Address, Signature};
use anyhow::ensure;
use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;

Expand All @@ -17,16 +18,32 @@ pub enum CommitteeError {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Committee {
pub validators: BTreeSet<Address>,
pub cert_quorum: usize,
pub quorum_size: usize,
}

impl Committee {
pub fn new(validators: impl IntoIterator<Item = Address>, quorum_size: usize) -> Self {
let validator_set = validators.into_iter().collect();
Committee {
validators: validator_set,
pub fn new(
validators: impl IntoIterator<Item = Address>,
cert_quorum: usize,
quorum_size: usize,
) -> anyhow::Result<Self> {
ensure!(quorum_size > 0, "quorum_size must be greater than 0");
ensure!(cert_quorum > 0, "cert_quorum must be greater than 0");
ensure!(
cert_quorum <= quorum_size,
"cert_quorum must be less than or equal to quorum_size"
);
let validators: BTreeSet<Address> = validators.into_iter().collect();
ensure!(
quorum_size <= validators.len(),
"quorum_size must be less than or equal to the number of validators"
);
Ok(Committee {
validators,
quorum_size,
}
cert_quorum,
})
}

pub fn size(&self) -> usize {
Expand Down Expand Up @@ -66,11 +83,12 @@ impl Committee {
&self,
digest: Hash,
signatures: &Vec<Signature>,
quorum_size: usize,
) -> Result<(), CommitteeError> {
if signatures.len() < self.quorum_size {
if signatures.len() < quorum_size {
return Err(CommitteeError::InsufficientQuorum {
got: signatures.len(),
required: self.quorum_size,
required: quorum_size,
});
}

Expand All @@ -95,10 +113,10 @@ impl Committee {
}

// Verify we have enough unique valid signatures from committee members
if recovered_signers.len() < self.quorum_size {
if recovered_signers.len() < quorum_size {
return Err(CommitteeError::InsufficientQuorum {
got: recovered_signers.len(),
required: self.quorum_size,
required: quorum_size,
});
}

Expand All @@ -108,10 +126,26 @@ impl Committee {
pub fn verify_certificate<C: Hashable>(
&self,
certificate: &Certificate<C>,
quorum_size: usize,
) -> Result<(), CommitteeError> {
self.verify_aggregate_attestation(
certificate.certified.hash_custom(),
&certificate.signatures,
quorum_size,
)
}

pub fn verify_high_quorum_certificate<C: Hashable>(
&self,
certificate: &Certificate<C>,
) -> Result<(), CommitteeError> {
self.verify_certificate(certificate, self.quorum_size)
}

pub fn verify_low_quorum_certificate<C: Hashable>(
&self,
certificate: &Certificate<C>,
) -> Result<(), CommitteeError> {
self.verify_certificate(certificate, self.cert_quorum)
}
}
21 changes: 12 additions & 9 deletions types/src/ledger/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,18 @@ impl VerifiableLog {
})
}
pub fn verify(&self, committee: &Committee) -> Result<(), CommitteeError> {
committee.verify_certificate(&Certificate {
signatures: self
.pod_metadata
.attestations
.iter()
.map(|att| att.signature)
.collect(),
certified: self.pod_metadata.receipt.clone(),
})
committee.verify_certificate(
&Certificate {
signatures: self
.pod_metadata
.attestations
.iter()
.map(|att| att.signature)
.collect(),
certified: self.pod_metadata.receipt.clone(),
},
committee.quorum_size,
)
}
pub fn confirmation_time(&self) -> Timestamp {
let num_attestations = self.pod_metadata.attestations.len();
Expand Down
21 changes: 20 additions & 1 deletion types/src/ledger/transaction.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use alloy_consensus::{SignableTransaction, TxEip1559};
use alloy_primitives::Address;
use alloy_primitives::{Address, U256};
use alloy_sol_types::SolValue;

use crate::cryptography::{
Expand All @@ -9,6 +9,25 @@ use crate::cryptography::{

pub type Transaction = TxEip1559;

pub trait PodTransactionExt {
/// TX gas cost (max_fee_per_gas * gas_limit)
fn cost_from_gas(&self) -> Option<u128>;

/// Total cost of the transaction (gas cost + value)
fn total_cost(&self) -> Option<U256>;
}

impl PodTransactionExt for Transaction {
fn cost_from_gas(&self) -> Option<u128> {
self.max_fee_per_gas.checked_mul(self.gas_limit as u128)
}

fn total_cost(&self) -> Option<U256> {
self.cost_from_gas()
.and_then(|cost| U256::from(cost).checked_add(self.value))
}
}

impl Merkleizable for Transaction {
fn append_leaves(&self, builder: &mut MerkleBuilder) {
builder.add_field("to", self.to.to().unwrap_or(&Address::ZERO).hash_custom());
Expand Down