use std::borrow::Cow;
use std::str::FromStr;
use std::sync::Arc;
use ahash::HashMap;
use anyhow::Result;
use serde::Deserialize;
use sha2::Digest;
use crate::abi::value::ser::AbiSerializer;
use crate::abi::AbiHeader;
use crate::cell::{
Cell, CellBuilder, CellFamily, CellSlice, CellSliceRange, DynCell, HashBytes, Size, Store,
};
use crate::dict::RawDict;
use crate::models::{
ExtInMsgInfo, IntAddr, MsgInfo, OwnedMessage, OwnedRelaxedMessage, RelaxedIntMsgInfo,
RelaxedMsgInfo, StateInit, StdAddr,
};
use crate::num::Tokens;
use crate::prelude::Dict;
use super::error::AbiError;
use super::{AbiHeaderType, AbiType, AbiValue, AbiVersion, NamedAbiType, NamedAbiValue};
pub struct Contract {
pub abi_version: AbiVersion,
pub headers: Arc<[AbiHeaderType]>,
pub functions: HashMap<Arc<str>, Function>,
pub events: HashMap<Arc<str>, Event>,
pub init_data: HashMap<Arc<str>, (u64, NamedAbiType)>,
pub fields: Arc<[NamedAbiType]>,
}
impl Contract {
pub fn find_function_by_id(&self, id: u32, input: bool) -> Option<&Function> {
self.functions
.values()
.find(|item| input && item.input_id == id || !input && item.output_id == id)
}
pub fn find_event_by_id(&self, id: u32) -> Option<&Event> {
self.events.values().find(|event| event.id == id)
}
pub fn update_init_data(
&self,
pubkey: Option<&ed25519_dalek::VerifyingKey>,
tokens: &[NamedAbiValue],
data: &Cell,
) -> Result<Cell> {
let mut result = data.parse::<RawDict<64>>()?;
if pubkey.is_none() && tokens.is_empty() {
return Ok(data.clone());
}
let context = &mut Cell::empty_context();
let mut key_builder = CellBuilder::new();
for token in tokens {
let Some((key, ty)) = self.init_data.get(token.name.as_ref()) else {
anyhow::bail!(AbiError::UnexpectedInitDataParam(token.name.clone()));
};
token.check_type(ty)?;
key_builder.store_u64(*key)?;
result.set_ext(
key_builder.as_data_slice(),
&token.make_builder(self.abi_version)?.as_full_slice(),
context,
)?;
key_builder.rewind(64)?;
}
if let Some(pubkey) = pubkey {
key_builder.store_u64(0)?;
result.set_ext(
key_builder.as_data_slice(),
&CellBuilder::from_raw_data(pubkey.as_bytes(), 256)?.as_data_slice(),
context,
)?;
}
CellBuilder::build_from_ext(result, context).map_err(From::from)
}
pub fn encode_init_data(
&self,
pubkey: &ed25519_dalek::VerifyingKey,
tokens: &[NamedAbiValue],
) -> Result<Cell> {
let mut result = RawDict::<64>::new();
let mut init_data = self
.init_data
.iter()
.map(|(name, value)| (name.as_ref(), value))
.collect::<HashMap<_, _>>();
let context = &mut Cell::empty_context();
let mut key_builder = CellBuilder::new();
for token in tokens {
let Some((key, ty)) = init_data.remove(token.name.as_ref()) else {
anyhow::bail!(AbiError::UnexpectedInitDataParam(token.name.clone()));
};
token.check_type(ty)?;
key_builder.store_u64(*key)?;
result.set_ext(
key_builder.as_data_slice(),
&token.make_builder(self.abi_version)?.as_full_slice(),
context,
)?;
key_builder.rewind(64)?;
}
for (key, ty) in init_data.into_values() {
key_builder.store_u64(*key)?;
result.set_ext(
key_builder.as_data_slice(),
&ty.make_default_value()
.make_builder(self.abi_version)?
.as_full_slice(),
context,
)?;
key_builder.rewind(64)?;
}
key_builder.store_u64(0)?;
result.set_ext(
key_builder.as_data_slice(),
&CellBuilder::from_raw_data(pubkey.as_bytes(), 256)?.as_data_slice(),
context,
)?;
CellBuilder::build_from_ext(result, context).map_err(From::from)
}
pub fn decode_init_data(&self, data: &DynCell) -> Result<Vec<NamedAbiValue>> {
let init_data = data.parse::<Dict<u64, CellSlice>>()?;
let mut result = Vec::with_capacity(self.init_data.len());
for (key, item) in self.init_data.values() {
let Some(mut value) = init_data.get(key)? else {
anyhow::bail!(AbiError::InitDataFieldNotFound(item.name.clone()));
};
result.push(NamedAbiValue::load(item, self.abi_version, &mut value)?);
}
Ok(result)
}
pub fn encode_fields(&self, tokens: &[NamedAbiValue]) -> Result<CellBuilder> {
ok!(NamedAbiValue::check_types(tokens, &self.fields));
NamedAbiValue::tuple_to_builder(tokens, self.abi_version).map_err(From::from)
}
pub fn decode_fields(&self, mut slice: CellSlice<'_>) -> Result<Vec<NamedAbiValue>> {
self.decode_fields_ext(&mut slice, false)
}
pub fn decode_fields_ext(
&self,
slice: &mut CellSlice<'_>,
allow_partial: bool,
) -> Result<Vec<NamedAbiValue>> {
let res = ok!(NamedAbiValue::load_tuple_ext(
&self.fields,
self.abi_version,
true,
allow_partial,
slice
));
ok!(AbiValue::check_remaining(slice, allow_partial));
Ok(res)
}
}
impl<'de> Deserialize<'de> for Contract {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Error;
#[repr(transparent)]
struct Id(u32);
impl<'de> Deserialize<'de> for Id {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(transparent)]
struct Id<'a>(#[serde(borrow)] Cow<'a, str>);
match ok!(Id::deserialize(deserializer)).0.strip_prefix("0x") {
Some(s) => u32::from_str_radix(s, 16).map(Self).map_err(Error::custom),
None => Err(Error::custom("Hex number must be prefixed with 0x")),
}
}
}
#[derive(Deserialize)]
struct SerdeContract {
#[serde(default, rename = "ABI version")]
abi_version: Option<u8>,
#[serde(default)]
version: Option<String>,
#[serde(default)]
header: Vec<AbiHeaderType>,
functions: Vec<SerdeFunction>,
#[serde(default)]
events: Vec<SerdeEvent>,
#[serde(default)]
data: Vec<InitData>,
#[serde(default)]
fields: Vec<NamedAbiType>,
}
#[derive(Deserialize)]
struct SerdeFunction {
name: String,
#[serde(default)]
inputs: Vec<NamedAbiType>,
#[serde(default)]
outputs: Vec<NamedAbiType>,
#[serde(default)]
id: Option<Id>,
}
#[derive(Deserialize)]
struct SerdeEvent {
name: String,
#[serde(default)]
inputs: Vec<NamedAbiType>,
#[serde(default)]
id: Option<Id>,
}
#[derive(Deserialize)]
struct InitData {
key: u64,
#[serde(flatten)]
ty: NamedAbiType,
}
let contract = ok!(SerdeContract::deserialize(deserializer));
let abi_version = if let Some(version) = &contract.version {
ok!(AbiVersion::from_str(version).map_err(Error::custom))
} else if let Some(major) = contract.abi_version {
AbiVersion::new(major, 0)
} else {
return Err(Error::custom("No ABI version found"));
};
let headers = Arc::<[AbiHeaderType]>::from(contract.header);
let functions = contract
.functions
.into_iter()
.map(|item| {
let (input_id, output_id) = match item.id {
Some(Id(id)) => (id, id),
None => {
let id = Function::compute_function_id(
abi_version,
&item.name,
headers.as_ref(),
&item.inputs,
&item.outputs,
);
(id & Function::INPUT_ID_MASK, id | !Function::INPUT_ID_MASK)
}
};
let name = Arc::<str>::from(item.name);
(
name.clone(),
Function {
abi_version,
name,
headers: headers.clone(),
inputs: Arc::from(item.inputs),
outputs: Arc::from(item.outputs),
input_id,
output_id,
},
)
})
.collect();
let events = contract
.events
.into_iter()
.map(|item| {
let id = match item.id {
Some(Id(id)) => id,
None => {
let id = Event::compute_event_id(abi_version, &item.name, &item.inputs);
id & Function::INPUT_ID_MASK
}
};
let name = Arc::<str>::from(item.name);
(
name.clone(),
Event {
abi_version,
name,
inputs: Arc::from(item.inputs),
id,
},
)
})
.collect();
let init_data = contract
.data
.into_iter()
.map(|item| {
let name = item.ty.name.clone();
(name, (item.key, item.ty))
})
.collect();
Ok(Self {
abi_version,
headers,
functions,
events,
init_data,
fields: Arc::from(contract.fields),
})
}
}
#[derive(Debug, Clone)]
pub struct Function {
pub abi_version: AbiVersion,
pub headers: Arc<[AbiHeaderType]>,
pub name: Arc<str>,
pub inputs: Arc<[NamedAbiType]>,
pub outputs: Arc<[NamedAbiType]>,
pub input_id: u32,
pub output_id: u32,
}
impl Function {
pub const INPUT_ID_MASK: u32 = 0x7fffffff;
pub fn compute_function_id(
abi_version: AbiVersion,
name: &str,
headers: &[AbiHeaderType],
inputs: &[NamedAbiType],
outputs: &[NamedAbiType],
) -> u32 {
let mut hasher = sha2::Sha256::new();
FunctionSignatureRaw {
abi_version,
name,
headers,
inputs,
outputs,
}
.update_hasher(&mut hasher);
let hash: [u8; 32] = hasher.finalize().into();
u32::from_be_bytes(hash[0..4].try_into().unwrap())
}
pub fn encode_internal_msg_body(
version: AbiVersion,
id: u32,
tokens: &[NamedAbiValue],
) -> Result<CellBuilder> {
let mut serializer = AbiSerializer::new(version);
let output_id = AbiValue::uint(32, id);
serializer.reserve_value(&output_id);
for token in tokens {
serializer.reserve_value(&token.value);
}
let context = &mut Cell::empty_context();
serializer.write_value(&output_id, context)?;
serializer.write_tuple(tokens, context)?;
serializer.finalize(context).map_err(From::from)
}
#[inline]
pub fn builder<T: Into<String>>(abi_version: AbiVersion, name: T) -> FunctionBuilder {
FunctionBuilder::new(abi_version, name)
}
pub fn encode_external<'f, 'a: 'f>(&'f self, tokens: &'a [NamedAbiValue]) -> ExternalInput {
ExternalInput {
function: self,
tokens,
time: None,
expire_at: None,
pubkey: None,
address: None,
}
}
pub fn decode_external_input(&self, mut slice: CellSlice<'_>) -> Result<Vec<NamedAbiValue>> {
self.decode_external_input_ext(&mut slice, false)
}
pub fn decode_external_input_ext(
&self,
slice: &mut CellSlice<'_>,
allow_partial: bool,
) -> Result<Vec<NamedAbiValue>> {
let id = if self.abi_version.major == 1 {
let id = slice.load_u32()?;
slice.load_reference()?;
ok!(AbiHeader::skip_all(&self.headers, slice));
id
} else {
if slice.load_bit()? {
slice.skip_first(512, 0)?;
}
ok!(AbiHeader::skip_all(&self.headers, slice));
slice.load_u32()?
};
anyhow::ensure!(
id == self.input_id,
AbiError::InputIdMismatch {
expected: self.input_id,
id
}
);
let res = ok!(NamedAbiValue::load_tuple_ext(
&self.inputs,
self.abi_version,
true,
allow_partial,
slice
));
ok!(AbiValue::check_remaining(slice, allow_partial));
Ok(res)
}
pub fn encode_internal_input(&self, tokens: &[NamedAbiValue]) -> Result<CellBuilder> {
ok!(NamedAbiValue::check_types(tokens, &self.inputs));
Self::encode_internal_msg_body(self.abi_version, self.input_id, tokens)
}
pub fn encode_internal_message(
&self,
tokens: &[NamedAbiValue],
dst: IntAddr,
value: Tokens,
bounce: bool,
state_init: Option<&StateInit>,
) -> Result<Box<OwnedRelaxedMessage>> {
let body = self.encode_internal_input(tokens)?;
let cell = body.build()?;
let range = CellSliceRange::full(cell.as_ref());
Ok(Box::new(OwnedRelaxedMessage {
info: RelaxedMsgInfo::Int(RelaxedIntMsgInfo {
dst,
bounce,
value: value.into(),
..Default::default()
}),
body: (cell, range),
init: state_init.cloned(),
layout: None,
}))
}
pub fn decode_internal_input(&self, mut slice: CellSlice<'_>) -> Result<Vec<NamedAbiValue>> {
self.decode_internal_input_ext(&mut slice, false)
}
pub fn decode_internal_input_ext(
&self,
slice: &mut CellSlice<'_>,
allow_partial: bool,
) -> Result<Vec<NamedAbiValue>> {
let id = slice.load_u32()?;
anyhow::ensure!(
id == self.input_id,
AbiError::InputIdMismatch {
expected: self.input_id,
id
}
);
let res = ok!(NamedAbiValue::load_tuple_ext(
&self.inputs,
self.abi_version,
true,
allow_partial,
slice
));
ok!(AbiValue::check_remaining(slice, allow_partial));
Ok(res)
}
pub fn encode_output(&self, tokens: &[NamedAbiValue]) -> Result<CellBuilder> {
ok!(NamedAbiValue::check_types(tokens, &self.outputs));
Self::encode_internal_msg_body(self.abi_version, self.output_id, tokens)
}
pub fn decode_output(&self, mut slice: CellSlice<'_>) -> Result<Vec<NamedAbiValue>> {
self.decode_output_ext(&mut slice, false)
}
pub fn decode_output_ext(
&self,
slice: &mut CellSlice<'_>,
allow_partial: bool,
) -> Result<Vec<NamedAbiValue>> {
let id = slice.load_u32()?;
anyhow::ensure!(
id == self.output_id,
AbiError::OutputIdMismatch {
expected: self.output_id,
id
}
);
let res = ok!(NamedAbiValue::load_tuple_ext(
&self.outputs,
self.abi_version,
true,
allow_partial,
slice
));
ok!(AbiValue::check_remaining(slice, allow_partial));
Ok(res)
}
pub fn display_signature(&self) -> impl std::fmt::Display + '_ {
FunctionSignatureRaw {
abi_version: self.abi_version,
name: &self.name,
headers: &self.headers,
inputs: &self.inputs,
outputs: &self.outputs,
}
}
}
#[derive(Clone)]
pub struct ExternalInput<'f, 'a> {
function: &'f Function,
tokens: &'a [NamedAbiValue],
time: Option<u64>,
expire_at: Option<u32>,
pubkey: Option<&'a ed25519_dalek::VerifyingKey>,
address: Option<&'a StdAddr>,
}
impl<'f, 'a> ExternalInput<'f, 'a> {
pub fn build_message(&self, address: &StdAddr) -> Result<UnsignedExternalMessage> {
Ok(ok!(self.build_input_ext(Some(address))).with_dst(address.clone()))
}
pub fn build_message_without_signature(
&self,
address: &StdAddr,
) -> Result<(u32, OwnedMessage)> {
let (expire_at, body) = ok!(self.build_input_without_signature());
let range = CellSliceRange::full(body.as_ref());
Ok((
expire_at,
OwnedMessage {
info: MsgInfo::ExtIn(ExtInMsgInfo {
dst: IntAddr::Std(address.clone()),
..Default::default()
}),
body: (body, range),
init: None,
layout: None,
},
))
}
pub fn build_input(&self) -> Result<UnsignedBody> {
self.build_input_ext(self.address)
}
fn build_input_ext(&self, address: Option<&StdAddr>) -> Result<UnsignedBody> {
let (expire_at, payload) = self.build_payload(true)?;
let context = &mut Cell::empty_context();
let hash = if self.function.abi_version >= AbiVersion::V2_3 {
let mut to_sign = CellBuilder::new();
match address {
Some(address) => address.store_into(&mut to_sign, context)?,
None => anyhow::bail!(AbiError::AddressNotProvided),
};
to_sign.store_slice(payload.as_slice()?)?;
*to_sign.build_ext(context)?.repr_hash()
} else {
*payload.repr_hash()
};
Ok(UnsignedBody {
abi_version: self.function.abi_version,
expire_at,
payload,
hash,
})
}
pub fn build_input_without_signature(&self) -> Result<(u32, Cell)> {
self.build_payload(false)
}
fn build_payload(&self, reserve_signature: bool) -> Result<(u32, Cell)> {
const DEFAULT_TIMEOUT_SEC: u32 = 60;
fn now_ms() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.unwrap()
.as_millis() as u64
}
ok!(NamedAbiValue::check_types(
self.tokens,
&self.function.inputs
));
let abi_version = self.function.abi_version;
let mut serializer = AbiSerializer::new(abi_version);
if reserve_signature {
serializer.add_offset(if abi_version.major == 1 {
Size { bits: 0, refs: 1 }
} else {
let bits = if abi_version >= AbiVersion::V2_3 {
IntAddr::BITS_MAX
} else {
1 + 512
};
Size { bits, refs: 0 }
});
}
let input_id = AbiValue::uint(32, self.function.input_id);
serializer.reserve_headers(&self.function.headers, self.pubkey.is_some());
serializer.reserve_value(&input_id);
for token in self.tokens {
serializer.reserve_value(&token.value);
}
let context = &mut Cell::empty_context();
if !reserve_signature {
let value = if abi_version.major == 1 {
AbiValue::Cell(Cell::default())
} else {
AbiValue::Bool(false)
};
serializer.reserve_value(&value);
serializer.write_value(&value, context)?;
}
let time = self.time.unwrap_or_else(now_ms);
let expire_at = self
.expire_at
.unwrap_or((time / 1000) as u32 + DEFAULT_TIMEOUT_SEC);
for header in self.function.headers.as_ref() {
serializer.write_header_value(&match header {
AbiHeaderType::Time => AbiHeader::Time(time),
AbiHeaderType::Expire => AbiHeader::Expire(expire_at),
AbiHeaderType::PublicKey => {
AbiHeader::PublicKey(self.pubkey.map(|key| Box::new(*key)))
}
})?;
}
serializer.write_value(&input_id, context)?;
serializer.write_tuple(self.tokens, context)?;
let payload = serializer.finalize(context)?.build_ext(context)?;
Ok((expire_at, payload))
}
#[inline]
pub fn set_time(&mut self, time: u64) {
self.time = Some(time);
}
#[inline]
pub fn with_time(mut self, time: u64) -> Self {
self.set_time(time);
self
}
#[inline]
pub fn set_expire_at(&mut self, expire_at: u32) {
self.expire_at = Some(expire_at);
}
#[inline]
pub fn with_expire_at(mut self, expire_at: u32) -> Self {
self.set_expire_at(expire_at);
self
}
#[inline]
pub fn set_pubkey(&mut self, pubkey: &'a ed25519_dalek::VerifyingKey) {
self.pubkey = Some(pubkey);
}
#[inline]
pub fn with_pubkey(mut self, pubkey: &'a ed25519_dalek::VerifyingKey) -> Self {
self.set_pubkey(pubkey);
self
}
#[inline]
pub fn set_address(&mut self, address: &'a StdAddr) {
self.address = Some(address);
}
#[inline]
pub fn with_address(mut self, address: &'a StdAddr) -> Self {
self.set_address(address);
self
}
}
#[derive(Debug, Clone)]
pub struct FunctionBuilder {
abi_version: AbiVersion,
name: String,
headers: Vec<AbiHeaderType>,
inputs: Vec<NamedAbiType>,
outputs: Vec<NamedAbiType>,
id: Option<u32>,
}
impl FunctionBuilder {
pub fn new<T: Into<String>>(abi_version: AbiVersion, name: T) -> Self {
Self {
abi_version,
name: name.into(),
headers: Default::default(),
inputs: Default::default(),
outputs: Default::default(),
id: None,
}
}
pub fn build(self) -> Function {
let (input_id, output_id) = match self.id {
Some(id) => (id, id),
None => {
let id = Function::compute_function_id(
self.abi_version,
&self.name,
&self.headers,
&self.inputs,
&self.outputs,
);
(id & Function::INPUT_ID_MASK, id | !Function::INPUT_ID_MASK)
}
};
Function {
abi_version: self.abi_version,
headers: Arc::from(self.headers),
name: Arc::from(self.name),
inputs: Arc::from(self.inputs),
outputs: Arc::from(self.outputs),
input_id,
output_id,
}
}
pub fn with_headers<I: IntoIterator<Item = AbiHeaderType>>(mut self, headers: I) -> Self {
self.headers = headers.into_iter().collect();
self
}
pub fn with_inputs<I, T>(mut self, inputs: I) -> Self
where
I: IntoIterator<Item = T>,
T: Into<NamedAbiType>,
{
self.inputs = inputs.into_iter().map(Into::into).collect();
self
}
pub fn with_unnamed_inputs<I: IntoIterator<Item = AbiType>>(mut self, inputs: I) -> Self {
self.inputs = inputs
.into_iter()
.enumerate()
.map(NamedAbiType::from)
.collect();
self
}
pub fn with_outputs<I, T>(mut self, outputs: I) -> Self
where
I: IntoIterator<Item = NamedAbiType>,
T: Into<NamedAbiType>,
{
self.outputs = outputs.into_iter().collect();
self
}
pub fn with_unnamed_outputs<I: IntoIterator<Item = AbiType>>(mut self, outputs: I) -> Self {
self.outputs = outputs
.into_iter()
.enumerate()
.map(NamedAbiType::from)
.collect();
self
}
pub fn with_id(mut self, id: u32) -> Self {
self.id = Some(id);
self
}
}
#[derive(Debug, Clone)]
pub struct Event {
pub abi_version: AbiVersion,
pub name: Arc<str>,
pub inputs: Arc<[NamedAbiType]>,
pub id: u32,
}
impl Event {
pub fn compute_event_id(abi_version: AbiVersion, name: &str, inputs: &[NamedAbiType]) -> u32 {
let mut hasher = sha2::Sha256::new();
EventSignatureRaw {
abi_version,
name,
inputs,
}
.update_hasher(&mut hasher);
let hash: [u8; 32] = hasher.finalize().into();
u32::from_be_bytes(hash[0..4].try_into().unwrap())
}
#[inline]
pub fn builder<T: Into<String>>(abi_version: AbiVersion, name: T) -> EventBuilder {
EventBuilder::new(abi_version, name)
}
pub fn encode_internal_input(&self, tokens: &[NamedAbiValue]) -> Result<CellBuilder> {
ok!(NamedAbiValue::check_types(tokens, &self.inputs));
Function::encode_internal_msg_body(self.abi_version, self.id, tokens)
}
pub fn decode_internal_input(&self, mut slice: CellSlice<'_>) -> Result<Vec<NamedAbiValue>> {
self.decode_internal_input_ext(&mut slice, false)
}
pub fn decode_internal_input_ext(
&self,
slice: &mut CellSlice<'_>,
allow_partial: bool,
) -> Result<Vec<NamedAbiValue>> {
let id = slice.load_u32()?;
anyhow::ensure!(
id == self.id,
AbiError::InputIdMismatch {
expected: self.id,
id
}
);
let res = ok!(NamedAbiValue::load_tuple_ext(
&self.inputs,
self.abi_version,
true,
allow_partial,
slice
));
ok!(AbiValue::check_remaining(slice, allow_partial));
Ok(res)
}
pub fn display_signature(&self) -> impl std::fmt::Display + '_ {
EventSignatureRaw {
abi_version: self.abi_version,
name: &self.name,
inputs: &self.inputs,
}
}
}
#[derive(Debug, Clone)]
pub struct EventBuilder {
abi_version: AbiVersion,
name: String,
inputs: Vec<NamedAbiType>,
id: Option<u32>,
}
impl EventBuilder {
pub fn new<T: Into<String>>(abi_version: AbiVersion, name: T) -> Self {
Self {
abi_version,
name: name.into(),
inputs: Default::default(),
id: None,
}
}
pub fn build(self) -> Event {
let id = match self.id {
Some(id) => id,
None => {
let id = Event::compute_event_id(self.abi_version, &self.name, &self.inputs);
id & Function::INPUT_ID_MASK
}
};
Event {
abi_version: self.abi_version,
name: Arc::from(self.name),
inputs: Arc::from(self.inputs),
id,
}
}
pub fn with_inputs<I, T>(mut self, inputs: I) -> Self
where
I: IntoIterator<Item = T>,
T: Into<NamedAbiType>,
{
self.inputs = inputs.into_iter().map(Into::into).collect();
self
}
pub fn with_unnamed_inputs<I: IntoIterator<Item = AbiType>>(mut self, inputs: I) -> Self {
self.inputs = inputs
.into_iter()
.enumerate()
.map(NamedAbiType::from)
.collect();
self
}
pub fn with_id(mut self, id: u32) -> Self {
self.id = Some(id);
self
}
}
pub struct UnsignedExternalMessage {
pub dst: StdAddr,
pub body: UnsignedBody,
pub init: Option<StateInit>,
}
impl UnsignedExternalMessage {
pub fn set_state_init(&mut self, init: Option<StateInit>) {
self.init = init;
}
pub fn with_state_init(mut self, init: StateInit) -> Self {
self.set_state_init(Some(init));
self
}
#[inline]
pub fn expire_at(&self) -> u32 {
self.body.expire_at
}
pub fn sign(
self,
key: &ed25519_dalek::SigningKey,
signature_id: Option<i32>,
) -> Result<OwnedMessage> {
let signature =
crate::abi::sign_with_signature_id(key, self.body.hash.as_slice(), signature_id);
self.with_signature(&signature)
}
pub fn with_signature(self, signature: &ed25519_dalek::Signature) -> Result<OwnedMessage> {
self.into_signed(Some(&signature.to_bytes()))
}
pub fn with_fake_signature(self) -> Result<OwnedMessage> {
self.into_signed(Some(&[0u8; 64]))
}
pub fn without_signature(self) -> Result<OwnedMessage> {
self.into_signed(None)
}
pub fn fill_signature(&self, signature: Option<&[u8; 64]>) -> Result<OwnedMessage> {
let body = self.body.fill_signature(signature)?;
let range = CellSliceRange::full(body.as_ref());
Ok(OwnedMessage {
info: MsgInfo::ExtIn(ExtInMsgInfo {
dst: IntAddr::Std(self.dst.clone()),
..Default::default()
}),
body: (body, range),
init: self.init.clone(),
layout: None,
})
}
fn into_signed(self, signature: Option<&[u8; 64]>) -> Result<OwnedMessage> {
let body = self.body.fill_signature(signature)?;
let range = CellSliceRange::full(body.as_ref());
Ok(OwnedMessage {
info: MsgInfo::ExtIn(ExtInMsgInfo {
dst: IntAddr::Std(self.dst),
..Default::default()
}),
body: (body, range),
init: self.init,
layout: None,
})
}
}
pub struct UnsignedBody {
pub abi_version: AbiVersion,
pub payload: Cell,
pub hash: HashBytes,
pub expire_at: u32,
}
impl UnsignedBody {
pub fn with_dst(self, dst: StdAddr) -> UnsignedExternalMessage {
UnsignedExternalMessage {
dst,
body: self,
init: None,
}
}
pub fn sign(self, key: &ed25519_dalek::SigningKey, signature_id: Option<i32>) -> Result<Cell> {
let signature = crate::abi::sign_with_signature_id(key, self.hash.as_slice(), signature_id);
self.with_signature(&signature)
}
pub fn with_signature(self, signature: &ed25519_dalek::Signature) -> Result<Cell> {
self.fill_signature(Some(&signature.to_bytes()))
}
pub fn with_fake_signature(self) -> Result<Cell> {
self.fill_signature(Some(&[0u8; 64]))
}
pub fn without_signature(self) -> Result<Cell> {
self.fill_signature(None)
}
pub fn fill_signature(&self, signature: Option<&[u8; 64]>) -> Result<Cell> {
let mut builder = CellBuilder::new();
if self.abi_version.major == 1 {
builder.store_reference(match signature {
Some(signature) => {
CellBuilder::from_raw_data(signature, 512).and_then(CellBuilder::build)?
}
None => Cell::empty_cell(),
})?;
} else {
match signature {
Some(signature) => {
builder.store_bit_one()?;
builder.store_raw(signature, 512)?;
}
None => builder.store_bit_zero()?,
}
builder.store_slice(self.payload.as_slice()?)?;
}
builder.build().map_err(From::from)
}
}
struct FunctionSignatureRaw<'a> {
abi_version: AbiVersion,
name: &'a str,
headers: &'a [AbiHeaderType],
inputs: &'a [NamedAbiType],
outputs: &'a [NamedAbiType],
}
impl FunctionSignatureRaw<'_> {
fn update_hasher<H: Digest>(&self, engine: &mut H) {
std::fmt::write(&mut DisplayHasher(engine), format_args!("{self}")).unwrap();
}
}
impl std::fmt::Display for FunctionSignatureRaw<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
ok!(write!(f, "{}(", self.name));
let mut first = true;
if self.abi_version.major == 1 {
for header in self.headers {
if !std::mem::take(&mut first) {
ok!(f.write_str(","));
}
ok!(std::fmt::Display::fmt(header, f));
}
}
for item in self.inputs {
if !std::mem::take(&mut first) {
ok!(f.write_str(","));
}
ok!(std::fmt::Display::fmt(&item.ty, f));
}
ok!(f.write_str(")("));
first = true;
for item in self.outputs {
if !std::mem::take(&mut first) {
ok!(f.write_str(","));
}
ok!(std::fmt::Display::fmt(&item.ty, f));
}
write!(f, ")v{}", self.abi_version.major)
}
}
struct EventSignatureRaw<'a> {
abi_version: AbiVersion,
name: &'a str,
inputs: &'a [NamedAbiType],
}
impl EventSignatureRaw<'_> {
fn update_hasher<H: Digest>(&self, engine: &mut H) {
std::fmt::write(&mut DisplayHasher(engine), format_args!("{self}")).unwrap();
}
}
impl std::fmt::Display for EventSignatureRaw<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
ok!(write!(f, "{}(", self.name));
let mut first = true;
for item in self.inputs {
if !std::mem::take(&mut first) {
ok!(f.write_str(","));
}
ok!(std::fmt::Display::fmt(&item.ty, f));
}
write!(f, ")v{}", self.abi_version.major)
}
}
#[repr(transparent)]
struct DisplayHasher<'a, H>(&'a mut H);
impl<H: Digest> std::fmt::Write for DisplayHasher<'_, H> {
#[inline]
fn write_str(&mut self, s: &str) -> std::fmt::Result {
self.0.update(s.as_bytes());
Ok(())
}
}