Skip to content

Commit 07b7e43

Browse files
authored
Merge pull request #505 from hashgraph/sr/delegate-contract-id
2 parents 1a959b9 + 4ac8c53 commit 07b7e43

File tree

6 files changed

+186
-5
lines changed

6 files changed

+186
-5
lines changed

src/account/account_id.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,6 @@ impl AccountId {
131131
/// Validates `self.checksum` (if it exists) for `client`.
132132
///
133133
/// # Errors
134-
/// - [`Error::CannotPerformTaskWithoutLedgerId`] if the client has no `ledger_id`.
135134
/// - [`Error::BadEntityId`] if there is a checksum, and the checksum is not valid for the client's `ledger_id`.
136135
pub fn validate_checksum(&self, client: &Client) -> crate::Result<()> {
137136
if self.alias.is_some() || self.evm_address.is_some() {

src/contract/contract_id.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ impl ContractId {
134134
/// Convert `self` to a string with a valid checksum.
135135
///
136136
/// # Errors
137-
/// - [`Error::CannotPerformTaskWithoutLedgerId`] if the client has no ledger ID. This may become a panic in a future (breaking) release.
137+
/// - [`Error::CannotCreateChecksum`] if self has an `evm_address`.
138138
pub fn to_string_with_checksum(&self, client: &Client) -> Result<String, Error> {
139139
if self.evm_address.is_some() {
140140
Err(Error::CannotCreateChecksum)
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/*
2+
* ‌
3+
* Hedera Rust SDK
4+
* ​
5+
* Copyright (C) 2022 - 2023 Hedera Hashgraph, LLC
6+
* ​
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* ‍
19+
*/
20+
21+
use std::fmt;
22+
use std::str::FromStr;
23+
24+
use hedera_proto::services;
25+
26+
use crate::entity_id::{
27+
Checksum,
28+
PartialEntityId,
29+
};
30+
use crate::ethereum::IdEvmAddress;
31+
use crate::protobuf::{
32+
FromProtobuf,
33+
ToProtobuf,
34+
};
35+
use crate::{
36+
EntityId,
37+
Error,
38+
};
39+
40+
/// A unique identifier for a smart contract on Hedera.
41+
#[derive(Hash, PartialEq, Eq, Clone, Copy)]
42+
pub struct DelegateContractId {
43+
/// A non-negative number identifying the shard containing this contract instance.
44+
pub shard: u64,
45+
46+
/// A non-negative number identifying the realm within the shard containing this contract instance.
47+
pub realm: u64,
48+
49+
/// A non-negative number identifying the entity within the realm containing this contract instance.
50+
///
51+
/// Note: Exactly one of `evm_address` and `num` must exist.
52+
pub num: u64,
53+
54+
/// A checksum if the contract ID was read from a user inputted string which inclueded a checksum
55+
pub checksum: Option<Checksum>,
56+
57+
/// EVM address identifying the entity within the realm containing this contract instance.
58+
///
59+
/// Note: Exactly one of `evm_address` and `num` must exist.
60+
pub evm_address: Option<[u8; 20]>,
61+
}
62+
63+
impl DelegateContractId {
64+
/// Create a `DelegateContractId` from the given shard/realm/num
65+
#[must_use]
66+
pub fn new(shard: u64, realm: u64, num: u64) -> Self {
67+
Self { shard, realm, num, evm_address: None, checksum: None }
68+
}
69+
70+
/// Create a `DelegateContractId` from a solidity address.
71+
///
72+
/// # Errors
73+
/// - [`Error::BasicParse`] if `address` cannot be parsed as a solidity address.
74+
pub fn from_solidity_address(address: &str) -> crate::Result<Self> {
75+
let EntityId { shard, realm, num, checksum } = EntityId::from_solidity_address(address)?;
76+
77+
Ok(Self { shard, realm, num, evm_address: None, checksum })
78+
}
79+
}
80+
81+
impl fmt::Debug for DelegateContractId {
82+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83+
write!(f, "\"{self}\"")
84+
}
85+
}
86+
87+
impl fmt::Display for DelegateContractId {
88+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89+
if let Some(address) = &self.evm_address {
90+
write!(f, "{}.{}.{}", self.shard, self.realm, IdEvmAddress::from_ref(address))
91+
} else {
92+
write!(f, "{}.{}.{}", self.shard, self.realm, self.num)
93+
}
94+
}
95+
}
96+
97+
impl FromStr for DelegateContractId {
98+
type Err = Error;
99+
100+
fn from_str(s: &str) -> Result<Self, Self::Err> {
101+
// override the error message for better context.
102+
let partial = PartialEntityId::from_str(s).map_err(|_| {
103+
Error::basic_parse(format!(
104+
"expecting <shard>.<realm>.<num> or <shard>.<realm>.<evm_address>, got `{s}`"
105+
))
106+
})?;
107+
108+
match partial {
109+
PartialEntityId::ShortNum(it) => Ok(Self::from(it)),
110+
PartialEntityId::LongNum(it) => Ok(Self::from(it)),
111+
PartialEntityId::ShortOther(_) => Err(Error::basic_parse(format!(
112+
"expecting <shard>.<realm>.<num> or <shard>.<realm>.<evm_address>, got `{s}`"
113+
))),
114+
PartialEntityId::LongOther { shard, realm, last } => {
115+
let evm_address = Some(IdEvmAddress::from_str(last)?.to_bytes());
116+
117+
Ok(Self { shard, realm, num: 0, evm_address, checksum: None })
118+
}
119+
}
120+
}
121+
}
122+
123+
impl From<[u8; 20]> for DelegateContractId {
124+
fn from(address: [u8; 20]) -> Self {
125+
Self { shard: 0, realm: 0, num: 0, evm_address: Some(address), checksum: None }
126+
}
127+
}
128+
129+
impl From<u64> for DelegateContractId {
130+
fn from(num: u64) -> Self {
131+
Self::new(0, 0, num)
132+
}
133+
}
134+
135+
impl From<EntityId> for DelegateContractId {
136+
fn from(value: EntityId) -> Self {
137+
let EntityId { shard, realm, num, checksum } = value;
138+
139+
Self { shard, realm, num, evm_address: None, checksum }
140+
}
141+
}
142+
143+
impl FromProtobuf<services::ContractId> for DelegateContractId {
144+
fn from_protobuf(pb: services::ContractId) -> crate::Result<Self> {
145+
let contract = pb_getf!(pb, contract)?;
146+
147+
let (num, evm_address) = match contract {
148+
services::contract_id::Contract::ContractNum(it) => (it as u64, None),
149+
services::contract_id::Contract::EvmAddress(it) => {
150+
(0, Some(IdEvmAddress::try_from(it)?.to_bytes()))
151+
}
152+
};
153+
154+
Ok(Self {
155+
evm_address,
156+
num,
157+
shard: pb.shard_num as u64,
158+
realm: pb.realm_num as u64,
159+
checksum: None,
160+
})
161+
}
162+
}
163+
164+
impl ToProtobuf for DelegateContractId {
165+
type Protobuf = services::ContractId;
166+
167+
fn to_protobuf(&self) -> Self::Protobuf {
168+
services::ContractId {
169+
contract: Some(match &self.evm_address {
170+
Some(address) => services::contract_id::Contract::EvmAddress(address.to_vec()),
171+
None => services::contract_id::Contract::ContractNum(self.num as i64),
172+
}),
173+
realm_num: self.realm as i64,
174+
shard_num: self.shard as i64,
175+
}
176+
}
177+
}

src/contract/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ mod contract_info;
3232
mod contract_info_query;
3333
mod contract_log_info;
3434
mod contract_update_transaction;
35+
mod delegate_contract_id;
3536

3637
pub use contract_bytecode_query::ContractBytecodeQuery;
3738
pub(crate) use contract_bytecode_query::ContractBytecodeQueryData;
@@ -53,3 +54,4 @@ pub(crate) use contract_info_query::ContractInfoQueryData;
5354
pub use contract_log_info::ContractLogInfo;
5455
pub use contract_update_transaction::ContractUpdateTransaction;
5556
pub(crate) use contract_update_transaction::ContractUpdateTransactionData;
57+
pub use delegate_contract_id::DelegateContractId;

src/key/key.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
use hedera_proto::services;
2222

23+
use crate::contract::DelegateContractId;
2324
use crate::{
2425
ContractId,
2526
Error,
@@ -31,6 +32,7 @@ use crate::{
3132

3233
/// Any method that can be used to authorize an operation on Hedera.
3334
#[derive(Eq, PartialEq, Debug, Clone, Hash)]
35+
#[non_exhaustive]
3436
pub enum Key {
3537
// todo(sr): not happy with any of these (fix before merge)
3638
/// A single public key.
@@ -40,7 +42,7 @@ pub enum Key {
4042
ContractId(ContractId),
4143

4244
/// A delegatable contract ID.
43-
DelegatableContractId(ContractId),
45+
DelegateContractId(DelegateContractId),
4446

4547
/// A key list.
4648
KeyList(KeyList),
@@ -72,7 +74,7 @@ impl ToProtobuf for Key {
7274
}
7375

7476
Self::ContractId(id) => ContractId(id.to_protobuf()),
75-
Self::DelegatableContractId(id) => DelegatableContractId(id.to_protobuf()),
77+
Self::DelegateContractId(id) => DelegatableContractId(id.to_protobuf()),
7678
// `KeyList`s are special and can be both a key list and a threshold key.
7779
Self::KeyList(key) => key.to_protobuf_key(),
7880
}),
@@ -109,7 +111,7 @@ impl FromProtobuf<services::Key> for Key {
109111
Some(Ed25519(bytes)) => Ok(Self::Single(PublicKey::from_bytes_ed25519(&bytes)?)),
110112
Some(ContractId(id)) => Ok(Self::ContractId(crate::ContractId::from_protobuf(id)?)),
111113
Some(DelegatableContractId(id)) => {
112-
Ok(Self::DelegatableContractId(crate::ContractId::from_protobuf(id)?))
114+
Ok(Self::DelegateContractId(crate::DelegateContractId::from_protobuf(id)?))
113115
}
114116
Some(Rsa3072(_)) => {
115117
Err(Error::from_protobuf("unexpected unsupported RSA-3072 key in Key"))

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ pub use contract::{
196196
ContractInfoQuery,
197197
ContractLogInfo,
198198
ContractUpdateTransaction,
199+
DelegateContractId,
199200
};
200201
pub use entity_id::EntityId;
201202
pub(crate) use entity_id::ValidateChecksums;

0 commit comments

Comments
 (0)