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

Ocean: Initial setup #2856

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
710 changes: 672 additions & 38 deletions lib/Cargo.lock

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ jsonrpsee-core = "0.16"
jsonrpsee-server = "0.16"
jsonrpsee-types = "0.16"

axum = { version = "0.7.1", features = ["macros"] }
tempdir = "0.3"

rocksdb = { version = "0.21", default-features = false }
Expand All @@ -88,8 +89,6 @@ rustc_version_runtime = "0.2.1"
parking_lot = "0.12.1"
spin = "0.9.8"

bitcoin = "0.31"

### eth

evm = { version = "0.39", default-features = false, features = ["with-serde", "tracing"] }
Expand Down Expand Up @@ -118,6 +117,11 @@ lru = "0.12"
sp-io = "24.0"
substrate-bn = "0.6"

#### Ocean dependencies
bitcoin = "0.31"
cached = { version = "0.48", features = ["async"] }
defichain-rpc = { version = "0.18.0", git = "https://github.com/defich/rust-defichain-rpc.git" }

### Local crates
ain-cpp-imports = { path = "./ain-cpp-imports" }
ain-db = { path = "./ain-db" }
Expand Down
75 changes: 74 additions & 1 deletion lib/ain-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, Fields, ItemFn, ReturnType, Type};
use syn::{
parse_macro_input, Attribute, Data, DeriveInput, Fields, ItemFn, LitStr, ReturnType, Type,
};

#[proc_macro_attribute]
pub fn ffi_fallible(_attr: TokenStream, item: TokenStream) -> TokenStream {
Expand Down Expand Up @@ -54,6 +56,77 @@ pub fn ffi_fallible(_attr: TokenStream, item: TokenStream) -> TokenStream {
TokenStream::from(expanded)
}

fn parse_repository_attr(attr: &Attribute) -> syn::Result<(String, String)> {
let mut key_type = None;
let mut value_type = None;

attr.parse_nested_meta(|meta| {
if meta.path.is_ident("K") {
let val = meta.value()?;
let s: LitStr = val.parse()?;
key_type = Some(s);
}
if meta.path.is_ident("V") {
let val = meta.value()?;
let s: LitStr = val.parse()?;
value_type = Some(s);
}
Ok(())
})?;

Ok((
key_type.expect("Missing attribute 'K'").value(),
value_type.expect("Missing attribute 'V'").value(),
))
}

#[proc_macro_derive(Repository, attributes(repository))]
pub fn repository_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident; // Struct name

let mut key_type_str = String::new();
let mut value_type_str = String::new();

for attr in &input.attrs {
if attr.path().is_ident("repository") {
let (key, value) =
parse_repository_attr(attr).expect("Error parsing 'repository' attribute");
key_type_str = key;
value_type_str = value;
}
}

let key_type_ident = syn::Ident::new(&key_type_str, proc_macro2::Span::call_site());
let value_type_ident = syn::Ident::new(&value_type_str, proc_macro2::Span::call_site());

// Generate the implementation
let expanded = quote! {
impl RepositoryOps<#key_type_ident, #value_type_ident> for #name {
type ListItem = std::result::Result<(#key_type_ident, #value_type_ident), ain_db::DBError>;

fn get(&self, id: &#key_type_ident) -> Result<Option<#value_type_ident>> {
Ok(self.col.get(id)?)
}

fn put(&self, id: &#key_type_ident, item: &#value_type_ident) -> Result<()> {
Ok(self.col.put(id, item)?)
}

fn delete(&self, id: &#key_type_ident) -> Result<()> {
Ok(self.col.delete(id)?)
}

fn list<'a>(&'a self, from: Option<#key_type_ident>, dir: crate::storage::SortOrder) -> Result<Box<dyn Iterator<Item = Self::ListItem> + 'a>> {
let it = self.col.iter(from, dir.into())?;
Ok(Box::new(it))
}
}
};

TokenStream::from(expanded)
}

#[proc_macro_derive(ConsensusEncoding)]
pub fn consensus_encoding_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
Expand Down
47 changes: 47 additions & 0 deletions lib/ain-ocean/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
[package]
name = "ain-ocean"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[profile.test]
debug = true

[dependencies]
ain-cpp-imports.workspace = true
ain-db.workspace = true
ain-macros.workspace = true

axum.workspace = true
hyper.workspace = true
keccak-hash.workspace = true
log.workspace = true
serde.workspace = true
serde_with.workspace = true
thiserror.workspace = true
hex.workspace = true
ain-dftx.workspace = true
bitcoin = { workspace = true, features = ["serde"] }
tokio = { workspace = true, features = ["full"] }
serde_json.workspace = true
json = "0.12.4"
futures = "0.3.29"
jsonrpsee.workspace = true
rocksdb.workspace = true
anyhow.workspace = true
cached.workspace = true
lazy_static.workspace = true
bincode.workspace = true
defichain-rpc.workspace = true
jsonrpc-async = "2.0.2"
serde_urlencoded = "0.7"
env_logger.workspace = true
rust_decimal = { version = "1.34", features = ["serde", "serde-float", "serde-with-str"] }
rust_decimal_macros = "1.34"
clap = { version = "4.5.0", features = ["derive"] }
num_cpus.workspace = true

