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

πŸ• Add BitVM-friendly BigInt Multiplication #22

Merged
merged 12 commits into from
Oct 28, 2024
Merged
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ members = [
"bitcoin-winternitz",
"bitcoin-scriptexec",
"bitcoin-testscripts",
"core"
, "bitcoin-utils", "nero-cli"]
"core",
"bitcoin-utils",
"nero-cli",
]
resolver = "2"

[workspace.dependencies]
Expand Down
23 changes: 16 additions & 7 deletions bitcoin-splitter/src/split/script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use bitcoin_utils::{comparison::OP_LONGEQUALVERIFY, stack_to_script, treepp::*};

/// Structure that represents a pair of input and output scripts. Typically, the prover
/// wants to prove `script(input) == output`
pub struct IOPair<const INPUT_SIZE: usize, const OUTPUT_SIZE: usize> {
pub struct IOPair {
/// Input script containing the elements which will be fed to the main script
pub input: Script,
/// Output script containing the elements which will be compared to the output of the main script
Expand Down Expand Up @@ -130,9 +130,16 @@ impl SplitResult {
///
/// **WARNING**: This function is used for testing purposes only, DO NOT ever try to use it in production code.
pub fn distort(&self) -> (Self, usize) {
// Choosing a random shard to distort
let distorted_shard_id = rand::random::<usize>() % self.shards.len();

// Getting the current stack (if it is empty, we cannot distort it)
let current_stack = self.intermediate_states[distorted_shard_id].stack.clone();
assert!(!current_stack.is_empty(), "Stack must not be empty");

// Distortion works very simply: take the last element of the stack
// and change it to OP_0. This way, the size of the stack will be the same,
// but with overwhemling probability, the script will be incorrect.
let mut new_split_result = self.clone();
new_split_result.intermediate_states[distorted_shard_id].stack = {
// Executing a random script and getting the stack
Expand All @@ -149,21 +156,23 @@ impl SplitResult {
}

/// Trait that any script that can be split should implement
pub trait SplitableScript<const INPUT_SIZE: usize, const OUTPUT_SIZE: usize> {
const INPUT_SIZE: usize = INPUT_SIZE;
const OUTPUT_SIZE: usize = OUTPUT_SIZE;
pub trait SplitableScript {
/// Number of limbs to represent the input to the script
const INPUT_SIZE: usize;
/// Number of limbs to represent the output of the script
const OUTPUT_SIZE: usize;

/// Returns the main logic (f) of the script
fn script() -> Script;

/// Generates a random valid input for the script
fn generate_valid_io_pair() -> IOPair<INPUT_SIZE, OUTPUT_SIZE>;
fn generate_valid_io_pair() -> IOPair;

/// Genreates invalid input for the script
///
/// NOTE: This function is used for testing purposes, specifically
/// for testing the **Disprove** script.
fn generate_invalid_io_pair() -> IOPair<INPUT_SIZE, OUTPUT_SIZE>;
fn generate_invalid_io_pair() -> IOPair;

/// Verifies that the input is valid for the script
fn verify(input: Script, output: Script) -> bool {
Expand All @@ -175,7 +184,7 @@ pub trait SplitableScript<const INPUT_SIZE: usize, const OUTPUT_SIZE: usize> {
// Now, we need to verify that the output is correct.
// Since the output is not necessarily a single element, we check
// elements one by one
{ OP_LONGEQUALVERIFY(OUTPUT_SIZE) }
{ OP_LONGEQUALVERIFY(Self::OUTPUT_SIZE) }

// If everything was verified correctly, we return true to mark the script as successful
OP_TRUE
Expand Down
1 change: 1 addition & 0 deletions bitcoin-testscripts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ bitcoin-script = { git = "https://github.com/BitVM/rust-bitcoin-script" }

# BitVM scripts
bitcoin-window-mul = { git = "https://github.com/distributed-lab/bitcoin-window-mul.git" }
# bitcoin-window-mul = { path = "../../../alpen/bitcoin-window-mul" }
bitcoin-splitter = { path = "../bitcoin-splitter" }
bitcoin-utils = { path = "../bitcoin-utils" }

Expand Down
58 changes: 58 additions & 0 deletions bitcoin-testscripts/sage/window_decomposition.sage
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# This script is used to verify the logic of decomposing an integer
# into a window width representation and then reconstructing the integer
# from the window width representation.

import random

N_BITS: Integer = 254 # Number of bits in the integer
LIMB_SIZE: Integer = 30 # Number of bits in a limb
WINDOW_SIZE: Integer = 3 # Window size

def to_limbs(a: Integer, limb_size: Integer) -> list[Integer]:
"""
Converts the given integer a into a list of 254-bit limbs
"""

limbs = []
while a >= 1:
c = a % (1 << limb_size)
limbs.append(c)
a = a - c
a = a // (1 << limb_size)

return limbs

def chunks(lst, n):
"""
Yield successive n-sized chunks from lst
"""

for i in range(0, len(lst), n):
yield lst[i:i+n]

def recover_to_limbs(window_decomposition: list[Integer], width: Integer, limb_size: Integer) -> list[Integer]:
"""
Recovers the integer in the limb format from the window decomposition
"""

assert limb_size % width == 0, "limb size must be a multiple of the window width"
chunk_size = limb_size // width

return [sum([x*(1<<(i*width)) for i, x in enumerate(chunk)]) for chunk in chunks(window_decomposition, chunk_size)]

# Pick a random integer
a = Integer(random.randint(0, 1<<N_BITS))
print(f"Random integer: {a}")

# Show the limbs
limbs = to_limbs(a, LIMB_SIZE)
print(f"Limbs: {limbs}")

# Show the window decomposition
window_decomposition = to_limbs(a, WINDOW_SIZE)
print(f"Window decomposition: {window_decomposition}")

# Show the recovered limbs
print(f"Recover: {recover_to_limbs(window_decomposition, WINDOW_SIZE, LIMB_SIZE)}")


Loading