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

Transaction Validation #736

Merged
merged 24 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f224a72
Use i16::MAX instead of u16::MAX for maximum inputs/outputs in a tran…
SethDusek Dec 28, 2023
861b3e8
Add stateless validation for transactions
SethDusek Dec 28, 2023
996d293
Add ArbBoxParameters for ErgoBox/ErgoBoxCandidate proptests
SethDusek Jan 17, 2024
9c9d326
Add Parameters to ErgoStateContext
SethDusek Jan 30, 2024
abac93a
Stateful Transaction Validation
SethDusek Jan 30, 2024
17c6a1e
Add documentation for Parameters/TxValidationError
SethDusek Feb 3, 2024
ece67fe
Add monotonic height validation
SethDusek Feb 5, 2024
f345e7f
Add Parameters to WASM/C bindings
SethDusek Feb 5, 2024
2d06ca2
Ignore miette set_hook errors
SethDusek Feb 13, 2024
8aeed0a
Fix ByteArrayToLong for certain inputs
SethDusek Feb 23, 2024
18c1021
Fix indexOf implementation
SethDusek Mar 4, 2024
ce182a1
sig-serializer: Support reading scalars in signature < 32 bytes
SethDusek Mar 5, 2024
32f595e
Add additional byteArrayToLong test
SethDusek Mar 5, 2024
60fd0c7
Add CONTEXT.minerPubkey method
SethDusek Mar 5, 2024
ffb8989
Relax flatMap restrictions + fix inference bugs
SethDusek Mar 8, 2024
fc88669
Handle negative from/replaced in Coll.patch
SethDusek Mar 9, 2024
b6d7dca
perf: Fix quadratic TransactionContext::new creation
SethDusek Mar 10, 2024
4e5e393
Add Storage rent support in transaction validation
SethDusek Mar 13, 2024
5b15f57
perf: re-use bytes_to_sign() in verify_tx_input_proof
SethDusek Mar 13, 2024
392f2ac
Add TX validation bindings
SethDusek Mar 14, 2024
66b15c1
support atLeast(1, ...)
SethDusek Mar 15, 2024
3f98116
Fix doc comments + merge TxValidationError/TxVerifyError
SethDusek Mar 16, 2024
517f683
Fix flakey transaction validation tests
SethDusek Mar 16, 2024
0fdf2d2
Clippy + update documentation for TxValidationError::BoxSizeExceeded
SethDusek May 4, 2024
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
4 changes: 4 additions & 0 deletions bindings/ergo-lib-c-core/src/ergo_state_ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use ergo_lib::chain;
use crate::block_header::BlockHeader;
use crate::collections::ConstCollectionPtr;
use crate::header::PreHeader;
use crate::parameters::ConstParametersPtr;
use crate::util::const_ptr_as_ref;
use crate::Error;
use std::convert::TryInto;
Expand All @@ -18,10 +19,12 @@ pub type ConstErgoStateContextPtr = *const ErgoStateContext;
pub unsafe fn ergo_state_context_new(
pre_header_ptr: *const PreHeader,
headers: ConstCollectionPtr<BlockHeader>,
parameters_ptr: ConstParametersPtr,
ergo_state_context_out: *mut ErgoStateContextPtr,
) -> Result<(), Error> {
let pre_header = const_ptr_as_ref(pre_header_ptr, "pre_header_ptr")?;
let headers = const_ptr_as_ref(headers, "headers")?;
let parameters = const_ptr_as_ref(parameters_ptr, "parameters_ptr")?;
match headers.0.len() {
10 => {
*ergo_state_context_out = Box::into_raw(Box::new(ErgoStateContext(
Expand All @@ -35,6 +38,7 @@ pub unsafe fn ergo_state_context_new(
.collect::<Vec<_>>()
.try_into()
.unwrap(),
parameters.0.clone(),
),
)));
Ok(())
Expand Down
2 changes: 2 additions & 0 deletions bindings/ergo-lib-c-core/src/error_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::num::ParseIntError;

use base16::DecodeError;
use bounded_vec::BoundedVecOutOfBounds;
use ergo_lib::chain::transaction::ergo_transaction::TxValidationError;
use ergo_lib::chain::transaction::TransactionError;
use ergo_lib::ergo_chain_types::DigestNError;
use ergo_lib::ergo_nipopow::NipopowProofError;
Expand Down Expand Up @@ -52,6 +53,7 @@ convert_error!(BoxValueError);
convert_error!(TokenAmountError);
convert_error!(TxBuilderError);
convert_error!(TxSigningError);
convert_error!(TxValidationError);
convert_error!(WalletError);
convert_error!(DecodeError);
convert_error!(TryFromSliceError);
Expand Down
1 change: 1 addition & 0 deletions bindings/ergo-lib-c-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub mod input;
mod json;
pub mod merkleproof;
pub mod nipopow;
pub mod parameters;
pub mod reduced;
pub mod secret_key;
pub mod token;
Expand Down
22 changes: 22 additions & 0 deletions bindings/ergo-lib-c-core/src/parameters.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//! Ergo blockchain state (for ErgoTree evaluation)
use ergo_lib::chain;

/// Blockchain parameters
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct Parameters(pub(crate) chain::parameters::Parameters);
pub type ParametersPtr = *mut Parameters;
pub type ConstParametersPtr = *const Parameters;

/// Return default blockchain parameters that were set at genesis
pub unsafe fn parameters_default(parameters_out: *mut ParametersPtr) {
*parameters_out = Box::into_raw(Box::new(Parameters(
chain::parameters::Parameters::default(),
)));
}

pub unsafe fn parameters_delete(parameters: ParametersPtr) {
if !parameters.is_null() {
let boxed = Box::from_raw(parameters);
std::mem::drop(boxed);
}
}
22 changes: 22 additions & 0 deletions bindings/ergo-lib-c-core/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,3 +435,25 @@ pub unsafe fn tx_outputs(
)));
Ok(())
}

