Browse Source

Agolang, code, and functions

master
Lev 2 years ago
parent
commit
a8fc4686d5
  1. 2
      Cargo.toml
  2. 9
      agolang/Cargo.toml
  3. 88
      agolang/src/lib.rs
  4. 132
      agolang/src/templates.rs
  5. 69
      agolang/src/value.rs
  6. 1
      agorata_contracts/Cargo.toml
  7. 4
      agorata_contracts/src/lib.rs
  8. 33
      agorata_contracts/src/message.rs
  9. 2
      agorata_contracts/src/state.rs
  10. 61
      agorata_contracts/src/templates.rs
  11. 38
      agorata_contracts/src/value.rs

2
Cargo.toml

@ -7,7 +7,7 @@ authors = ["ennucore <lev@ennucore.com>"]
[workspace]
members = [
"agolytics", "agorata_contracts"
"agolytics", "agorata_contracts", "agolang"
]
[dependencies]

9
agolang/Cargo.toml

@ -0,0 +1,9 @@
[package]
name = "agolang"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = { version = "1.0", features = ["derive"] }

88
agolang/src/lib.rs

@ -0,0 +1,88 @@
use serde::{Deserialize, Serialize};
use crate::value::Value;
/// Variables for matching.
pub mod value;
/// Templates for types.
pub mod templates;
/// The Error type
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum AgolangError {
/// The given value is not of the expected type.
TypeError(String),
/// The given value(s) could not be matched.
ValueError(String),
/// The code is not valid.
CodeError(String),
}
/// Code - a function that works on the variables. For now, the output is fixed.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Code {
/// Output
pub output: Vec<Value>,
}
/// Function that takes a set of variables, matches them and returns a set of variables.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Function {
/// Input variables.
pub input: Vec<value::Type>,
/// Output variables.
pub output: Vec<value::Type>,
/// Code to run depending on the template.
pub code: Vec<(templates::ComplexMatcher, Code)>,
}
impl Code {
/// Check if all the types add up.
pub fn check_correctness(&self) -> bool {
true
}
/// Run the code and get the output.
pub fn eval(&self, _variables: &[Value]) -> Vec<Value> {
self.output.clone()
}
}
impl Function {
/// Check if all the types add up.
pub fn check_correctness(&self) -> bool {
for c in &self.code {
if !c.0.check_correctness_for_types(&self.input) {
return false;
}
if !c.1.check_correctness() {
return false;
}
if !c.1.output.iter().zip(self.output.iter()).all(|(o, t)| o.var_type() == *t) {
return false;
}
}
true
}
/// Get matcher for the whole input.
pub fn get_total_matcher(&self) -> templates::ComplexMatcher {
templates::ComplexMatcher::Or(self.code.iter().map(|c| c.0.clone()).collect())
}
/// Evaluate the function on the given values.
pub fn evaluate(&self, values: &[Value]) -> Result<Vec<Value>, AgolangError> {
if !self.check_correctness() {
return Err(AgolangError::CodeError("Function is not valid".to_string()));
}
for c in &self.code {
if c.0.match_values(values) {
return Ok(c.1.eval(values));
}
}
Err(AgolangError::CodeError("Function could not be matched".to_string()))
}
}
#[cfg(test)]
mod tests {
}

132
agolang/src/templates.rs

