1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Copyright 2020 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use cid::{Cid, Codec, Multihash, POSEIDON_BLS12_381_A1_FC1, SHA2_256_TRUNC254_PADDED};

pub type Commitment = [u8; 32];

/// CommitmentToCID converts a raw commitment hash to a CID
/// by adding:
/// - the given filecoin codec type
/// - the given filecoin hash type
pub fn commitment_to_cid(mc: Codec, mh: u64, commitment: &Commitment) -> Result<Cid, &'static str> {
    validate_filecoin_cid_segments(mc, mh, commitment)?;

    let mh = Multihash::wrap(mh, commitment).map_err(|_| "failed to wrap commitment cid")?;

    Ok(Cid::new_v1(mc, mh))
}

/// CIDToCommitment extracts the raw commitment bytes, the FilMultiCodec and
/// FilMultiHash from a CID, after validating that the codec and hash type are
/// consistent
pub fn cid_to_commitment(c: &Cid) -> Result<(Codec, u64, Commitment), &'static str> {
    validate_filecoin_cid_segments(c.codec, c.hash.code(), c.hash.digest())?;

    let mut comm = Commitment::default();
    comm.copy_from_slice(c.hash.digest());

    Ok((c.codec, c.hash.code(), comm))
}

/// DataCommitmentV1ToCID converts a raw data commitment to a CID
/// by adding:
/// - codec: cid.FilCommitmentUnsealed
/// - hash type: multihash.Sha2256Truncated256Padded
pub fn data_commitment_v1_to_cid(comm_d: &Commitment) -> Result<Cid, &'static str> {
    commitment_to_cid(
        Codec::FilCommitmentUnsealed,
        SHA2_256_TRUNC254_PADDED,
        comm_d,
    )
}

/// cid_to_data_commitment_v1 extracts the raw data commitment from a CID
/// assuming that it has the correct hashing function and
/// serialization types
pub fn cid_to_data_commitment_v1(c: &Cid) -> Result<Commitment, &'static str> {
    let (codec, _, comm_d) = cid_to_commitment(c)?;

    if codec != Codec::FilCommitmentUnsealed {
        return Err("data commitment codec must be Unsealed");
    }

    Ok(comm_d)
}

/// ReplicaCommitmentV1ToCID converts a raw data commitment to a CID
/// by adding:
/// - codec: cid.FilCommitmentSealed
/// - hash type: multihash.PoseidonBls12381A1Fc1
pub fn replica_commitment_v1_to_cid(comm_r: &Commitment) -> Result<Cid, &'static str> {
    commitment_to_cid(
        Codec::FilCommitmentSealed,
        POSEIDON_BLS12_381_A1_FC1,
        comm_r,
    )
}

/// cid_to_replica_commitment_v1 extracts the raw replica commitment from a CID
/// assuming that it has the correct hashing function and
/// serialization types
pub fn cid_to_replica_commitment_v1(c: &Cid) -> Result<Commitment, &'static str> {
    let (codec, _, comm_r) = cid_to_commitment(c)?;

    if codec != Codec::FilCommitmentSealed {
        return Err("data commitment codec must be Sealed");
    }

    Ok(comm_r)
}

/// ValidateFilecoinCidSegments returns an error if the provided CID parts
/// conflict with each other.
fn validate_filecoin_cid_segments(mc: Codec, mh: u64, comm_x: &[u8]) -> Result<(), &'static str> {
    match mc {
        Codec::FilCommitmentUnsealed => {
            if mh != SHA2_256_TRUNC254_PADDED {
                return Err("Incorrect hash function for unsealed commitment");
            }
        }
        Codec::FilCommitmentSealed => {
            if mh != POSEIDON_BLS12_381_A1_FC1 {
                return Err("Incorrect hash function for sealed commitment");
            }
        }
        _ => return Err("Invalid Codec, expected sealed or unsealed commitment codec"),
    }

    if comm_x.len() != 32 {
        Err("commitments must be 32 bytes long")
    } else {
        Ok(())
    }
}

/// piece_commitment_v1_to_cid converts a comm_p to a CID
/// -- it is just a helper function that is equivalent to
/// data_commitment_v1_to_cid.
pub fn piece_commitment_v1_to_cid(comm_p: &Commitment) -> Result<Cid, &'static str> {
    data_commitment_v1_to_cid(comm_p)
}

/// cid_to_piece_commitment_v1 converts a CID to a comm_p
/// -- it is just a helper function that is equivalent to
/// cid_to_data_commitment_v1.
pub fn cid_to_piece_commitment_v1(c: &Cid) -> Result<Commitment, &'static str> {
    cid_to_data_commitment_v1(c)
}