/// Validate a transaction
pub unsafe fn tx_validate(
tx_ptr: ConstTransactionPtr,
state_context_ptr: ConstErgoStateContextPtr,
boxes_to_spend_ptr: ConstCollectionPtr<ErgoBox>,
data_boxes_ptr: ConstCollectionPtr<ErgoBox>,
) -> Result<(), Error> {
let state_context = const_ptr_as_ref(state_context_ptr, "state_context_ptr")?;
let tx = const_ptr_as_ref(tx_ptr, "tx_ptr")?;
let boxes_to_spend = const_ptr_as_ref(boxes_to_spend_ptr, "boxes_to_spend_ptr")?;
let data_boxes = const_ptr_as_ref(data_boxes_ptr, "data_boxes_ptr")?;
let boxes_to_spend = boxes_to_spend.0.clone().into_iter().map(|b| b.0).collect();
let data_boxes = data_boxes.0.clone().into_iter().map(|b| b.0).collect();
let tx_context = ergo_lib::wallet::signing::TransactionContext::new(
tx.0.clone(),
boxes_to_spend,
data_boxes,
)?;
tx_context.validate(&state_context.0)?;
Ok(())
}
4 changes: 3 additions & 1 deletion bindings/ergo-lib-c/src/ergo_state_ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::{block_header::ConstBlockHeadersPtr, delete_ptr, ErrorPtr};
use ergo_lib_c_core::{
ergo_state_ctx::{ergo_state_context_new, ConstErgoStateContextPtr, ErgoStateContextPtr},
header::ConstPreHeaderPtr,
parameters::ConstParametersPtr,
Error,
};
use paste::paste;
Expand All @@ -12,9 +13,10 @@ use paste::paste;
pub unsafe extern "C" fn ergo_lib_ergo_state_context_new(
pre_header_ptr: ConstPreHeaderPtr,
headers: ConstBlockHeadersPtr,
parameters: ConstParametersPtr,
ergo_state_context_out: *mut ErgoStateContextPtr,
) -> ErrorPtr {
let res = ergo_state_context_new(pre_header_ptr, headers, ergo_state_context_out);
let res = ergo_state_context_new(pre_header_ptr, headers, parameters, ergo_state_context_out);
Error::c_api_from(res)
}

