Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debot interfaces #94

Merged
merged 15 commits into from
Feb 9, 2021
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ readme = "README.md"
license = "Apache-2.0"
keywords = ["TON", "SDK", "smart contract", "tonlabs", "solidity"]
edition = "2018"
version = "0.4.0"
version = "0.5.0"

[dependencies]
async-trait = "0.1.42"
Expand All @@ -35,7 +35,6 @@ ton_sdk = { git = 'https://github.com/tonlabs/TON-SDK.git' }
ton_types = { git = "https://github.com/tonlabs/ton-labs-types.git" }
ton_block = { git = "https://github.com/tonlabs/ton-labs-block.git" }


[dev-dependencies]
assert_cmd = "0.11"
predicates = "1"
Expand Down
30 changes: 17 additions & 13 deletions src/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,22 +57,22 @@ async fn prepare_message(
let params = serde_json::from_str(&params)
.map_err(|e| format!("arguments are not in json format: {}", e))?;


let call_set = Some(CallSet {
function_name: method.into(),
input: Some(params),
header: header.clone(),
});

let msg = encode_message(
ton,
ParamsOfEncodeMessage {
abi,
address: Some(addr.to_owned()),
deploy_set: None,
call_set,
signer: if keys.is_some() {
Signer::Keys { keys: keys.unwrap() }
signer: if keys.is_some() {
Signer::Keys { keys: keys.unwrap() }
} else {
Signer::None
},
Expand All @@ -81,7 +81,7 @@ async fn prepare_message(
).await
.map_err(|e| format!("failed to create inbound message: {}", e))?;

Ok(EncodedMessage {
Ok(EncodedMessage {
message: msg.message,
message_id: msg.message_id,
expire: header.and_then(|h| h.expire),
Expand Down Expand Up @@ -121,7 +121,7 @@ fn pack_message(msg: &EncodedMessage, method: &str, is_raw: bool) -> Vec<u8> {
fn unpack_message(str_msg: &str) -> Result<(EncodedMessage, String), String> {
let bytes = hex::decode(str_msg)
.map_err(|e| format!("couldn't unpack message: {}", e))?;

let str_msg = std::str::from_utf8(&bytes)
.map_err(|e| format!("message is corrupted: {}", e))?;

Expand All @@ -141,21 +141,23 @@ fn unpack_message(str_msg: &str) -> Result<(EncodedMessage, String), String> {
let address = json_msg["msg"]["address"].as_str()
.ok_or(r#"couldn't find "address" key in message"#)?
.to_owned();

let msg = EncodedMessage {
message_id, message, expire, address
};
Ok((msg, method))
}

fn decode_call_parameters(ton: TonClient, msg: &EncodedMessage, abi: Abi) -> Result<(String, String), String> {
async fn decode_call_parameters(ton: TonClient, msg: &EncodedMessage, abi: Abi) -> Result<(String, String), String> {
let result = decode_message(
ton,
ParamsOfDecodeMessage {
abi,
message: msg.message.clone(),
},
).map_err(|e| format!("couldn't decode message: {}", e))?;
)
.await
.map_err(|e| format!("couldn't decode message: {}", e))?;

Ok((
result.name,
Expand All @@ -178,7 +180,7 @@ fn parse_integer_param(value: &str) -> Result<String, String> {
fn build_json_from_params(params_vec: Vec<&str>, abi: &str, method: &str) -> Result<String, String> {
let abi_obj = Contract::load(abi.as_bytes()).map_err(|e| format!("failed to parse ABI: {}", e))?;
let functions = abi_obj.functions();

let func_obj = functions.get(method).unwrap();
let inputs = func_obj.input_params();

Expand Down Expand Up @@ -257,14 +259,16 @@ async fn send_message_and_wait(
account: acc_boc,
execution_options: None,
abi: Some(abi.clone()),
return_updated_account: Some(true),
boc_cache: None,
},
).await
.map_err(|e| format!("run failed: {:#}", e))?;
Ok(result.decoded.and_then(|d| d.output).unwrap_or(json!({})))
} else {
println!("Processing... ");
let callback = |_| {
async move {}
async move {}
};

let result = send_message(
Expand Down Expand Up @@ -397,7 +401,7 @@ pub async fn call_contract_with_msg(conf: Config, str_msg: String, abi: String)
let (msg, _) = unpack_message(&str_msg)?;
print_encoded_message(&msg);

let params = decode_call_parameters(ton.clone(), &msg, abi.clone())?;
let params = decode_call_parameters(ton.clone(), &msg, abi.clone()).await?;

println!("Calling method {} with parameters:", params.0);
println!("{}", params.1);
Expand Down Expand Up @@ -445,7 +449,7 @@ pub async fn run_get_method(conf: Config, addr: &str, method: &str, params: Opti
).await
.map_err(|e| format!("run failed: {}", e.to_string()))?
.output;

println!("Succeded.");
println!("Result: {}", result);
Ok(())
Expand Down
6 changes: 3 additions & 3 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ fn default_false() -> bool {
false
}

#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Clone)]
pub struct Config {
#[serde(default = "default_url")]
pub url: String,
Expand Down Expand Up @@ -91,7 +91,7 @@ pub fn clear_config(
wc: bool,
retries: bool,
timeout: bool,
depool_fee: bool,
depool_fee: bool,
) -> Result<(), String> {
if url {
conf.url = default_url();
Expand Down Expand Up @@ -153,7 +153,7 @@ pub fn set_config(
wc: Option<&str>,
retries: Option<&str>,
timeout: Option<&str>,
depool_fee: Option<&str>,
depool_fee: Option<&str>,
) -> Result<(), String> {
if let Some(s) = url {
conf.url = s.to_string();
Expand Down
73 changes: 73 additions & 0 deletions src/debot/interfaces/address_input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use crate::debot::term_browser::terminal_input;
use crate::helpers::load_ton_address;
use serde_json::Value;
use ton_client::abi::Abi;
use ton_client::debot::{DebotInterface, InterfaceResult};
use super::dinterface::decode_answer_id;
use crate::config::Config;

const ID: &'static str = "d7ed1bd8e6230871116f4522e58df0a93c5520c56f4ade23ef3d8919a984653b";

pub const ABI: &str = r#"
{
"ABI version": 2,
"header": ["time"],
"functions": [
{
"name": "select",
"inputs": [
{"name":"answerId","type":"uint32"}
],
"outputs": [
{"name":"value","type":"address"}
]
},
{
"name": "constructor",
"inputs": [
],
"outputs": [
]
}
],
"data": [
],
"events": [
]
}
"#;

pub struct AddressInput {
conf: Config
}
impl AddressInput {
pub fn new(conf: Config) -> Self {
Self {conf}
}
fn select(&self, args: &Value) -> InterfaceResult {
let answer_id = decode_answer_id(args)?;
let value = terminal_input("", |val| {
let _ = load_ton_address(val, &self.conf).map_err(|e| format!("Invalid address: {}", e))?;
Ok(())
});
Ok((answer_id, json!({ "value": value })))
}
}

#[async_trait::async_trait]
impl DebotInterface for AddressInput {
fn get_id(&self) -> String {
ID.to_string()
}

fn get_abi(&self) -> Abi {
Abi::Json(ABI.to_owned())
}

async fn call(&self, func: &str, args: &Value) -> InterfaceResult {
match func {
"select" => self.select(args),
_ => Err(format!("function \"{}\" is not implemented", func)),
}
}
}
84 changes: 84 additions & 0 deletions src/debot/interfaces/dinterface.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use super::address_input::AddressInput;
use super::echo::Echo;
use super::stdout::Stdout;
use super::terminal::Terminal;
use super::menu::Menu;
use crate::config::Config;
use crate::helpers::TonClient;
use serde_json::Value;
use std::collections::HashMap;
use std::sync::Arc;
use ton_client::debot::{DebotInterface, DebotInterfaceExecutor};

pub struct SupportedInterfaces {
client: TonClient,
interfaces: HashMap<String, Arc<dyn DebotInterface + Send + Sync>>,
}

#[async_trait::async_trait]
impl DebotInterfaceExecutor for SupportedInterfaces {
fn get_interfaces<'a>(&'a self) -> &'a HashMap<String, Arc<dyn DebotInterface + Send + Sync>> {
&self.interfaces
}
fn get_client(&self) -> TonClient {
self.client.clone()
}
}

impl SupportedInterfaces {
pub fn new(client: TonClient, conf: &Config) -> Self {
let mut interfaces = HashMap::new();

let iface: Arc<dyn DebotInterface + Send + Sync> = Arc::new(AddressInput::new(conf.clone()));
interfaces.insert(iface.get_id(), iface);

let iface: Arc<dyn DebotInterface + Send + Sync> = Arc::new(Stdout::new());
interfaces.insert(iface.get_id(), iface);

let iface: Arc<dyn DebotInterface + Send + Sync> = Arc::new(Echo::new());
interfaces.insert(iface.get_id(), iface);

let iface: Arc<dyn DebotInterface + Send + Sync> = Arc::new(Terminal::new());
interfaces.insert(iface.get_id(), iface);

let iface: Arc<dyn DebotInterface + Send + Sync> = Arc::new(Menu::new());
interfaces.insert(iface.get_id(), iface);

Self { client, interfaces }
}
}

pub fn decode_answer_id(args: &Value) -> Result<u32, String> {
u32::from_str_radix(
args["answerId"]
.as_str()
.ok_or(format!("answer id not found in argument list"))?,
10,
)
.map_err(|e| format!("{}", e))
}

pub fn decode_arg(args: &Value, name: &str) -> Result<String, String> {
args[name]
.as_str()
.ok_or(format!("\"{}\" not found", name))
.map(|x| x.to_string())
}

pub fn decode_bool_arg(args: &Value, name: &str) -> Result<bool, String> {
args[name]
.as_bool()
.ok_or(format!("\"{}\" not found", name))
}

pub fn decode_string_arg(args: &Value, name: &str) -> Result<String, String> {
let bytes = hex::decode(&decode_arg(args, name)?)
.map_err(|e| format!("{}", e))?;
std::str::from_utf8(&bytes)
.map_err(|e| format!("{}", e))
.map(|x| x.to_string())
}

pub fn decode_prompt(args: &Value) -> Result<String, String> {
decode_string_arg(args, "prompt")
}
68 changes: 68 additions & 0 deletions src/debot/interfaces/echo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use serde_json::Value;
use ton_client::debot::{DebotInterface, InterfaceResult};
use ton_client::abi::Abi;

const ECHO_ID: &'static str = "f6927c0d4bdb69e1b52d27f018d156ff04152f00558042ff674f0fec32e4369d";

pub const ECHO_ABI: &str = r#"
{
"ABI version": 2,
"header": ["time"],
"functions": [
{
"name": "echo",
"inputs": [
{"name":"answerId","type":"uint32"},
{"name":"request","type":"bytes"}
],
"outputs": [
{"name":"response","type":"bytes"}
]
},
{
"name": "constructor",
"inputs": [
],
"outputs": [
]
}
],
"data": [
],
"events": [
]
}
"#;

pub struct Echo {}
impl Echo {

pub fn new() -> Self {
Self{}
}

fn echo(&self, args: &Value) -> InterfaceResult {
let answer_id = u32::from_str_radix(args["answerId"].as_str().unwrap(), 10).unwrap();
let request_vec = hex::decode(args["request"].as_str().unwrap()).unwrap();
let request = std::str::from_utf8(&request_vec).unwrap();
Ok(( answer_id, json!({ "response": hex::encode(request.as_bytes()) }) ))
}
}

#[async_trait::async_trait]
impl DebotInterface for Echo {
fn get_id(&self) -> String {
ECHO_ID.to_string()
}

fn get_abi(&self) -> Abi {
Abi::Json(ECHO_ABI.to_owned())
}

async fn call(&self, func: &str, args: &Value) -> InterfaceResult {
match func {
"echo" => self.echo(args),
_ => Err(format!("function \"{}\" is not implemented", func)),
}
}
}
Loading