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

Metadata. Also add schema to each Borsh value #99

Merged
merged 10 commits into from
Mar 10, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/cross-contract-high-level/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ crate-type = ["cdylib", "rlib"]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.45"
near-bindgen = { path = "../../near-bindgen"}
borsh = "0.2.10"
borsh = "0.4.0"
wee_alloc = { version = "0.4.5", default-features = false, features = [] }

[profile.release]
Expand Down
4 changes: 2 additions & 2 deletions near-bindgen-core/src/code_generator/attr_sig_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ impl AttrSigInfo {
serde_json::from_slice(&data).expect("Failed to deserialize callback using JSON")
},
SerializerType::Borsh => quote! {
borsh::BorshDeserialize::try_from_slice(&data).expect("Failed to deserialize callback using JSON")
borsh::try_from_slice_with_schema(&data).expect("Failed to deserialize callback using JSON")
MaksymZavershynskyi marked this conversation as resolved.
Show resolved Hide resolved
},
};
quote! {
Expand All @@ -189,7 +189,7 @@ impl AttrSigInfo {
serde_json::from_slice(&data).expect("Failed to deserialize callback using JSON")
},
SerializerType::Borsh => quote! {
borsh::BorshDeserialize::try_from_slice(&data).expect("Failed to deserialize callback using JSON")
borsh::try_from_slice_with_schema(&data).expect("Failed to deserialize callback using JSON")
MaksymZavershynskyi marked this conversation as resolved.
Show resolved Hide resolved
},
};
quote! {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use syn::ReturnType;
impl ImplItemMethodInfo {
/// Generate wrapper method for the given method of the contract.
pub fn method_wrapper(&self) -> TokenStream2 {
self.record_metadata();
let ImplItemMethodInfo { attr_signature_info, struct_type, .. } = self;
// Args provided by `env::input()`.
let has_input_args = attr_signature_info.input_args().next().is_some();
Expand All @@ -28,7 +29,7 @@ impl ImplItemMethodInfo {
).expect("Failed to deserialize input from JSON.")
},
SerializerType::Borsh => quote! {
borsh::BorshDeserialize::try_from_slice(
borsh::try_from_slice_with_schema(
&near_bindgen::env::input().expect("Expected input since method has arguments.")
).expect("Failed to deserialize input from Borsh.")
},
Expand Down Expand Up @@ -98,7 +99,7 @@ impl ImplItemMethodInfo {
let result = serde_json::to_vec(&result).expect("Failed to serialize the return value using JSON.");
},
SerializerType::Borsh => quote! {
let result = borsh::BorshSerialize::try_to_vec(&result).expect("Failed to serialize the return value using Borsh.");
let result = borsh::try_to_vec_with_schema(&result).expect("Failed to serialize the return value using Borsh.");
},
};
quote! {
Expand Down
8 changes: 4 additions & 4 deletions near-bindgen-core/src/code_generator/item_impl_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,13 +413,13 @@ mod tests {
k: u64,
m: Bar,
}
let Input { k, m, }: Input = borsh::BorshDeserialize::try_from_slice(
let Input { k, m, }: Input = borsh::try_from_slice_with_schema(
&near_bindgen::env::input().expect("Expected input since method has arguments.")
)
.expect("Failed to deserialize input from Borsh.");
let mut contract: Hello = near_bindgen::env::state_read().unwrap_or_default();
let result = contract.method(k, m, );
let result = borsh::BorshSerialize::try_to_vec(&result)
let result = borsh::try_to_vec_with_schema(&result)
.expect("Failed to serialize the return value using Borsh.");
near_bindgen::env::value_return(&result);
near_bindgen::env::state_write(&contract);
Expand All @@ -446,15 +446,15 @@ mod tests {
struct Input {
y: String,
}
let Input { y, }: Input = borsh::BorshDeserialize::try_from_slice(
let Input { y, }: Input = borsh::try_from_slice_with_schema(
&near_bindgen::env::input().expect("Expected input since method has arguments.")
)
.expect("Failed to deserialize input from Borsh.");
let data: Vec<u8> = match near_bindgen::env::promise_result(0u64) {
near_bindgen::PromiseResult::Successful(x) => x,
_ => panic!("Callback computation {} was not successful", 0u64)
};
let mut x: u64 = borsh::BorshDeserialize::try_from_slice(&data)
let mut x: u64 = borsh::try_from_slice_with_schema(&data)
.expect("Failed to deserialize callback using JSON");
let data: Vec<u8> = match near_bindgen::env::promise_result(1u64) {
near_bindgen::PromiseResult::Successful(x) => x,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ impl TraitItemMethodInfo {
let args = serde_json::to_vec(&args).expect("Failed to serialize the cross contract args using JSON.");
},
SerializerType::Borsh => quote! {
let args = borsh::BorshSerialize::try_to_vec(&args).expect("Failed to serialize the cross contract args using Borsh.");
let args = borsh::try_to_vec_with_schema(&args).expect("Failed to serialize the cross contract args using Borsh.");
},
};
}
Expand Down
2 changes: 2 additions & 0 deletions near-bindgen-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@

mod code_generator;
mod info_extractor;
mod metadata_generator;
pub use code_generator::*;
pub use info_extractor::*;
pub use metadata_generator::generate_metadata_method;
110 changes: 110 additions & 0 deletions near-bindgen-core/src/metadata_generator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use crate::{BindgenArgType, ImplItemMethodInfo};
use syn::{Receiver, ReturnType};

use std::borrow::Borrow;
use std::cell::RefCell;
use syn::export::TokenStream2;

thread_local! {
static METADATA: RefCell<Vec<TokenStream2>> = RefCell::new(vec![]);
}

impl ImplItemMethodInfo {
/// Record metadata for this method in a global container.
pub fn record_metadata(&self) {
let method_name_str = self.attr_signature_info.ident.to_string();
let is_view = match &self.attr_signature_info.receiver {
None => true,
Some(rec) => rec.mutability.is_none(),
};
let is_init = self.attr_signature_info.is_init;
let args = if self.attr_signature_info.input_args().next().is_some() {
quote! {
Some(Input::schema_container())
}
} else {
quote! {
None
}
};
let callbacks: Vec<_> = self
.attr_signature_info
.args
.iter()
.filter(|arg| match arg.bindgen_ty {
BindgenArgType::CallbackArg => true,
_ => false,
})
.map(|arg| {
let ty = &arg.ty;
quote! {
#ty::schema_container()
}
})
.collect();
let callbacks_vec = match self
.attr_signature_info
.args
.iter()
.filter(|arg| match arg.bindgen_ty {
BindgenArgType::CallbackArgVec => true,
_ => false,
})
.last()
{
None => {
quote! {
None
}
}
Some(arg) => {
let ty = &arg.ty;
quote! {
Some(#ty::schema_container())
}
}
};
let result = match &self.attr_signature_info.returns {
ReturnType::Default => {
quote! {
None
}
}
ReturnType::Type(_, ty) => {
quote! {
Some(#ty::schema_container())
}
}
};

METADATA.with(move |m| {
m.borrow_mut().push(quote! {
near_bindgen::MethodMetadata {
name: #method_name_str.to_string(),
is_view: #is_view,
is_init: #is_init,
args: #args,
callbacks: #callbacks,
callbacks_vec: #callbacks_vec,
result: #result
}
});
});
}
}

/// Produce method that exposes metadata.
pub fn generate_metadata_method() -> TokenStream2 {
let methods = (*METADATA.borrow()).clone();
quote! {
#[cfg(target_arch = "wasm32")]
#[no_mangle]
pub extern "C" fn metadata() {
let metadata = near_bindgen::Metadata::new(vec![
#(#methods),*
]);
let data = borsh::try_to_vec_with_schema(&metadata).expect("Failed to serialize the metadata using Borsh");
near_bindgen::env::value_return(&data);
}
}
}
MaksymZavershynskyi marked this conversation as resolved.
Show resolved Hide resolved
6 changes: 6 additions & 0 deletions near-bindgen-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,9 @@ pub fn result_serializer(_attr: TokenStream, item: TokenStream) -> TokenStream {
pub fn init(_attr: TokenStream, item: TokenStream) -> TokenStream {
item
}

/// `metadata` generate the metadata method and should be placed at the very end of the `lib.rs` file.
#[proc_macro_attribute]
pub fn metadata(_attr: TokenStream, item: TokenStream) -> TokenStream {
generate_metadata_method().into()
}
2 changes: 1 addition & 1 deletion near-bindgen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ path = "compilation_tests/all.rs"
# Provide near_bindgen macros.
serde = { version = "1.0", features = ["derive"] }
near-bindgen-macros = { path = "../near-bindgen-macros", version = "0.4.2"}
borsh = "0.2.10"
borsh = "0.4.0"
near-vm-logic = "0.4.5"
near-runtime-fees = "0.4.5"

Expand Down
2 changes: 2 additions & 0 deletions near-bindgen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ pub use environment::env;
mod promise;
pub use promise::{Promise, PromiseOrValue};

mod metadata;

#[cfg(not(target_arch = "wasm32"))]
pub use environment::mocked_blockchain::MockedBlockchain;
#[cfg(not(target_arch = "wasm32"))]
Expand Down
35 changes: 35 additions & 0 deletions near-bindgen/src/metadata.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/// Version of the metadata format.
const METADATA_SEMVER: [u32; 3] = [0, 1, 0];

/// Metadata of the contract.
#[derive(BorshSerialize, BorshDeserialize, Debug, PartialEq)]
pub struct Metadata {
/// Semver of the metadata.
pub version: [u32; 3],
/// Metadata of all methods.
pub methods: Vec<MethodMetadata>,
}

impl Metadata {
pub fn new(methods: Vec<MethodMetadata>) -> Self {
Self { version: METADATA_SEMVER, methods }
}
}

/// Metadata of a single method.
#[derive(BorshSerialize, BorshDeserialize, Debug, PartialEq)]
pub struct MethodMetadata {
pub name: String,
/// Whether method does not modify the state.
pub is_view: bool,
/// Whether method can be used to initialize the state.
pub is_init: bool,
/// Schema of the arguments of the method.
pub args: Option<BorshSchemaContainer>,
/// Schemas for each callback of the method.
pub callbacks: Vec<BorshSchemaContainer>,
/// If all callbacks have the same type then this field can be used instead.
pub callbacks_vec: Option<BorshSchemaContainer>,
/// Schema of the return type.
pub result: Option<BorshSchemaContainer>,
}