Expand Down
2 changes: 2 additions & 0 deletions bindings/ergo-lib-c/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ mod header;
mod input;
mod merkleproof;
mod nipopow;
mod parameters;

mod reduced;

Expand Down Expand Up @@ -63,6 +64,7 @@ pub use crate::header::*;
pub use crate::input::*;
pub use crate::merkleproof::*;
pub use crate::nipopow::*;
pub use crate::parameters::*;
pub use crate::reduced::*;
pub use crate::secret_key::*;
pub use crate::token::*;
Expand Down
14 changes: 14 additions & 0 deletions bindings/ergo-lib-c/src/parameters.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use ergo_lib_c_core::parameters::{parameters_default, ParametersPtr};

use crate::delete_ptr;

/// Return default blockchain parameters that were set at genesis
#[no_mangle]
pub unsafe extern "C" fn ergo_lib_parameters_default(parameters_out: *mut ParametersPtr) {
parameters_default(parameters_out);
}

#[no_mangle]
pub unsafe extern "C" fn ergo_lib_parameters_delete(parameters: ParametersPtr) {
delete_ptr(parameters)
}
14 changes: 14 additions & 0 deletions bindings/ergo-lib-c/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,20 @@ pub unsafe extern "C" fn ergo_lib_tx_to_json_eip12(
Error::c_api_from(res)
}

