-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
df40e59
commit dcf2558
Showing
7 changed files
with
664 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,373 @@ | ||
use crate::allocator::{Allocator, NodePtr}; | ||
use crate::cost::{check_cost, Cost}; | ||
use crate::node::Node; | ||
use crate::number::number_from_u8; | ||
use crate::op_utils::{ | ||
arg_count, atom, check_arg_count, int_atom, mod_group_order, new_atom_and_cost, | ||
number_to_scalar, | ||
}; | ||
use crate::reduction::EvalErr; | ||
use crate::reduction::Response; | ||
use bls12_381::hash_to_curve::{ExpandMsgXmd, HashToCurve}; | ||
use bls12_381::{ | ||
multi_miller_loop, G1Affine, G1Projective, G2Affine, G2Prepared, G2Projective, Gt, | ||
}; | ||
|
||
const BLS_G1_SUBTRACT_BASE_COST: Cost = 132332; | ||
const BLS_G1_SUBTRACT_COST_PER_ARG: Cost = 1362553; | ||
const BLS_G1_MULTIPLY_BASE_COST: Cost = 2154347; | ||
const BLS_G1_MULTIPLY_COST_PER_BYTE: Cost = 12; | ||
const BLS_G1_NEGATE_BASE_COST: Cost = 470779; | ||
const BLS_G2_ADD_BASE_COST: Cost = 45440; | ||
const BLS_G2_ADD_COST_PER_ARG: Cost = 5544581; | ||
const BLS_G2_SUBTRACT_BASE_COST: Cost = 146290; | ||
const BLS_G2_SUBTRACT_COST_PER_ARG: Cost = 5495272; | ||
const BLS_G2_MULTIPLY_BASE_COST: Cost = 10078145; | ||
const BLS_G2_MULTIPLY_COST_PER_BYTE: Cost = 12; | ||
const BLS_G2_NEGATE_BASE_COST: Cost = 1881699; | ||
const BLS_GT_ADD_BASE_COST: Cost = 60118; | ||
const BLS_GT_ADD_COST_PER_ARG: Cost = 62655353; | ||
const BLS_GT_SUBTRACT_BASE_COST: Cost = 42927; | ||
const BLS_GT_SUBTRACT_COST_PER_ARG: Cost = 63060911; | ||
const BLS_GT_MULTIPLY_BASE_COST: Cost = 34026598; | ||
const BLS_GT_MULTIPLY_COST_PER_BYTE: Cost = 12; | ||
const BLS_GT_NEGATE_BASE_COST: Cost = 21787950; | ||
const BLS_PAIRING_BASE_COST: Cost = 4999087; | ||
const BLS_PAIRING_COST_PER_ARG: Cost = 4515438; | ||
const BLS_MAP_TO_G1_BASE_COST: Cost = 610907; | ||
const BLS_MAP_TO_G1_COST_PER_BYTE: Cost = 122; | ||
const BLS_MAP_TO_G1_COST_PER_DST_BYTE: Cost = 135; | ||
const BLS_MAP_TO_G2_BASE_COST: Cost = 3380023; | ||
const BLS_MAP_TO_G2_COST_PER_BYTE: Cost = 122; | ||
const BLS_MAP_TO_G2_COST_PER_DST_BYTE: Cost = 135; | ||
|
||
// TODO: add unit test | ||
fn g1_atom(node: Node) -> Result<G1Affine, EvalErr> { | ||
let blob = atom(node.clone(), "G1 atom")?; | ||
if blob.len() != 48 { | ||
return node.err(&format!( | ||
"atom is not G1 size, got {}: Length of bytes object not equal to 48", | ||
hex::encode(blob) | ||
)); | ||
} | ||
|
||
match G1Affine::from_compressed(blob.try_into().expect("G1 slice is not 48 bytes")).into() { | ||
Some(point) => Ok(point), | ||
_ => node.err("atom is not a G1 point"), | ||
} | ||
} | ||
|
||
// TODO: add unit test | ||
fn g2_atom(node: Node) -> Result<G2Affine, EvalErr> { | ||
let blob = atom(node.clone(), "G2 atom")?; | ||
if blob.len() != 96 { | ||
return node.err(&format!( | ||
"atom is not G2 size, got {}: Length of bytes object not equal to 96", | ||
hex::encode(blob) | ||
)); | ||
} | ||
|
||
match G2Affine::from_compressed(blob.try_into().expect("G2 slice is not 96 bytes")).into() { | ||
Some(point) => Ok(point), | ||
_ => node.err("atom is not a G2 point"), | ||
} | ||
} | ||
|
||
// TODO: add unit test | ||
fn gt_atom(node: Node) -> Result<Gt, EvalErr> { | ||
let blob = atom(node.clone(), "Gt atom")?; | ||
if blob.len() != 288 { | ||
return node.err(&format!( | ||
"atom is not Gt size, got {}: Length of bytes object not equal to 288", | ||
hex::encode(blob) | ||
)); | ||
} | ||
|
||
match Gt::from_compressed(blob.try_into().expect("Gt slice is not 288 bytes")).into() { | ||
Some(point) => Ok(point), | ||
_ => node.err(&format!("atom is not a Gt point {}", hex::encode(blob))), | ||
} | ||
} | ||
|
||
pub fn op_bls_g1_subtract(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response { | ||
let args = Node::new(a, input); | ||
let mut cost = BLS_G1_SUBTRACT_BASE_COST; | ||
check_cost(a, cost, max_cost)?; | ||
let mut total: G1Projective = G1Projective::identity(); | ||
let mut is_first = true; | ||
for arg in &args { | ||
let point = g1_atom(arg)?; | ||
cost += BLS_G1_SUBTRACT_COST_PER_ARG; | ||
check_cost(a, cost, max_cost)?; | ||
if is_first { | ||
total = G1Projective::from(point); | ||
} else { | ||
total -= point; | ||
}; | ||
is_first = false; | ||
} | ||
let total: G1Affine = total.into(); | ||
new_atom_and_cost(a, cost, &total.to_compressed()) | ||
} | ||
|
||
pub fn op_bls_g1_multiply(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response { | ||
let args = Node::new(a, input); | ||
check_arg_count(&args, 2, "bls_g1_multiply")?; | ||
|
||
let mut cost = BLS_G1_MULTIPLY_BASE_COST; | ||
check_cost(a, cost, max_cost)?; | ||
|
||
let mut total = G1Projective::from(g1_atom(args.first()?)?); | ||
let args = args.rest()?; | ||
let scalar_buf = int_atom(args.first()?, "bls_g1_multiply")?; | ||
cost += scalar_buf.len() as Cost * BLS_G1_MULTIPLY_COST_PER_BYTE; | ||
check_cost(a, cost, max_cost)?; | ||
|
||
total *= number_to_scalar(mod_group_order(number_from_u8(scalar_buf))); | ||
|
||
let total: G1Affine = total.into(); | ||
new_atom_and_cost(a, cost, &total.to_compressed()) | ||
} | ||
|
||
pub fn op_bls_g1_negate(a: &mut Allocator, input: NodePtr, _max_cost: Cost) -> Response { | ||
let args = Node::new(a, input); | ||
check_arg_count(&args, 1, "bls_g1_negate")?; | ||
let point = g1_atom(args.first()?)?; | ||
let total = -point; | ||
new_atom_and_cost(a, BLS_G1_NEGATE_BASE_COST, &total.to_compressed()) | ||
} | ||
|
||
pub fn op_bls_g2_add(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response { | ||
let args = Node::new(a, input); | ||
let mut cost = BLS_G2_ADD_BASE_COST; | ||
let mut total: G2Projective = G2Projective::identity(); | ||
for arg in &args { | ||
let point = g2_atom(arg)?; | ||
cost += BLS_G2_ADD_COST_PER_ARG; | ||
check_cost(a, cost, max_cost)?; | ||
total += &point; | ||
} | ||
let total: G2Affine = total.into(); | ||
new_atom_and_cost(a, cost, &total.to_compressed()) | ||
} | ||
|
||
pub fn op_bls_g2_subtract(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response { | ||
let args = Node::new(a, input); | ||
let mut cost = BLS_G2_SUBTRACT_BASE_COST; | ||
let mut total: G2Projective = G2Projective::identity(); | ||
let mut is_first = true; | ||
for arg in &args { | ||
let point = g2_atom(arg)?; | ||
cost += BLS_G2_SUBTRACT_COST_PER_ARG; | ||
check_cost(a, cost, max_cost)?; | ||
if is_first { | ||
total = G2Projective::from(point); | ||
} else { | ||
total -= point; | ||
}; | ||
is_first = false; | ||
} | ||
let total: G2Affine = total.into(); | ||
new_atom_and_cost(a, cost, &total.to_compressed()) | ||
} | ||
|
||
pub fn op_bls_g2_multiply(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response { | ||
let args = Node::new(a, input); | ||
check_arg_count(&args, 2, "op_bls_g2_multiply")?; | ||
|
||
let mut cost = BLS_G2_MULTIPLY_BASE_COST; | ||
check_cost(a, cost, max_cost)?; | ||
|
||
let mut total = G2Projective::from(g2_atom(args.first()?)?); | ||
let args = args.rest()?; | ||
let scalar_buf = int_atom(args.first()?, "bls_g2_multiply")?; | ||
cost += scalar_buf.len() as Cost * BLS_G2_MULTIPLY_COST_PER_BYTE; | ||
check_cost(a, cost, max_cost)?; | ||
|
||
total *= number_to_scalar(mod_group_order(number_from_u8(scalar_buf))); | ||
|
||
let total: G2Affine = total.into(); | ||
new_atom_and_cost(a, cost, &total.to_compressed()) | ||
} | ||
|
||
pub fn op_bls_g2_negate(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response { | ||
let args = Node::new(a, input); | ||
check_arg_count(&args, 1, "bls_g2_negate")?; | ||
let cost = BLS_G2_NEGATE_BASE_COST; | ||
check_cost(a, cost, max_cost)?; | ||
let point = g2_atom(args.first()?)?; | ||
let total = -point; | ||
new_atom_and_cost(a, cost, &total.to_compressed()) | ||
} | ||
|
||
pub fn op_bls_map_to_g1(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response { | ||
let args = Node::new(a, input); | ||
let ac = arg_count(&args, 2); | ||
if !(1..=2).contains(&ac) { | ||
return args.err("bls_map_to_g1 takes exactly 1 or 2 arguments"); | ||
} | ||
let mut cost: Cost = BLS_MAP_TO_G1_BASE_COST; | ||
|
||
let msg = atom(args.first()?, "bls_map_to_g1")?; | ||
let args = args.rest()?; | ||
cost += msg.len() as Cost * BLS_MAP_TO_G1_COST_PER_BYTE; | ||
check_cost(a, cost, max_cost)?; | ||
|
||
let dst: &[u8] = if ac == 2 { | ||
atom(args.first()?, "bls_map_to_g1")? | ||
} else { | ||
b"BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_" | ||
}; | ||
|
||
cost += dst.len() as Cost * BLS_MAP_TO_G1_COST_PER_DST_BYTE; | ||
check_cost(a, cost, max_cost)?; | ||
|
||
let point = <G1Projective as HashToCurve<ExpandMsgXmd<sha2::Sha256>>>::hash_to_curve(msg, dst); | ||
new_atom_and_cost(a, cost, &G1Affine::from(point).to_compressed()) | ||
} | ||
|
||
pub fn op_bls_map_to_g2(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response { | ||
let args = Node::new(a, input); | ||
let ac = arg_count(&args, 2); | ||
if !(1..=2).contains(&ac) { | ||
return args.err("bls_map_to_g2 takes exactly 1 or 2 arguments"); | ||
} | ||
let mut cost: Cost = BLS_MAP_TO_G2_BASE_COST; | ||
check_cost(a, cost, max_cost)?; | ||
|
||
let msg = atom(args.first()?, "bls_map_to_g2")?; | ||
let args = args.rest()?; | ||
cost += msg.len() as Cost * BLS_MAP_TO_G2_COST_PER_BYTE; | ||
|
||
let dst: &[u8] = if ac == 2 { | ||
atom(args.first()?, "bls_map_to_g2")? | ||
} else { | ||
b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_" | ||
}; | ||
|
||
cost += dst.len() as Cost * BLS_MAP_TO_G2_COST_PER_DST_BYTE; | ||
check_cost(a, cost, max_cost)?; | ||
|
||
let point = <G2Projective as HashToCurve<ExpandMsgXmd<sha2::Sha256>>>::hash_to_curve(msg, dst); | ||
new_atom_and_cost(a, cost, &G2Affine::from(point).to_compressed()) | ||
} | ||
|
||
pub fn op_bls_gt_add(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response { | ||
let args = Node::new(a, input); | ||
let mut cost = BLS_GT_ADD_BASE_COST; | ||
let mut total: Gt = Gt::identity(); | ||
for arg in &args { | ||
let point = gt_atom(arg)?; | ||
cost += BLS_GT_ADD_COST_PER_ARG; | ||
check_cost(a, cost, max_cost)?; | ||
total += &point; | ||
} | ||
new_atom_and_cost(a, cost, &total.to_compressed()) | ||
} | ||
|
||
pub fn op_bls_gt_subtract(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response { | ||
let args = Node::new(a, input); | ||
let mut cost = BLS_GT_SUBTRACT_BASE_COST; | ||
let mut total: Gt = Gt::identity(); | ||
let mut is_first = true; | ||
for arg in &args { | ||
let point = gt_atom(arg)?; | ||
cost += BLS_GT_SUBTRACT_COST_PER_ARG; | ||
check_cost(a, cost, max_cost)?; | ||
if is_first { | ||
total = point; | ||
} else { | ||
total -= point; | ||
}; | ||
is_first = false; | ||
} | ||
new_atom_and_cost(a, cost, &total.to_compressed()) | ||
} | ||
|
||
pub fn op_bls_gt_multiply(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response { | ||
let args = Node::new(a, input); | ||
check_arg_count(&args, 2, "op_bls_gt_multiply")?; | ||
let mut cost = BLS_GT_MULTIPLY_BASE_COST; | ||
check_cost(a, cost, max_cost)?; | ||
|
||
let mut total = Gt::from(gt_atom(args.first()?)?); | ||
let args = args.rest()?; | ||
|
||
let scalar_buf = int_atom(args.first()?, "bls_gt_multiply")?; | ||
cost += scalar_buf.len() as Cost * BLS_GT_MULTIPLY_COST_PER_BYTE; | ||
check_cost(a, cost, max_cost)?; | ||
|
||
total *= number_to_scalar(mod_group_order(number_from_u8(scalar_buf))); | ||
|
||
new_atom_and_cost(a, cost, &total.to_compressed()) | ||
} | ||
|
||
pub fn op_bls_gt_negate(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response { | ||
let args = Node::new(a, input); | ||
check_arg_count(&args, 1, "bls_gt_negate")?; | ||
let cost = BLS_GT_NEGATE_BASE_COST; | ||
check_cost(a, cost, max_cost)?; | ||
let point = gt_atom(args.first()?)?; | ||
let total = -point; | ||
new_atom_and_cost(a, cost, &total.to_compressed()) | ||
} | ||
|
||
// TODO: add unit test | ||
fn extract_point(points: Node) -> Result<(G1Affine, G2Prepared), EvalErr> { | ||
check_arg_count(&points, 2, "pairing")?; | ||
let p = g1_atom(points.first()?)?; | ||
let points = points.rest()?; | ||
let q = g2_atom(points.first()?)?; | ||
Ok((p, G2Prepared::from(q))) | ||
} | ||
|
||
// TODO: add unit test | ||
fn extract_points( | ||
mut args: Node, | ||
max_cost: u64, | ||
) -> Result<(Vec<(G1Affine, G2Prepared)>, u64), EvalErr> { | ||
let mut cost = 0; | ||
let mut items = Vec::<(G1Affine, G2Prepared)>::new(); | ||
|
||
while !args.nullp() { | ||
cost += BLS_PAIRING_COST_PER_ARG; | ||
check_cost(&Allocator::new(), cost, max_cost)?; | ||
items.push(extract_point(args.first()?)?); | ||
args = args.rest()?; | ||
} | ||
|
||
if items.is_empty() { | ||
return args.err("bls_pairing expects a non-empty list of pairs"); | ||
} | ||
|
||
Ok((items, cost)) | ||
} | ||
|
||
// This function accepts either two parameters, G1 and G2 and treats them as a single item | ||
// or a (single) list of pairs (G1Point G2Point) | ||
pub fn op_bls_pairing(a: &mut Allocator, input: NodePtr, max_cost: Cost) -> Response { | ||
let args = Node::new(a, input); | ||
let mut cost = BLS_PAIRING_BASE_COST; | ||
let mut items = Vec::<(G1Affine, G2Prepared)>::new(); | ||
let ac = arg_count(&args, 2); | ||
|
||
if ac == 1 { | ||
let (points, additional_cost) = extract_points(args.first()?, max_cost - cost)?; | ||
cost += additional_cost; | ||
check_cost(&Allocator::new(), cost, max_cost)?; | ||
items.extend(points); | ||
} else if ac == 2 { | ||
cost += BLS_PAIRING_COST_PER_ARG; | ||
check_cost(a, cost, max_cost)?; | ||
items.push(extract_point(args)?); | ||
} else { | ||
return args.err("bls_pairing takes exactly 1 list of pairs or 2 atoms"); | ||
} | ||
|
||
let mut item_refs = Vec::<(&G1Affine, &G2Prepared)>::new(); | ||
for (p, q) in &items { | ||
item_refs.push((p, q)); | ||
} | ||
let total = multi_miller_loop(&item_refs).final_exponentiation(); | ||
new_atom_and_cost(a, cost, &total.to_compressed()) | ||
} |
Oops, something went wrong.