Skip to content

Commit 3dfe463

Browse files
authored
TCK: add AccountCreateTransaction
1 parent 24d1b8a commit 3dfe463

File tree

9 files changed

+635
-143
lines changed

9 files changed

+635
-143
lines changed

Cargo.lock

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/node_address.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ impl ToProtobuf for NodeAddress {
114114
.map(|it| services::ServiceEndpoint {
115115
ip_address_v4: it.ip().octets().to_vec(),
116116
port: i32::from(it.port()),
117-
domain_name: "".to_owned(),
117+
domain_name: it.to_string(),
118118
})
119119
.collect();
120120

tck/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,17 @@ async-trait = "0.1.77"
1515
hyper = "0.14.20"
1616
tower-http = { version = "0.5.2", features = ["full"] }
1717
hedera = { path = "../." }
18+
hedera-proto = { path = "../protobufs", version = "0.14.0", features = [
19+
"time_0_3",
20+
"fraction",
21+
] }
1822
once_cell = "1.19.0"
1923
futures-util = "0.3.30"
24+
serde_json = {version = "1.0.1", features = ["raw_value"] }
25+
serde = { version = "1.0.181", features = ["derive"] }
26+
time = "0.3.9"
27+
hex = "0.4.3"
28+
hex-literal = "0.4.0"
2029

2130
[dependencies.jsonrpsee]
2231
version = "0.23.2"

tck/src/errors.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use hedera::Error;
2+
use jsonrpsee::types::error::INTERNAL_ERROR_CODE;
3+
use jsonrpsee::types::{
4+
ErrorObject,
5+
ErrorObjectOwned,
6+
};
7+
use serde_json::json;
8+
9+
pub(crate) const HEDERA_ERROR: i32 = -32001;
10+
11+
pub fn from_hedera_error(error: Error) -> ErrorObjectOwned {
12+
match error {
13+
Error::QueryPreCheckStatus { status, .. }
14+
| Error::ReceiptStatus { status, .. }
15+
| Error::TransactionPreCheckStatus { status, .. } => ErrorObject::owned(
16+
HEDERA_ERROR,
17+
"Hedera error".to_string(),
18+
Some(json!({
19+
"status": status.as_str_name().to_string(),
20+
"message": error.to_string(),
21+
})),
22+
),
23+
_ => ErrorObject::owned(INTERNAL_ERROR_CODE, error.to_string(), None::<()>),
24+
}
25+
}

tck/src/helpers.rs

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
use std::collections::HashMap;
2+
use std::str::FromStr;
3+
4+
use hedera::{
5+
AccountId,
6+
Hbar,
7+
Key,
8+
KeyList,
9+
PrivateKey,
10+
PublicKey,
11+
Transaction,
12+
TransactionId,
13+
};
14+
use hex::ToHex;
15+
use jsonrpsee::types::error::INVALID_PARAMS_CODE;
16+
use jsonrpsee::types::{
17+
ErrorObject,
18+
ErrorObjectOwned,
19+
};
20+
use serde_json::Value;
21+
use time::Duration;
22+
23+
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
24+
pub enum KeyType {
25+
Ed25519PrivateKeyType,
26+
Ed25519PublicKeyType,
27+
EcdsaSecp256k1PrivateKeyType,
28+
EcdsaSecp256k1PublicKeyType,
29+
KeyListType,
30+
ThresholdKeyType,
31+
EvmAddressType,
32+
}
33+
34+
impl FromStr for KeyType {
35+
type Err = ErrorObjectOwned;
36+
37+
fn from_str(s: &str) -> Result<Self, Self::Err> {
38+
match s {
39+
"ed25519PrivateKey" => Ok(KeyType::Ed25519PrivateKeyType),
40+
"ed25519PublicKey" => Ok(KeyType::Ed25519PublicKeyType),
41+
"ecdsaSecp256k1PrivateKey" => Ok(KeyType::EcdsaSecp256k1PrivateKeyType),
42+
"ecdsaSecp256k1PublicKey" => Ok(KeyType::EcdsaSecp256k1PublicKeyType),
43+
"keyList" => Ok(KeyType::KeyListType),
44+
"thresholdKey" => Ok(KeyType::ThresholdKeyType),
45+
"evmAddress" => Ok(KeyType::EvmAddressType),
46+
_ => Err(ErrorObject::borrowed(-32603, "generateKey: type is NOT a valid value", None)),
47+
}
48+
}
49+
}
50+
51+
pub(crate) fn fill_common_transaction_params<D>(
52+
transaction: &mut Transaction<D>,
53+
common_transaction_params: &HashMap<String, Value>,
54+
) {
55+
if let Some(transaction_id) = common_transaction_params.get("transactionId") {
56+
match transaction_id {
57+
Value::String(transaction_id) => {
58+
transaction
59+
.transaction_id(TransactionId::from_str(transaction_id.as_str()).unwrap());
60+
}
61+
_ => {}
62+
}
63+
}
64+
65+
if let Some(node_id) = common_transaction_params.get("nodeId") {
66+
match node_id {
67+
Value::String(node_id) => {
68+
transaction.node_account_ids([AccountId::from_str(&node_id.as_str()).unwrap()]);
69+
}
70+
_ => {}
71+
}
72+
}
73+
74+
if let Some(max_fee) = common_transaction_params.get("maxTransactionFee") {
75+
match max_fee {
76+
Value::String(max_fee) => {
77+
transaction.max_transaction_fee(Hbar::from_tinybars(
78+
max_fee.as_str().parse::<i64>().unwrap(),
79+
));
80+
}
81+
_ => {}
82+
}
83+
}
84+
85+
if let Some(transaction_valid_duration) =
86+
common_transaction_params.get("transactionValidDuration")
87+
{
88+
match transaction_valid_duration {
89+
Value::String(transaction_valid_duration) => {
90+
transaction.transaction_valid_duration(Duration::seconds(
91+
transaction_valid_duration.as_str().parse::<i64>().unwrap(),
92+
));
93+
}
94+
_ => {}
95+
}
96+
}
97+
98+
if let Some(memo) = common_transaction_params.get("memo") {
99+
match memo {
100+
Value::String(memo) => {
101+
transaction.transaction_memo(memo.as_str());
102+
}
103+
_ => {}
104+
}
105+
}
106+
}
107+
108+
pub(crate) fn generate_key_helper(
109+
_type: String,
110+
from_key: Option<String>,
111+
threshold: Option<i32>,
112+
keys: Option<Value>,
113+
private_keys: &mut Vec<Value>,
114+
is_list: bool,
115+
) -> Result<String, ErrorObjectOwned> {
116+
// Check the key type
117+
let key_type = KeyType::from_str(&_type)?;
118+
119+
if from_key.is_some()
120+
&& key_type != KeyType::Ed25519PublicKeyType
121+
&& key_type != KeyType::EcdsaSecp256k1PublicKeyType
122+
&& key_type != KeyType::EvmAddressType
123+
{
124+
return Err(ErrorObject::borrowed(INVALID_PARAMS_CODE, "generateKey: fromKey MUST NOT be provided for types other than ed25519PublicKey, ecdsaSecp256k1PublicKey, or evmAddress.", None));
125+
}
126+
127+
if threshold.is_some() && key_type != KeyType::ThresholdKeyType {
128+
return Err(ErrorObject::borrowed(
129+
INVALID_PARAMS_CODE,
130+
"generateKey: threshold MUST NOT be provided for types other than thresholdKey.",
131+
None,
132+
));
133+
} else if threshold.is_none() && key_type == KeyType::ThresholdKeyType {
134+
return Err(ErrorObject::borrowed(
135+
INVALID_PARAMS_CODE,
136+
"generateKey: threshold MUST be provided for thresholdKey types.",
137+
None,
138+
));
139+
};
140+
141+
if keys.is_some() && key_type != KeyType::ThresholdKeyType && key_type != KeyType::KeyListType {
142+
return Err(ErrorObject::borrowed(
143+
INVALID_PARAMS_CODE,
144+
"generateKey: keys MUST NOT be provided for types other than keyList or thresholdKey.",
145+
None,
146+
));
147+
} else if keys.is_none()
148+
&& (key_type == KeyType::ThresholdKeyType || key_type == KeyType::KeyListType)
149+
{
150+
return Err(ErrorObject::borrowed(
151+
INVALID_PARAMS_CODE,
152+
"generateKey: keys MUST be provided for keyList and thresholdKey types.",
153+
None,
154+
));
155+
};
156+
157+
match key_type {
158+
KeyType::Ed25519PrivateKeyType | KeyType::EcdsaSecp256k1PrivateKeyType => {
159+
let key = if key_type == KeyType::Ed25519PublicKeyType {
160+
PrivateKey::generate_ed25519().to_string_der()
161+
} else {
162+
PrivateKey::generate_ecdsa().to_string_der()
163+
};
164+
165+
if is_list {
166+
private_keys.push(Value::String(key.clone()));
167+
}
168+
169+
return Ok(key);
170+
}
171+
KeyType::Ed25519PublicKeyType | KeyType::EcdsaSecp256k1PublicKeyType => {
172+
if let Some(from_key) = from_key {
173+
return PrivateKey::from_str_der(&from_key)
174+
.map(|key| key.public_key().to_string_der())
175+
.map_err(|_| {
176+
ErrorObject::borrowed(
177+
INVALID_PARAMS_CODE,
178+
"generateKey: could not produce {key_type:?}",
179+
None,
180+
)
181+
});
182+
};
183+
184+
let key = if key_type == KeyType::Ed25519PublicKeyType {
185+
PrivateKey::generate_ed25519()
186+
} else {
187+
PrivateKey::generate_ecdsa()
188+
};
189+
190+
if is_list {
191+
private_keys.push(Value::String(key.to_string_der()));
192+
}
193+
194+
return Ok(key.public_key().to_string_der());
195+
}
196+
KeyType::KeyListType | KeyType::ThresholdKeyType => {
197+
let mut key_list = KeyList::new();
198+
199+
if let Value::Array(key_array) = keys.unwrap() {
200+
for key in key_array {
201+
let generate_key = &generate_key_helper(
202+
key["type"].as_str().unwrap().to_string(),
203+
None,
204+
None,
205+
key.get("keys").map(|value| value.clone()),
206+
private_keys,
207+
true,
208+
)?;
209+
210+
let get_key = get_hedera_key(&generate_key)?;
211+
212+
key_list.keys.push(get_key);
213+
}
214+
}
215+
216+
if KeyType::from_str(&_type)? == KeyType::ThresholdKeyType {
217+
key_list.threshold = Some(threshold.unwrap() as u32);
218+
}
219+
220+
return Ok(Key::KeyList(key_list).to_bytes().encode_hex());
221+
}
222+
KeyType::EvmAddressType => {
223+
if from_key.is_none() {
224+
return Ok(PrivateKey::generate_ecdsa()
225+
.public_key()
226+
.to_evm_address()
227+
.unwrap()
228+
.to_string());
229+
}
230+
231+
let private_key = PrivateKey::from_str_ecdsa(&from_key.clone().unwrap());
232+
233+
match private_key {
234+
Ok(key) => {
235+
return Ok(key.public_key().to_evm_address().unwrap().to_string());
236+
}
237+
Err(_) => {
238+
let private_key = PublicKey::from_str_ecdsa(&from_key.unwrap());
239+
240+
match private_key {
241+
Ok(key) => {
242+
return Ok(key.to_evm_address().unwrap().to_string());
243+
}
244+
Err(_) => {
245+
return Err(ErrorObject::borrowed(INVALID_PARAMS_CODE, "generateKey: fromKey for evmAddress MUST be an ECDSAsecp256k1 private or public key.", None));
246+
}
247+
}
248+
}
249+
}
250+
}
251+
}
252+
}
253+
254+
pub(crate) fn get_hedera_key(key: &str) -> Result<Key, ErrorObjectOwned> {
255+
match PrivateKey::from_str_der(key).map(|pk| Key::Single(pk.public_key())) {
256+
Ok(key) => Ok(key),
257+
Err(_) => match PublicKey::from_str_der(key).map(Key::Single) {
258+
Ok(key) => Ok(key),
259+
Err(_) => {
260+
let public_key = PublicKey::from_str_ed25519(key).map_err(|_| {
261+
ErrorObject::borrowed(-32603, "generateKey: fromKey is invalid.", None)
262+
})?;
263+
264+
Ok(public_key.into())
265+
}
266+
},
267+
}
268+
}

tck/src/main.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@ use jsonrpsee::server::{
1717
};
1818
use jsonrpsee::types::Request;
1919
use jsonrpsee::MethodResponse;
20-
use sdk_client::{
20+
use methods::{
2121
RpcServer,
2222
RpcServerImpl,
2323
};
2424
use tokio::signal;
2525

26-
pub(crate) mod sdk_client;
26+
mod errors;
27+
mod helpers;
28+
pub(crate) mod methods;
29+
mod responses;
2730

2831
#[tokio::main]
2932
async fn main() -> anyhow::Result<()> {
@@ -78,13 +81,11 @@ where
7881
{
7982
type Future = BoxFuture<'a, MethodResponse>;
8083
fn call(&self, req: Request<'a>) -> Self::Future {
81-
tracing::info!("TCK server processed method call: {}", req.method);
8284
let count = self.count.clone();
8385
let service = self.service.clone();
8486
Box::pin(async move {
8587
let rp = service.call(req).await;
86-
// Modify the state.
87-
count.fetch_add(1, Ordering::Relaxed);
88+
count.fetch_add(1, Ordering::SeqCst);
8889
rp
8990
})
9091
}

0 commit comments

Comments
 (0)