@ -0,0 +1,132 @@
use serde::{Deserialize, Serialize};
use crate::value::{Type, Value};
/// Matchers of values
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Matcher {
Any,
Fixed(Value),
NotEqual(Value),
Whitelist(Vec<Value>),
Blacklist(Vec<Value>),
IntRange(i64, i64),
Regexp(String),
}
impl Matcher {
/// Check if this is a valid matcher for a given type.
pub fn check_correctness_for_type(&self, type_: Type) -> bool {
match self {
Matcher::Any => true,
Matcher::Fixed(v) => v.var_type() == type_,
Matcher::NotEqual(v) => v.var_type() == type_,
Matcher::Whitelist(v) | Matcher::Blacklist(v) => {
if v.is_empty() {
return false;
}
// Check that all values are of the same type and that there are no duplicates.
if v.iter().any(|v| v.var_type() != type_) {
return false;
}
let mut v_clone = v.clone();
v_clone.dedup();
v_clone.len() == v.len()
},
Matcher::IntRange(i1, i2) => i1 <= i2 && type_ == Type::Int,
Matcher::Regexp(_) => type_ == Type::String,
}
}
/// Match a value
pub fn match_value(&self, value: &Value) -> bool {
if !self.check_correctness_for_type(value.var_type()) {
return false;
}
match self {
Matcher::Any => true,
Matcher::Fixed(v) => v == value,
Matcher::NotEqual(v) => v != value,
Matcher::Whitelist(v) => v.contains(value),
Matcher::Blacklist(v) => !v.contains(value),
Matcher::IntRange(i1, i2) => match value {
Value::Int(i) => i >= i1 && i <= i2,
_ => false,
},
Matcher::Regexp(_regexp) => match value {
Value::String(_s) => todo!(),
_ => false,
},
}
}
}
/// **The Complex Matcher**
///
/// It works on lists of variables and can be composed of other (primitive) matchers.
/// The length of the input list will be called the arity of the matcher.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ComplexMatcher {
/// Checks one value using a primitive matcher. The arity of the matcher is 1.
Primitive(Matcher),
/// Checks if the variables match all matchers. The arity of all matchers is the same.
And(Vec<ComplexMatcher>),
/// Checks if the variables match any matchers. The arity of all matchers is the same.
Or(Vec<ComplexMatcher>),
/// Checks the first variables (as many as possible) using the first matcher, then the second variables (as many as possible) using the second matcher, etc.
/// The arity of this matcher is the sum of the arities of all matchers.
Compose(Vec<ComplexMatcher>),
}
impl ComplexMatcher {
/// Get the arity of the matcher.
pub fn arity(&self) -> usize {
match self {
ComplexMatcher::Primitive(_m) => 1,
ComplexMatcher::And(m) => m[0].arity(),
ComplexMatcher::Or(m) => m[0].arity(),
ComplexMatcher::Compose(m) => m.iter().map(|m| m.arity()).sum(),
}
}
/// Check if this is a valid matcher for the given types.
pub fn check_correctness_for_types(&self, types: &[Type]) -> bool {
match self {
ComplexMatcher::Primitive(m) => types.len() == 1 && m.check_correctness_for_type(types[0]),
ComplexMatcher::And(ms) => ms.iter().all(|m| m.check_correctness_for_types(types)),
ComplexMatcher::Or(ms) => ms.iter().any(|m| m.check_correctness_for_types(types)),
ComplexMatcher::Compose(ms) => {
let mut sum = 0;
for m in ms {
if !m.check_correctness_for_types(&types[sum..sum + m.arity()]) {
return false;
}
sum += m.arity();
}
sum == types.len()
},
}
}
/// Check a list of values against this matcher.
pub fn match_values(&self, values: &[Value]) -> bool {
if !self.check_correctness_for_types(&values.iter().map(|v| v.var_type()).collect::<Vec<_>>()) {
return false;
}
match self {
ComplexMatcher::Primitive(m) => m.match_value(&values[0]),
ComplexMatcher::And(ms) => ms.iter().all(|m| m.match_values(values)),
ComplexMatcher::Or(ms) => ms.iter().any(|m| m.match_values(values)),
ComplexMatcher::Compose(ms) => {
let mut sum = 0;
for m in ms {
if !m.match_values(&values[sum..sum + m.arity()]) {
return false;
}
sum += m.arity();
}
sum == values.len()
},
}
}
}

69
agolang/src/value.rs

@ -0,0 +1,69 @@
use serde::{Deserialize, Serialize};
/// Values used for variables in templates.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Value {
Int(i64),
Bool(bool),
Address(Address),
Token(Address),
Data(Vec<u8>),
String(String),
}
/// Types for variable values.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)]
pub enum Type {
Int,
Bool,
Address,
Token,
Data,
String,
}
impl Value {
/// Returns the type of the value.
pub fn var_type(&self) -> Type {
match self {
Value::Int(_) => Type::Int,
Value::Bool(_) => Type::Bool,
Value::Address(_) => Type::Address,
Value::Token(_) => Type::Token,
Value::Data(_) => Type::Data,
Value::String(_) => Type::String,
}
}
}
/// For now, only TON addresses are supported.
/// See [here](https://ton.org/docs/#/howto/step-by-step) for docs.
/// Later, multiple blockchains will be added.
/// Also, another networks might be added to TON as separate workchains, thus they will be supported automatically.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Address {
/// Workchain id.
pub workchain: i32,
/// The address inside the workchain (64-512 bits depending on the workchain).
/// `address.0` is the length in bytes, the rest is the address.
pub address: (u8, [u8; 32], [u8; 32]),
}
impl Address {
/// Creates a new address.
pub fn new(workchain: i32, address_vec: Vec<u8>) -> Self {
let address_len = address_vec.len();
let address_bytes = address_vec.as_slice();
Address {
workchain,
address: (address_len as u8, address_bytes[0..32].try_into().unwrap(), address_bytes[32..64].try_into().unwrap()),
}
}
/// Get address in the workchain as bytes.
pub fn address_as_bytes(&self) -> Vec<u8> {
let mut address = self.address.1[..self.address.0.min(32) as usize].to_vec();
address.extend_from_slice(&self.address.2[..(self.address.0 as i16 - 32).max(0) as usize]);
address
}
}

1
agorata_contracts/Cargo.toml

