darkfi_sdk/crypto/
contract_id.rs

1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2025 Dyne.org foundation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18
19#[cfg(feature = "async")]
20use darkfi_serial::async_trait;
21use darkfi_serial::{serialize, SerialDecodable, SerialEncodable};
22use lazy_static::lazy_static;
23use pasta_curves::{group::ff::PrimeField, pallas};
24
25use super::{poseidon_hash, PublicKey, SecretKey};
26use crate::error::ContractError;
27
28/// The hardcoded db name for the zkas circuits database tree
29pub const SMART_CONTRACT_ZKAS_DB_NAME: &str = "_zkas";
30
31/// The hardcoded db name for the monotree database tree
32pub const SMART_CONTRACT_MONOTREE_DB_NAME: &str = "_monotree";
33
34lazy_static! {
35    // The idea here is that 0 is not a valid x coordinate for any pallas point,
36    // therefore a signature cannot be produced for such IDs. This allows us to
37    // avoid hardcoding contract IDs for arbitrary contract deployments, because
38    // the contracts with 0 as their x coordinate can never have a valid signature.
39
40    /// Derivation prefix for `ContractId`
41    pub static ref CONTRACT_ID_PREFIX: pallas::Base = pallas::Base::from(42);
42
43    /// Contract ID for the native money contract
44    ///
45    /// `BZHKGQ26bzmBithTQYTJtjo2QdCqpkR9tjSBopT4yf4o`
46    pub static ref MONEY_CONTRACT_ID: ContractId =
47        ContractId::from(poseidon_hash([*CONTRACT_ID_PREFIX, pallas::Base::zero(), pallas::Base::from(0)]));
48
49    /// Contract ID for the native DAO contract
50    ///
51    /// `Fd8kfCuqU8BoFFp6GcXv5pC8XXRkBK7gUPQX5XDz7iXj`
52    pub static ref DAO_CONTRACT_ID: ContractId =
53        ContractId::from(poseidon_hash([*CONTRACT_ID_PREFIX, pallas::Base::zero(), pallas::Base::from(1)]));
54
55    /// Contract ID for the native Deployooor contract
56    ///
57    /// `EJs7oEjKkvCeEVCmpRsd6fEoTGCFJ7WKUBfmAjwaegN`
58    pub static ref DEPLOYOOOR_CONTRACT_ID: ContractId =
59        ContractId::from(poseidon_hash([*CONTRACT_ID_PREFIX, pallas::Base::zero(), pallas::Base::from(2)]));
60
61    /// Native contract IDs bytes, for various checks
62    pub static ref NATIVE_CONTRACT_IDS_BYTES: [[u8; 32]; 3] =
63        [MONEY_CONTRACT_ID.to_bytes(), DAO_CONTRACT_ID.to_bytes(), DEPLOYOOOR_CONTRACT_ID.to_bytes()];
64
65    /// Native contract zkas circuits database trees, for various checks
66    pub static ref NATIVE_CONTRACT_ZKAS_DB_NAMES: [[u8; 32]; 3] = [
67        MONEY_CONTRACT_ID.hash_state_id(SMART_CONTRACT_ZKAS_DB_NAME),
68        DAO_CONTRACT_ID.hash_state_id(SMART_CONTRACT_ZKAS_DB_NAME),
69        DEPLOYOOOR_CONTRACT_ID.hash_state_id(SMART_CONTRACT_ZKAS_DB_NAME),
70    ];
71}
72
73/// ContractId represents an on-chain identifier for a certain smart contract.
74#[derive(Copy, Clone, Debug, Eq, PartialEq, SerialEncodable, SerialDecodable)]
75pub struct ContractId(pallas::Base);
76
77impl ContractId {
78    /// Derives a `ContractId` from a `SecretKey` (deploy key)
79    pub fn derive(deploy_key: SecretKey) -> Self {
80        let public_key = PublicKey::from_secret(deploy_key);
81        let (x, y) = public_key.xy();
82        let hash = poseidon_hash([*CONTRACT_ID_PREFIX, x, y]);
83        Self(hash)
84    }
85
86    /// Derive a contract ID from a `PublicKey`
87    pub fn derive_public(public_key: PublicKey) -> Self {
88        let (x, y) = public_key.xy();
89        let hash = poseidon_hash([*CONTRACT_ID_PREFIX, x, y]);
90        Self(hash)
91    }
92
93    /// Get the inner `pallas::Base` element.
94    pub fn inner(&self) -> pallas::Base {
95        self.0
96    }
97
98    /// Create a `ContractId` object from given bytes.
99    pub fn from_bytes(x: [u8; 32]) -> Result<Self, ContractError> {
100        match pallas::Base::from_repr(x).into() {
101            Some(v) => Ok(Self(v)),
102            None => Err(ContractError::IoError(
103                "Failed to instantiate ContractId from bytes".to_string(),
104            )),
105        }
106    }
107
108    /// Convert a `ContractId` object to its byte representation
109    pub fn to_bytes(&self) -> [u8; 32] {
110        self.0.to_repr()
111    }
112
113    /// `blake3(self || tree_name)` is used in databases to have a
114    /// fixed-size name for a contract's state db.
115    pub fn hash_state_id(&self, tree_name: &str) -> [u8; 32] {
116        let mut hasher = blake3::Hasher::new();
117        hasher.update(&serialize(self));
118        hasher.update(tree_name.as_bytes());
119        let id = hasher.finalize();
120        *id.as_bytes()
121    }
122}
123
124use core::str::FromStr;
125crate::fp_from_bs58!(ContractId);
126crate::fp_to_bs58!(ContractId);
127crate::ty_from_fp!(ContractId);