[dev-dependencies]
tempdir.workspace = true
tempfile = "3.8.1"
chrono = "0.4.31"
Empty file added lib/ain-ocean/README.md
Empty file.
142 changes: 142 additions & 0 deletions lib/ain-ocean/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use std::num::ParseIntError;

use ain_db::DBError;
use anyhow::format_err;
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
Json,
};
use bitcoin::hex::HexToArrayError;
use serde::Serialize;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum NotFoundKind {
#[error("proposal")]
Proposal,
#[error("masternode")]
Masternode,
#[error("scheme")]
Scheme,
}

#[derive(Error, Debug)]
pub enum Error {
#[error("Ocean: HexToArrayError error: {0:?}")]
HexToArrayError(#[from] HexToArrayError),
#[error("Ocean: ParseIntError error: {0:?}")]
ParseIntError(#[from] ParseIntError),
#[error("Ocean: DBError error: {0:?}")]
DBError(#[from] DBError),
#[error("Ocean: IO error: {0:?}")]
IOError(#[from] std::io::Error),
#[error("Ocean: FromHexError error: {0:?}")]
FromHexError(#[from] hex::FromHexError),
#[error("Ocean: Consensus encode error: {0:?}")]
ConsensusEncodeError(#[from] bitcoin::consensus::encode::Error),
#[error("Ocean: jsonrpsee error: {0:?}")]
JsonrpseeError(#[from] jsonrpsee::core::Error),
#[error("Ocean: serde_json error: {0:?}")]
SerdeJSONError(#[from] serde_json::Error),
#[error("Ocean: RPC error: {0:?}")]
RpcError(#[from] defichain_rpc::Error),
#[error("Unable to find {0:}")]
NotFound(NotFoundKind),
#[error("Decimal conversion error")]
SecondaryIndex,
#[error("Error fetching primary value")]
DecimalError,
#[error(transparent)]
Other(#[from] anyhow::Error),
}

#[derive(Serialize)]
pub enum ErrorKind {
NotFound,
BadRequest,
Unknown,
}

#[derive(Serialize)]
struct ApiErrorData {
code: u16,
r#type: ErrorKind,
at: u128,
message: String,
url: String,
}
#[derive(Serialize)]
pub struct ApiError {
error: ApiErrorData,
#[serde(skip)]
status: StatusCode,
}

impl ApiError {
pub fn new(status: StatusCode, message: String, url: String) -> Self {
let current_time = std::time::SystemTime::now();
let at = current_time
.duration_since(std::time::UNIX_EPOCH)
.expect("Time went backwards")
.as_millis();

let r#type = match status {
StatusCode::NOT_FOUND => ErrorKind::NotFound,
StatusCode::BAD_REQUEST => ErrorKind::BadRequest,
_ => ErrorKind::Unknown,
};

Self {
error: ApiErrorData {
r#type,
code: status.as_u16(),
message,
url,
at,
},
status,
}
}
}

impl IntoResponse for ApiError {
fn into_response(self) -> Response {
let status = self.status;
let reason = Json(self);
(status, reason).into_response()
}
}

impl Error {
pub fn into_code_and_message(self) -> (StatusCode, String) {
let (code, reason) = match &self {
Error::RpcError(defichain_rpc::Error::JsonRpc(jsonrpc_async::error::Error::Rpc(e))) => {
(
StatusCode::NOT_FOUND,
match e {
e if e.message.contains("Cannot find existing loan scheme") => {
format!("{}", Error::NotFound(NotFoundKind::Scheme))
}
_ => e.message.to_string(),
},
)
}
Error::NotFound(_) => (StatusCode::NOT_FOUND, format!("{self}")),
_ => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()),
};
(code, reason)
}
}

impl From<Box<dyn std::error::Error>> for Error {
fn from(err: Box<dyn std::error::Error>) -> Error {
Error::Other(format_err!("{err}"))
}
}

impl From<&str> for Error {
fn from(s: &str) -> Self {
Error::Other(format_err!("{s}"))
}
}
12 changes: 12 additions & 0 deletions lib/ain-ocean/src/indexer/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use defichain_rpc::json::blockchain::{Block, Transaction};
use std::sync::Arc;

use crate::{Result, Services};

pub fn index_block(services: &Arc<Services>, block: Block<Transaction>) -> Result<()> {
Ok(())
}

pub fn invalidate_block(_block: Block<Transaction>) -> Result<()> {
Ok(())
}
Loading
Loading