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

Feature/scaffolding for tx validations #10

Merged
merged 6 commits into from
Apr 10, 2024
Merged
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
2 changes: 1 addition & 1 deletion napi-pallas/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ hex = "0.4.3"
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
napi = { version = "2.12.2", default-features = false, features = ["napi4"] }
napi-derive = "2.12.2"
pallas = "0.21.0"
pallas = { git = "https://github.com/alegadea/pallas.git", rev = "54ffc77" , features = ["unstable"]}

[build-dependencies]
napi-build = "2.0.1"
Expand Down
14 changes: 13 additions & 1 deletion napi-pallas/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,17 @@ export interface Section {
children: Array<Section>
}
export function parseAddress(raw: string): Output
export function safeParseTx(raw: string): Section
export interface SectionValidation {
section: Section
validations: Validations
}
export function safeParseTx(raw: string): SectionValidation
export function safeParseBlock(raw: string): Section
export interface Validation {
name: string
value: boolean
description: string
}
export interface Validations {
validations: Array<Validation>
}
73 changes: 70 additions & 3 deletions napi-pallas/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ extern crate napi_derive;
mod address;
mod block;
mod tx;
mod validations;

#[derive(Default)]
#[napi(object)]
Expand Down Expand Up @@ -143,11 +144,27 @@ pub fn parse_address(raw: String) -> address::Output {
}
}

#[derive(Default)]
#[napi(object)]
pub struct SectionValidation {
pub section: Section,
pub validations: Validations,
}

#[napi]
pub fn safe_parse_tx(raw: String) -> Section {
pub fn safe_parse_tx(raw: String) -> SectionValidation {
match tx::parse(raw) {
Ok(x) => x,
Err(x) => x,
Ok(x) => {
let (section, validations) = x;
SectionValidation {
section,
validations: validations,
}
}
Err(x) => SectionValidation {
section: x,
validations: Validations::new(),
},
}
}

Expand All @@ -158,3 +175,53 @@ pub fn safe_parse_block(raw: String) -> Section {
Err(x) => x,
}
}

#[derive(Default, Debug)]
#[napi(object)]
pub struct Validation {
pub name: String,
pub value: bool,
pub description: String,
}

impl Validation {
fn new() -> Self {
Default::default()
}

pub fn with_description(self, description: impl ToString) -> Self {
Self {
description: description.to_string(),
..self
}
}

pub fn with_value(self, value: bool) -> Self {
Self { value, ..self }
}

pub fn with_name(self, name: impl ToString) -> Self {
Self {
name: name.to_string(),
..self
}
}
}

#[derive(Debug, Default)]
#[napi(object)]
pub struct Validations {
pub validations: Vec<Validation>,
}

impl Validations {
pub fn new() -> Self {
Default::default()
}

pub fn add_new_validation(mut self, validation: Validation) -> Self {
self.validations.push(validation);

self
}
}
40 changes: 31 additions & 9 deletions napi-pallas/src/tx.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use crate::validations::validate::validate;
use crate::Validations;