#[no_mangle]
pub unsafe extern "C" fn ergo_lib_tx_validate(
tx_ptr: ConstTransactionPtr,
state_context_ptr: ConstErgoStateContextPtr,
boxes_to_spend_ptr: ConstCollectionPtr<ErgoBox>,
data_boxes_ptr: ConstCollectionPtr<ErgoBox>,
) -> ErrorPtr {
Error::c_api_from(tx_validate(
tx_ptr,
state_context_ptr,
boxes_to_spend_ptr,
data_boxes_ptr,
))
}
/// Drop `Transaction`
#[no_mangle]
pub unsafe extern "C" fn ergo_lib_tx_delete(ptr: TransactionPtr) {
Expand Down
4 changes: 2 additions & 2 deletions bindings/ergo-lib-ios/Sources/ErgoLib/ErgoStateContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ class ErgoStateContext {
internal var pointer: ErgoStateContextPtr

/// Create new context
init(preHeader : PreHeader, headers: BlockHeaders) throws {
init(preHeader: PreHeader, headers: BlockHeaders, parameters: Parameters) throws {
var ptr: ErgoStateContextPtr?
let error = ergo_lib_ergo_state_context_new(preHeader.pointer, headers.pointer, &ptr)
let error = ergo_lib_ergo_state_context_new(preHeader.pointer, headers.pointer, parameters.pointer, &ptr)
try checkError(error)
self.pointer = ptr!
}
Expand Down
19 changes: 19 additions & 0 deletions bindings/ergo-lib-ios/Sources/ErgoLib/Parameters.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

import Foundation
import ErgoLibC

/// Blockchain parameters that can be changed by voting
class Parameters {
internal var pointer: ParametersPtr

/// Create default parameters
init() {
var ptr: ParametersPtr?
ergo_lib_parameters_default(&ptr)
self.pointer = ptr!
}

deinit {
ergo_lib_parameters_delete(self.pointer)
}
}
10 changes: 10 additions & 0 deletions bindings/ergo-lib-ios/Sources/ErgoLib/Transaction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,16 @@ class Transaction {
ergo_lib_tx_outputs(self.pointer, &ptr)
return ErgoBoxes(withRawPointer: ptr!)
}
/// Attempt to validate a transaction. throws an exception if validation fails
func validateTransaction(
stateContext: ErgoStateContext,
boxesToSpend: ErgoBoxes,
dataBoxes: ErgoBoxes
) throws {
let error = ergo_lib_tx_validate(self.pointer, stateContext.pointer, boxesToSpend.pointer, dataBoxes.pointer);
try checkError(error)
}


/// JSON representation as text (compatible with Ergo Node/Explorer API, numbers are encoded as numbers)
func toJSON() throws -> JSON? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ final class ErgoStateContextTests: XCTestCase {
let headerJSON = HeaderTests.jsonHeaderExample()
let blockHeader = try BlockHeader(withJson: headerJSON)
let preHeader = PreHeader(withBlockHeader: blockHeader)
let parameters = Parameters()
var blockHeadersJSON = Array(repeating: HeaderTests.jsonHeaderExample(), count: 10)
var blockHeaders = try BlockHeaders(fromJSON: blockHeadersJSON)
XCTAssertNoThrow(try ErgoStateContext(preHeader: preHeader, headers: blockHeaders))
XCTAssertNoThrow(try ErgoStateContext(preHeader: preHeader, headers: blockHeaders, parameters: parameters))

// Now test for incorrect number of block headers
blockHeadersJSON = Array(repeating: HeaderTests.jsonHeaderExample(), count: 8)
blockHeaders = try BlockHeaders(fromJSON: blockHeadersJSON)
XCTAssertThrowsError(try ErgoStateContext(preHeader: preHeader, headers: blockHeaders))
XCTAssertThrowsError(try ErgoStateContext(preHeader: preHeader, headers: blockHeaders, parameters: parameters))
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ final class TransactionTests: XCTestCase {
let txDataInputs = try ErgoBoxes(fromJSON: [])
let blockHeaders = try HeaderTests.generateBlockHeadersFromJSON()
let preHeader = PreHeader(withBlockHeader: blockHeaders.get(index: UInt(0))!)
let ctx = try ErgoStateContext(preHeader: preHeader, headers: blockHeaders)
let ctx = try ErgoStateContext(preHeader: preHeader, headers: blockHeaders, parameters: Parameters())
let secretKeys = SecretKeys()
secretKeys.add(secretKey: sk)
let wallet = Wallet(secrets: secretKeys)
Expand Down Expand Up @@ -224,7 +224,7 @@ final class TransactionTests: XCTestCase {
let txDataInputs = try ErgoBoxes(fromJSON: [])
let blockHeaders = try HeaderTests.generateBlockHeadersFromJSON()
let preHeader = PreHeader(withBlockHeader: blockHeaders.get(index: UInt(0))!)
let ctx = try ErgoStateContext(preHeader: preHeader, headers: blockHeaders)
let ctx = try ErgoStateContext(preHeader: preHeader, headers: blockHeaders, parameters: Parameters())
let secretKeys = SecretKeys()
secretKeys.add(secretKey: sk)
let wallet = Wallet(secrets: secretKeys)
Expand Down Expand Up @@ -358,7 +358,7 @@ final class TransactionTests: XCTestCase {
let txDataInputs = try ErgoBoxes.init(fromJSON: [])
let blockHeaders = try HeaderTests.generateBlockHeadersFromJSON()
let preHeader = PreHeader(withBlockHeader: blockHeaders.get(index: UInt(0))!)
let ctx = try ErgoStateContext(preHeader: preHeader, headers: blockHeaders)
let ctx = try ErgoStateContext(preHeader: preHeader, headers: blockHeaders, parameters: Parameters())
let sksAlice = SecretKeys()
sksAlice.add(secretKey: aliceSecret)
let walletAlice = Wallet(secrets: sksAlice)
Expand Down
14 changes: 12 additions & 2 deletions bindings/ergo-lib-wasm/src/ergo_state_ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use derive_more::{From, Into};

use crate::block_header::BlockHeaders;
use crate::header::PreHeader;
use crate::parameters::Parameters;

/// Blockchain state (last headers, etc.)
#[wasm_bindgen]
Expand All @@ -19,8 +20,17 @@ pub struct ErgoStateContext(pub(crate) chain::ergo_state_context::ErgoStateConte
impl ErgoStateContext {
/// Create new context from pre-header
#[wasm_bindgen(constructor)]
pub fn new(pre_header: PreHeader, headers: BlockHeaders) -> Result<ErgoStateContext, JsValue> {
pub fn new(
pre_header: PreHeader,
headers: BlockHeaders,
parameters: Parameters,
) -> Result<ErgoStateContext, JsValue> {
let headers = Headers::try_from(headers)?;
Ok(chain::ergo_state_context::ErgoStateContext::new(pre_header.into(), headers).into())
Ok(chain::ergo_state_context::ErgoStateContext::new(
pre_header.into(),
headers,
parameters.into(),
)
.into())
}
}
4 changes: 2 additions & 2 deletions bindings/ergo-lib-wasm/src/error_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use std::num::ParseIntError;

use base16::DecodeError;
use bounded_vec::BoundedVecOutOfBounds;
use ergo_lib::chain::transaction::ergo_transaction::TxValidationError;
use ergo_lib::chain::transaction::TransactionSignatureVerificationError;
use ergo_lib::chain::transaction::TxVerifyError;
use ergo_lib::ergo_chain_types::DigestNError;
use ergo_lib::ergo_nipopow::NipopowProofError;
#[cfg(feature = "rest")]
Expand Down Expand Up @@ -96,7 +96,7 @@ from_error_to_wrap!(PeerDiscoveryError);
from_error_to_wrap!(ParseError);
from_error_to_wrap!(ConvError);
from_error_to_wrap!(TransactionContextError);
from_error_to_wrap!(TxVerifyError);
from_error_to_wrap!(TxValidationError);
from_error_to_wrap!(RegisterValueError);
from_error_to_wrap!(String);

Expand Down
1 change: 1 addition & 0 deletions bindings/ergo-lib-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub mod header;
pub mod input;
pub mod merkleproof;
pub mod nipopow;
pub mod parameters;

pub mod prover_result;
pub mod secret_key;
Expand Down
55 changes: 55 additions & 0 deletions bindings/ergo-lib-wasm/src/parameters.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//! Blockchain parameters. This module defines adjustable blockchain parameters that can be voted on by miners

use ergo_lib::chain::parameters;
use wasm_bindgen::prelude::*;
extern crate derive_more;
use derive_more::{From, Into};

/// Blockchain parameters
#[wasm_bindgen]
#[derive(PartialEq, Debug, Clone, Eq, From, Into)]
pub struct Parameters(pub(crate) parameters::Parameters);

#[wasm_bindgen]
impl Parameters {
/// Return default blockchain parameters that were set at genesis
pub fn default_parameters() -> Parameters {
parameters::Parameters::default().into()
}
/// Get current block version
pub fn block_version(&self) -> i32 {
self.0.block_version()
}
/// Cost of storing 1 byte per Storage Period of block chain
pub fn storage_fee_factor(&self) -> i32 {
self.0.storage_fee_factor()
}
/// Minimum value per byte an output must have to not be considered dust
pub fn min_value_per_byte(&self) -> i32 {
self.0.min_value_per_byte()
}
/// Maximum size of transactions size in a block
pub fn max_block_size(&self) -> i32 {
self.0.max_block_size()
}
/// Maximum total computation cost in a block
pub fn max_block_cost(&self) -> i32 {
self.0.max_block_cost()
}
/// Cost of accessing a single token
pub fn token_access_cost(&self) -> i32 {
self.0.token_access_cost()
}
/// Validation cost per one transaction input
pub fn input_cost(&self) -> i32 {
self.0.input_cost()
}
/// Validation cost per data input
pub fn data_input_cost(&self) -> i32 {
self.0.data_input_cost()
}
/// Validation cost per one output
pub fn output_cost(&self) -> i32 {
self.0.output_cost()
}
}
Loading
Loading