@ -7,3 +7,4 @@ edition = "2021"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
agolang = { path = "../agolang" }

4
agorata_contracts/src/lib.rs

@ -2,10 +2,6 @@ extern crate serde;
/// Message and Address types
pub mod message;
/// Templates for types.
pub mod templates;
/// Variables for matching.
pub mod value;
pub mod state;
#[cfg(test)]

33
agorata_contracts/src/message.rs

@ -1,36 +1,5 @@
use serde::{Deserialize, Serialize};
/// For now, only TON addresses are supported.
/// See [here](https://ton.org/docs/#/howto/step-by-step) for docs.
/// Later, multiple blockchains will be added.
/// Also, another networks might be added to TON as separate workchains, thus they will be supported automatically.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Address {
/// Workchain id.
pub workchain: i32,
/// The address inside the workchain (64-512 bits depending on the workchain).
/// `address.0` is the length in bytes, the rest is the address.
pub address: (u8, [u8; 32], [u8; 32]),
}
impl Address {
/// Creates a new address.
pub fn new(workchain: i32, address_vec: Vec<u8>) -> Self {
let address_len = address_vec.len();
let address_bytes = address_vec.as_slice();
Address {
workchain,
address: (address_len as u8, address_bytes[0..32].try_into().unwrap(), address_bytes[32..64].try_into().unwrap()),
}
}
/// Get address in the workchain as bytes.
pub fn address_as_bytes(&self) -> Vec<u8> {
let mut address = self.address.1[..self.address.0.min(32) as usize].to_vec();
address.extend_from_slice(&self.address.2[..(self.address.0 as i16 - 32).max(0) as usize]);
address
}
}
use agolang::value::Address;
/// The Message. For now, it's the TON Message. Later, it will be adapted to other blockchains.
/// See [here](https://github.com/ton-blockchain/ton/blob/master/crypto/block/block.tlb) for the full TON message schema (seizure warning) or [here](https://ton.org/docs/#/smart-contracts/messages) for a humane version.

2
agorata_contracts/src/state.rs

@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize};
use crate::value::{Type, Value};
use agolang::value::{Type, Value};
/// Description of a smart contract's state format.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]

61
agorata_contracts/src/templates.rs

@ -1,61 +0,0 @@
use serde::{Deserialize, Serialize};
use crate::value::{Type, Value};
/// Matchers of values
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Matcher {
Any,
Fixed(Value),
NotEqual(Value),
Whitelist(Vec<Value>),
Blacklist(Vec<Value>),
IntRange(i64, i64),
Regexp(String),
}
impl Matcher {
/// Check if this is a valid matcher for a given type.
pub fn check_correctness_for_type(&self, type_: Type) -> bool {
match self {
Matcher::Any => true,
Matcher::Fixed(v) => v.var_type() == type_,
Matcher::NotEqual(v) => v.var_type() == type_,
Matcher::Whitelist(v) | Matcher::Blacklist(v) => {
if v.is_empty() {
return false;
}
// Check that all values are of the same type and that there are no duplicates.
if v.iter().any(|v| v.var_type() != type_) {
return false;
}
let mut v_clone = v.clone();
v_clone.dedup();
v_clone.len() == v.len()
},
Matcher::IntRange(i1, i2) => i1 <= i2 && type_ == Type::Int,
Matcher::Regexp(_) => type_ == Type::String,
}
}
/// Match a value
pub fn match_value(&self, value: &Value) -> bool {
if !self.check_correctness_for_type(value.var_type()) {
return false;
}
match self {
Matcher::Any => true,
Matcher::Fixed(v) => v == value,
Matcher::NotEqual(v) => v != value,
Matcher::Whitelist(v) => v.contains(value),
Matcher::Blacklist(v) => !v.contains(value),
Matcher::IntRange(i1, i2) => match value {
Value::Int(i) => i >= i1 && i <= i2,
_ => false,
},
Matcher::Regexp(_regexp) => match value {
Value::String(_s) => todo!(),
_ => false,
},
}
}
}

38
agorata_contracts/src/value.rs

@ -1,38 +0,0 @@
use crate::message::Address;
use serde::{Deserialize, Serialize};
/// Values used for variables in templates.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Value {
Int(i64),
Bool(bool),
Address(Address),
Token(Address),
Data(Vec<u8>),
String(String),
}
/// Types for variable values.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)]
pub enum Type {
Int,
Bool,
Address,
Token,
Data,
String,
}
impl Value {
/// Returns the type of the value.
pub fn var_type(&self) -> Type {
match self {
Value::Int(_) => Type::Int,
Value::Bool(_) => Type::Bool,
Value::Address(_) => Type::Address,
Value::Token(_) => Type::Token,
Value::Data(_) => Type::Data,
Value::String(_) => Type::String,
}
}
}
Loading…
Cancel
Save