use super::Section;
use pallas::{
codec::utils::KeepRaw,
crypto::hash::Hasher,
ledger::{
primitives::{
babbage::{Redeemer, RedeemerTag},
conway::{Metadatum, PlutusData, PlutusV1Script, VKeyWitness},
conway::{Metadatum, PlutusData, VKeyWitness},
ToCanonicalJson,
},
traverse::{ComputeHash, MultiEraInput, MultiEraOutput, MultiEraTx},
Expand Down Expand Up @@ -98,8 +101,8 @@ fn print_metadatum(datum: &Metadatum) -> String {
Metadatum::Int(x) => x.to_string(),
Metadatum::Bytes(x) => hex::encode(x.as_slice()),
Metadatum::Text(x) => x.to_owned(),
Metadatum::Array(x) => "[Array]".to_string(),
Metadatum::Map(x) => "[Map]".to_string(),
Metadatum::Array(_) => "[Array]".to_string(),
Metadatum::Map(_) => "[Map]".to_string(),
}
}

Expand Down Expand Up @@ -190,7 +193,8 @@ fn tx_witnesses_section(tx: &MultiEraTx<'_>) -> Section {
Section::new()
.with_topic("tx_witnesses")
.append_children(tx.vkey_witnesses().iter().map(tx_vkey_witnesses_section))
.append_children(tx.redeemers().iter().map(tx_redeemer_section))
// TODO: Uncomment when branch with this issue fixed is used
// .append_children(tx.redeemers().iter().map(tx_redeemer_section))
.append_children(tx.plutus_data().iter().map(tx_plutus_datum_section))
.append_children(
tx.plutus_v1_scripts()
Expand All @@ -209,11 +213,8 @@ fn tx_witnesses_section(tx: &MultiEraTx<'_>) -> Section {
)
}

pub fn parse(raw: String) -> Result<Section, Section> {
pub fn create_cbor_structure(tx: &MultiEraTx<'_>) -> Section {
let out = Section::new().with_topic("cbor_parse").try_build_child(|| {
let cbor = hex::decode(raw)?;
let tx = MultiEraTx::decode(&cbor)?;

let child = Section::new()
.with_topic("tx")
.with_attr("era", tx.era())
Expand All @@ -231,6 +232,27 @@ pub fn parse(raw: String) -> Result<Section, Section> {

Ok(child)
});
out
}

Ok(out)
pub fn parse(raw: String) -> Result<(Section, Validations), Section> {
let res_cbor = hex::decode(raw);
match res_cbor {
Ok(cbor) => {
let res_mtx = MultiEraTx::decode(&cbor);
match res_mtx {
Ok(mtx) => Ok((create_cbor_structure(&mtx), validate(&mtx))),
Err(e) => {
let mut err = Section::new();
err.error = Some(e.to_string());
Err(err)
}
}
}
Err(e) => {
let mut err = Section::new();
err.error = Some(e.to_string());
Err(err)
}
}
}
8 changes: 8 additions & 0 deletions napi-pallas/src/validations/alonzo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use pallas::ledger::primitives::alonzo::MintedTx;

use crate::Validations;

pub fn validate_alonzo(mtx_a: &MintedTx) -> Validations {
let out = Validations::new();
out
}
22 changes: 22 additions & 0 deletions napi-pallas/src/validations/babbage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use crate::{Validation, Validations};
use pallas::{
applying::babbage::check_ins_not_empty,
ledger::primitives::babbage::{MintedTransactionBody, MintedTx as BabbageMintedTx},
};

use super::validate::set_description;

fn validate_babbage_ins_not_empty(mtx: &BabbageMintedTx) -> Validation {
let tx_body: &MintedTransactionBody = &mtx.transaction_body.clone();
let res = check_ins_not_empty(tx_body);
let description = set_description(&res, "Inputs are not empty".to_string());
return Validation::new()
.with_name("Non empty inputs".to_string())
.with_value(res.is_ok())
.with_description(description);
}

pub fn validate_babbage(mtx_b: &BabbageMintedTx) -> Validations {
let out = Validations::new().add_new_validation(validate_babbage_ins_not_empty(&mtx_b));
out
}
6 changes: 6 additions & 0 deletions napi-pallas/src/validations/byron.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use crate::Validations;
use pallas::ledger::primitives::byron::MintedTxPayload;
pub fn validate_byron(mtxp: &MintedTxPayload) -> Validations {
let out = Validations::new();
out
}
7 changes: 7 additions & 0 deletions napi-pallas/src/validations/conway.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use pallas::ledger::primitives::conway::MintedTx;

use crate::Validations;
pub fn validate_conway(mtx_c: &MintedTx) -> Validations {
let out = Validations::new();
out
}
6 changes: 6 additions & 0 deletions napi-pallas/src/validations/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pub mod alonzo;
pub mod babbage;
pub mod byron;
pub mod conway;
pub mod shelley_ma;
pub mod validate;
8 changes: 8 additions & 0 deletions napi-pallas/src/validations/shelley_ma.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use pallas::ledger::primitives::alonzo::MintedTx;

use crate::Validations;

pub fn validate_shelley_ma(mtx_sma: &MintedTx) -> Validations {
let out = Validations::new();
out
}
31 changes: 31 additions & 0 deletions napi-pallas/src/validations/validate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use crate::validations::babbage::validate_babbage;
use crate::Validations;

use pallas::applying::utils::ValidationError;
use pallas::ledger::traverse::{Era, MultiEraTx};

use super::alonzo::validate_alonzo;
use super::byron::validate_byron;
use super::conway::validate_conway;
use super::shelley_ma::validate_shelley_ma;

pub fn set_description(res: &Result<(), ValidationError>, success: String) -> String {
match res {
Ok(_) => success,
Err(e) => format!("Error {:?}", e),
}
}

pub fn validate(mtx: &MultiEraTx<'_>) -> Validations {
match &mtx {
MultiEraTx::Byron(mtxp) => validate_byron(&mtxp),
MultiEraTx::AlonzoCompatible(mtx_sma, Era::Shelley)
| MultiEraTx::AlonzoCompatible(mtx_sma, Era::Allegra)
| MultiEraTx::AlonzoCompatible(mtx_sma, Era::Mary) => validate_shelley_ma(&mtx_sma),
MultiEraTx::AlonzoCompatible(mtx_a, Era::Alonzo) => validate_alonzo(&mtx_a),
MultiEraTx::Babbage(mtx_b) => validate_babbage(&mtx_b),
MultiEraTx::Conway(mtx_c) => validate_conway(&mtx_c),
// This case is impossible. TODO: Handle error
_ => Validations::new(),
}
}
39 changes: 29 additions & 10 deletions web/app/components.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Attribute, type Section } from "napi-pallas";
import { PropsWithChildren, useState } from "react";
import { IValidation } from "./routes/tx";
import { DataProps, IValidation } from "./routes/tx";

export type TopicMeta = {
title: string;
Expand Down Expand Up @@ -53,18 +53,37 @@ export const P1 = Paragraph;
export function RootSection(props: {
data: Section;
topics: Record<string, TopicMeta>;
validations: IValidation[];
}) {
const [open, setOpen] = useState(false);
const handleClick = () => setOpen(!open);
const topic = getTopicMeta(props.data.topic, props.topics);

if (props.data.error)
return (
<div className="block mt-8 p-4 border-2 bg-red-200 border-red-700 shadow shadow-black rounded-lg text-2xl">
<h4 className="text-3xl">{topic.description}</h4>
{props.data.error}
</div>
);

return (
<>
<div className="flex flex-col">
<div className="mb-14">
<button
className={`flex items-center w-full text-left select-none duration-300`}
onClick={handleClick}
>
<div
className={`h-8 w-8 inline-flex items-center justify-center duration-300 `}
>
{open ? "▼" : "▶"}
</div>
<h4 className="text-3xl ">Tx Validations</h4>
</button>
{open && <ValidationAccordion validations={props.validations} />}
</div>
<h4 className="text-3xl">{topic.title}</h4>
{!props.data.error && topic.description}
{!!props.data.error && (
<div className="block mt-8 p-4 border-2 bg-red-200 border-red-700 shadow shadow-black rounded-lg text-2xl">
{props.data.error}
</div>
)}
{!!props.data.bytes && (
<HexBlock name="bytes (hex)" value={props.data.bytes} />
)}
Expand All @@ -74,7 +93,7 @@ export function RootSection(props: {
{props.data.children?.map((c) => (
<DataSection key={c.identity} data={c} topics={props.topics} />
))}
</>
</div>
);
}

Expand Down Expand Up @@ -159,7 +178,7 @@ export function TextArea(props: { name: string; placeholder?: string }) {
);
}

export function logCuriosity(data: any) {
export function logCuriosity(data: DataProps) {
if (data) {
console.group("CURIOUS FELLOW, EH?");
console.log("hello there! want to learn how we parse the data?");
Expand Down
2 changes: 1 addition & 1 deletion web/app/routes/tx.server.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { type Section, safeParseTx } from "napi-pallas";
export { safeParseTx, type Section } from "napi-pallas";
Loading
Loading