Skip to content

Commit e726dcd

Browse files
committed
Add powHit method
1 parent 97fc8bf commit e726dcd

File tree

5 files changed

+128
-15
lines changed

5 files changed

+128
-15
lines changed

ergo-chain-types/src/autolykos_pow_scheme.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,10 @@ pub struct AutolykosPowScheme {
101101

102102
impl AutolykosPowScheme {
103103
/// Create a new `AutolykosPowScheme`. Returns None if k is not >= 2 && <= 32 or big_n is not >= 16
104-
pub fn new(k: u64, big_n: u32) -> Option<Self> {
105-
let k = BoundedU64::new(k)?;
106-
let big_n = BoundedU32::new(big_n)?;
107-
Some(Self {
104+
pub fn new(k: u64, big_n: u32) -> Result<Self, AutolykosPowSchemeError> {
105+
let k = BoundedU64::new(k).ok_or(AutolykosPowSchemeError::OutOfBounds)?;
106+
let big_n = BoundedU32::new(big_n).ok_or(AutolykosPowSchemeError::OutOfBounds)?;
107+
Ok(Self {
108108
k,
109109
big_n_base: big_n,
110110
})
@@ -116,7 +116,7 @@ impl AutolykosPowScheme {
116116
msg: &[u8],
117117
nonce: &[u8],
118118
h: &[u8],
119-
big_n: usize,
119+
big_n: u32,
120120
) -> Result<BigUint, AutolykosPowSchemeError> {
121121
let seed_hash = self.calc_seed_v2(big_n, msg, nonce, h)?;
122122
let indexes = self.gen_indexes(&seed_hash, big_n);
@@ -171,7 +171,7 @@ impl AutolykosPowScheme {
171171
/// in ErgoPow paper.
172172
pub fn calc_seed_v2(
173173
&self,
174-
big_n: usize,
174+
big_n: u32,
175175
msg: &[u8],
176176
nonce: &[u8],
177177
header_height_bytes: &[u8],
@@ -201,7 +201,7 @@ impl AutolykosPowScheme {
201201
}
202202

203203
/// Returns a list of size `k` with numbers in [0,`N`)
204-
pub fn gen_indexes(&self, seed_hash: &[u8; 32], big_n: usize) -> Vec<u32> {
204+
pub fn gen_indexes(&self, seed_hash: &[u8; 32], big_n: u32) -> Vec<u32> {
205205
let mut res = vec![];
206206
let mut extended_hash: Vec<u8> = seed_hash.to_vec();
207207
extended_hash.extend(&seed_hash[..3]);
@@ -218,9 +218,9 @@ impl AutolykosPowScheme {
218218
}
219219

220220
/// Calculates table size (N value) for a given height (moment of time)
221-
pub fn calc_big_n(&self, header_version: u8, header_height: u32) -> usize {
221+
pub fn calc_big_n(&self, header_version: u8, header_height: u32) -> u32 {
222222
// Number of elements in a table to find k-sum problem solution on top of
223-
let n_base = self.big_n_base.get() as usize;
223+
let n_base = self.big_n_base.get();
224224
if header_version == 1 {
225225
n_base
226226
} else {
@@ -288,6 +288,9 @@ pub enum AutolykosPowSchemeError {
288288
/// Checking proof-of-work for AutolykosV1 is not supported
289289
#[error("Header.check_pow is not supported for Autolykos1")]
290290
Unsupported,
291+
/// k or N are out of bounds, see [`AutolykosPowScheme::new`]
292+
#[error("Arguments to AutolykosPowScheme::new were out of bounds")]
293+
OutOfBounds,
291294
}
292295

293296
/// The following tests are taken from <https://github.com/ergoplatform/ergo/blob/f7b91c0be00531c6d042c10a8855149ca6924373/src/test/scala/org/ergoplatform/mining/AutolykosPowSchemeSpec.scala#L43-L130>
@@ -302,7 +305,7 @@ mod tests {
302305
#[test]
303306
fn test_calc_big_n() {
304307
let pow = AutolykosPowScheme::default();
305-
let n_base = pow.big_n_base.get() as usize;
308+
let n_base = pow.big_n_base.get();
306309

307310
// autolykos v1
308311
assert_eq!(pow.calc_big_n(1, 700000), n_base);

ergotree-interpreter/src/eval.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,7 @@ fn smethod_eval_fn(method: &SMethod) -> Result<EvalFn, EvalError> {
361361
sglobal::SERIALIZE_METHOD_ID => self::sglobal::SERIALIZE_EVAL_FN,
362362
sglobal::SOME_METHOD_ID => self::sglobal::SGLOBAL_SOME_EVAL_FN,
363363
sglobal::NONE_METHOD_ID => self::sglobal::SGLOBAL_NONE_EVAL_FN,
364+
sglobal::POW_HIT_METHOD_ID => self::sglobal::POW_HIT_EVAL_FN,
364365
method_id => {
365366
return Err(EvalError::NotFound(format!(
366367
"Eval fn: method {:?} with method id {:?} not found in SGlobal",

ergotree-interpreter/src/eval/sglobal.rs

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use alloc::boxed::Box;
33
use alloc::{string::ToString, sync::Arc};
44

55
use ergotree_ir::serialization::sigma_byte_writer::SigmaByteWrite;
6+
use ergotree_ir::unsignedbigint256::UnsignedBigInt;
67
use ergotree_ir::{
78
mir::{
89
constant::{Constant, TryExtractInto},
@@ -17,7 +18,7 @@ use ergotree_ir::{
1718

1819
use super::EvalFn;
1920
use crate::eval::Vec;
20-
use ergo_chain_types::ec_point::generator;
21+
use ergo_chain_types::{autolykos_pow_scheme::AutolykosPowScheme, ec_point::generator};
2122
use ergotree_ir::bigint256::BigInt256;
2223
use ergotree_ir::types::stype::SType;
2324

@@ -219,6 +220,42 @@ pub(crate) static SGLOBAL_NONE_EVAL_FN: EvalFn = |_mc, _env, _ctx, obj, _args| {
219220
Ok(Value::Opt(None))
220221
};
221222

223+
pub(crate) static POW_HIT_EVAL_FN: EvalFn = |_mc, _env, _ctx, _obj, mut args| {
224+
// Pop arguments to avoid cloning
225+
let big_n: u32 = args
226+
.pop()
227+
.ok_or_else(|| EvalError::NotFound("powHit: missing N".into()))?
228+
.try_extract_into::<i32>()?
229+
.try_into()
230+
.map_err(|_| EvalError::Misc("N out of bounds".into()))?;
231+
let h = args
232+
.pop()
233+
.ok_or_else(|| EvalError::NotFound("powHit: missing h".into()))?
234+
.try_extract_into::<Vec<u8>>()?;
235+
let nonce = args
236+
.pop()
237+
.ok_or_else(|| EvalError::NotFound("powHit: missing nonce".into()))?
238+
.try_extract_into::<Vec<u8>>()?;
239+
let msg = args
240+
.pop()
241+
.ok_or_else(|| EvalError::NotFound("powHit: missing msg".into()))?
242+
.try_extract_into::<Vec<u8>>()?;
243+
let k = args
244+
.pop()
245+
.ok_or_else(|| EvalError::NotFound("powHit: missing msg".into()))?
246+
.try_extract_into::<i32>()?;
247+
Ok(UnsignedBigInt::try_from(
248+
AutolykosPowScheme::new(
249+
k.try_into()
250+
.map_err(|_| EvalError::Misc("k out of bounds".into()))?,
251+
big_n,
252+
)?
253+
.pow_hit_message_v2(&msg, &nonce, &h, big_n)?,
254+
)
255+
.map_err(EvalError::Misc)?
256+
.into())
257+
};
258+
222259
#[allow(clippy::unwrap_used)]
223260
#[cfg(test)]
224261
#[cfg(feature = "arbitrary")]
@@ -238,11 +275,12 @@ mod tests {
238275
use ergotree_ir::types::sgroup_elem::GET_ENCODED_METHOD;
239276
use ergotree_ir::types::stype_param::STypeVar;
240277
use ergotree_ir::unsignedbigint256::UnsignedBigInt;
278+
use num_traits::Num;
241279
use proptest::proptest;
242280

243281
use crate::eval::tests::{eval_out, eval_out_wo_ctx, try_eval_out_with_version};
244282
use ergotree_ir::chain::context::Context;
245-
use ergotree_ir::types::sglobal::{self, DESERIALIZE_METHOD, SERIALIZE_METHOD};
283+
use ergotree_ir::types::sglobal::{self, DESERIALIZE_METHOD, POW_HIT_METHOD, SERIALIZE_METHOD};
246284
use ergotree_ir::types::stype::SType;
247285
use sigma_test_util::force_any_val;
248286

@@ -315,6 +353,23 @@ mod tests {
315353
}
316354
}
317355

356+
fn pow_hit(k: u32, msg: &[u8], nonce: &[u8], h: &[u8], big_n: u32) -> UnsignedBigInt {
357+
let expr: Expr = MethodCall::new(
358+
Expr::Global,
359+
POW_HIT_METHOD.clone(),
360+
vec![
361+
Constant::from(k as i32).into(),
362+
Constant::from(msg.to_owned()).into(),
363+
Constant::from(nonce.to_owned()).into(),
364+
Constant::from(h.to_owned()).into(),
365+
Constant::from(big_n as i32).into(),
366+
],
367+
)
368+
.unwrap()
369+
.into();
370+
eval_out_wo_ctx(&expr)
371+
}
372+
318373
#[test]
319374
fn eval_group_generator() {
320375
let expr: Expr = PropertyCall::new(Expr::Global, sglobal::GROUP_GENERATOR_METHOD.clone())
@@ -558,6 +613,21 @@ mod tests {
558613
);
559614
}
560615

616+
#[test]
617+
fn pow_hit_eval() {
618+
let msg = base16::decode("0a101b8c6a4f2e").unwrap();
619+
let nonce = base16::decode("000000000000002c").unwrap();
620+
let hbs = base16::decode("00000000").unwrap();
621+
assert_eq!(
622+
pow_hit(32, &msg, &nonce, &hbs, 1024 * 1024),
623+
UnsignedBigInt::from_str_radix(
624+
"326674862673836209462483453386286740270338859283019276168539876024851191344",
625+
10
626+
)
627+
.unwrap()
628+
);
629+
}
630+
561631
proptest! {
562632
#[test]
563633
fn serialize_sigmaprop_eq_prop_bytes(sigma_prop: SigmaProp) {

ergotree-ir/src/types/sglobal.rs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ pub const SERIALIZE_METHOD_ID: MethodId = MethodId(3);
2828
pub const DESERIALIZE_METHOD_ID: MethodId = MethodId(4);
2929
/// "fromBigEndianBytes" predefined function
3030
pub const FROM_BIGENDIAN_BYTES_METHOD_ID: MethodId = MethodId(5);
31+
/// Global.powHit function
32+
pub const POW_HIT_METHOD_ID: MethodId = MethodId(8);
3133
/// "some" property
3234
pub const SOME_METHOD_ID: MethodId = MethodId(9);
3335
/// "none" property
@@ -36,7 +38,7 @@ pub const NONE_METHOD_ID: MethodId = MethodId(10);
3638
lazy_static! {
3739
/// Global method descriptors
3840
pub(crate) static ref METHOD_DESC: Vec<SMethodDesc> =
39-
vec![GROUP_GENERATOR_METHOD_DESC.clone(), XOR_METHOD_DESC.clone(), SERIALIZE_METHOD_DESC.clone(), DESERIALIZE_METHOD_DESC.clone(), FROM_BIGENDIAN_BYTES_METHOD_DESC.clone(), NONE_METHOD_DESC.clone(), SOME_METHOD_DESC.clone()];
41+
vec![GROUP_GENERATOR_METHOD_DESC.clone(), XOR_METHOD_DESC.clone(), SERIALIZE_METHOD_DESC.clone(), DESERIALIZE_METHOD_DESC.clone(), FROM_BIGENDIAN_BYTES_METHOD_DESC.clone(), NONE_METHOD_DESC.clone(), SOME_METHOD_DESC.clone(), POW_HIT_METHOD_DESC.clone()];
4042
}
4143

4244
lazy_static! {
@@ -125,6 +127,27 @@ lazy_static! {
125127
};
126128
/// GLOBAL.serialize
127129
pub static ref SERIALIZE_METHOD: SMethod = SMethod::new(STypeCompanion::Global, SERIALIZE_METHOD_DESC.clone(),);
130+
131+
static ref POW_HIT_METHOD_DESC: SMethodDesc = SMethodDesc {
132+
method_id: POW_HIT_METHOD_ID,
133+
name: "powHit",
134+
tpe: SFunc {
135+
t_dom: vec![
136+
SType::SGlobal,
137+
SType::SInt,
138+
SType::SColl(SType::SByte.into()),
139+
SType::SColl(SType::SByte.into()),
140+
SType::SColl(SType::SByte.into()),
141+
SType::SInt,
142+
],
143+
t_range: SType::SBoolean.into(),
144+
tpe_params: vec![],
145+
},
146+
explicit_type_args: vec![],
147+
min_version: ErgoTreeVersion::V3
148+
};
149+
/// Global.powHit
150+
pub static ref POW_HIT_METHOD: SMethod = SMethod::new(STypeCompanion::Global, POW_HIT_METHOD_DESC.clone());
128151
}
129152

130153
lazy_static! {
@@ -167,9 +190,9 @@ mod test {
167190

168191
use crate::{
169192
ergo_tree::ErgoTreeVersion,
170-
mir::{expr::Expr, method_call::MethodCall},
193+
mir::{constant::Constant, expr::Expr, method_call::MethodCall},
171194
serialization::roundtrip_new_feature,
172-
types::{stype::SType, stype_param::STypeVar},
195+
types::{sglobal::POW_HIT_METHOD, stype::SType, stype_param::STypeVar},
173196
};
174197

175198
use super::DESERIALIZE_METHOD;
@@ -185,5 +208,10 @@ mod test {
185208
).unwrap();
186209
roundtrip_new_feature(&mc, ErgoTreeVersion::V3);
187210
}
211+
#[test]
212+
fn pow_hit_roundtrip(k in any::<i32>(), msg in any::<Vec<u8>>(), nonce in any::<Vec<u8>>(), h in any::<Vec<u8>>(), big_n: u32) {
213+
let mc = MethodCall::new(Expr::Global, POW_HIT_METHOD.clone(), vec![Constant::from(k).into(), Constant::from(msg).into(), Constant::from(nonce).into(), Constant::from(h).into(), Constant::from(big_n as i32).into()]).unwrap();
214+
roundtrip_new_feature(&mc, ErgoTreeVersion::V3);
215+
}
188216
}
189217
}

ergotree-ir/src/unsignedbigint256.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! 256-bit unsigned big integer type
2+
use alloc::string::String;
23
use alloc::vec::Vec;
34
use core::ops::{Div, Mul, Rem};
5+
use num_bigint::BigUint;
46

57
use bnum::{
68
cast::{As, CastFrom},
@@ -169,6 +171,15 @@ impl UnsignedBigInt {
169171
}
170172
}
171173

174+
impl TryFrom<BigUint> for UnsignedBigInt {
175+
type Error = String;
176+
177+
fn try_from(value: BigUint) -> Result<Self, Self::Error> {
178+
let bytes = value.to_bytes_be();
179+
Self::from_be_slice(&bytes).ok_or_else(|| "BigInt256 value: {value} out of bounds".into())
180+
}
181+
}
182+
172183
impl From<u32> for UnsignedBigInt {
173184
fn from(value: u32) -> Self {
174185
Self(U256::from(value))

0 commit comments

Comments
 (0)