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

Crypto op code sanity checks #1430

Merged
merged 11 commits into from
Oct 23, 2023
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ Description of the upcoming release here.

### Added

- [#1436](https://github.com/FuelLabs/fuel-core/pull/1436): Add a github action to continuously test beta-4
- [#1436](https://github.com/FuelLabs/fuel-core/pull/1436): Add a github action to continuously test beta-4.
- [#1430](https://github.com/FuelLabs/fuel-core/pull/1430): Add "sanity" benchmarks for crypto opcodes.
- [#1432](https://github.com/FuelLabs/fuel-core/pull/1432): Add a new `--api-request-timeout` argument to control TTL for GraphQL requests.
- [#1419](https://github.com/FuelLabs/fuel-core/pull/1419): Add additional "sanity" benchmarks for arithmetic op code instructions.
- [#1411](https://github.com/FuelLabs/fuel-core/pull/1411): Added WASM and `no_std` compatibility.
Expand Down
88 changes: 6 additions & 82 deletions benches/benches/block_target_gas.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use block_target_gas_set::alu::run_alu;
use block_target_gas_set::{
alu::run_alu,
crypto::run_crypto,
};
use criterion::{
criterion_group,
criterion_main,
Expand Down Expand Up @@ -170,89 +173,10 @@ fn block_target_gas(c: &mut Criterion) {
vec![],
);

let message = fuel_core_types::fuel_crypto::Message::new(b"foo");
let ecr1_secret = p256::ecdsa::SigningKey::random(&mut rand::thread_rng());
let ecr1_signature = secp256r1::sign_prehashed(&ecr1_secret, &message)
.expect("Failed to sign with secp256r1");

run(
"Script with ecr1 opcode and infinite loop",
&mut group,
[
op::gtf_args(0x20, 0x00, GTFArgs::ScriptData),
op::addi(
0x21,
0x20,
ecr1_signature.as_ref().len().try_into().unwrap(),
),
op::addi(0x22, 0x21, message.as_ref().len().try_into().unwrap()),
op::movi(0x10, PublicKey::LEN.try_into().unwrap()),
op::aloc(0x10),
op::move_(0x11, RegId::HP),
op::ecr1(0x11, 0x20, 0x21),
op::jmpb(RegId::ZERO, 0),
]
.to_vec(),
ecr1_signature
.as_ref()
.iter()
.chain(message.as_ref())
.copied()
.collect(),
);

let ed19_keypair =
ed25519_dalek::Keypair::generate(&mut ed25519_dalek_old_rand::rngs::OsRng {});
let ed19_signature = ed19_keypair.sign(&*message);

run(
"Script with ed19 opcode and infinite loop",
&mut group,
[
op::gtf_args(0x20, 0x00, GTFArgs::ScriptData),
op::addi(
0x21,
0x20,
ed19_keypair.public.as_ref().len().try_into().unwrap(),
),
op::addi(
0x22,
0x21,
ed19_signature.as_ref().len().try_into().unwrap(),
),
op::addi(0x22, 0x21, message.as_ref().len().try_into().unwrap()),
op::movi(0x10, ed25519_dalek::PUBLIC_KEY_LENGTH.try_into().unwrap()),
op::aloc(0x10),
op::move_(0x11, RegId::HP),
op::ed19(0x20, 0x21, 0x22),
op::jmpb(RegId::ZERO, 0),
]
.to_vec(),
ed19_keypair
.public
.as_ref()
.iter()
.chain(ed19_signature.as_ref())
.chain(message.as_ref())
.copied()
.collect(),
);

// The test is supper long because we don't use `DependentCost` for k256 opcode
// run(
// "Script with k256 opcode and infinite loop",
// &mut group,
// [
// op::movi(0x10, 1 << 18 - 1),
// op::aloc(0x10),
// op::k256(RegId::HP, RegId::ZERO, 0x10),
// op::jmpb(RegId::ZERO, 0),
// ]
// .to_vec(),
// );

run_alu(&mut group);

run_crypto(&mut group);

group.finish();
}

Expand Down
171 changes: 171 additions & 0 deletions benches/benches/block_target_gas_set/crypto.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
use crate::{
utils::generate_linear_costs,
*,
};
use rand::{
rngs::StdRng,
SeedableRng,
};

// ECK1: Secp251k1 signature recovery
// ECR1: Secp256r1 signature recovery
// ED19: edDSA curve25519 verification
// K256: keccak-256
// S256: SHA-2-256
pub fn run_crypto(group: &mut BenchmarkGroup<WallTime>) {
let rng = &mut StdRng::seed_from_u64(2322u64);

let message = Message::new(b"foo");
MitchTurner marked this conversation as resolved.
Show resolved Hide resolved

let eck1_secret = SecretKey::random(rng);
let eck1_signature = Signature::sign(&eck1_secret, &message);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I don't know, but maybe it is also worth testing in case of invalid signatures. Maybe some of these curves work slower in case of invalid signatures.

Or maybe you can find public research regarding the performance of these curves. Based on this research we can select the worst case.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added an invalid test, which seems to be roughly the same efficiency.

I don't know what it would mean to find a worst-case signature. It's possible that some signatures are more expensive to verify than others, but the more you can say about the original message in those cases, the worse the algo is, right? So Idk what we'd be looking for other than finding some specific input that results in a slow test.

Perhaps we could modify the test to just have a bunch of random inputs and see what it averages out to.

run(
"crypto/eck1 opcode valid",
group,
[
op::gtf_args(0x20, 0x00, GTFArgs::ScriptData),
op::addi(
0x21,
0x20,
eck1_signature.as_ref().len().try_into().unwrap(),
),
op::movi(0x10, PublicKey::LEN.try_into().unwrap()),
op::aloc(0x10),
op::eck1(RegId::HP, 0x20, 0x21),
op::jmpb(RegId::ZERO, 0),
]
.to_vec(),
eck1_signature
.iter()
.chain(message.iter())
.copied()
.collect(),
);

let wrong_message = Message::new(b"bar");

run(
"crypto/eck1 opcode invalid",
group,
[
op::gtf_args(0x20, 0x00, GTFArgs::ScriptData),
op::addi(
0x21,
0x20,
eck1_signature.as_ref().len().try_into().unwrap(),
),
op::movi(0x10, PublicKey::LEN.try_into().unwrap()),
op::aloc(0x10),
op::eck1(RegId::HP, 0x20, 0x21),
op::jmpb(RegId::ZERO, 0),
]
.to_vec(),
eck1_signature
.iter()
.chain(wrong_message.iter())
.copied()
.collect(),
);

let message = fuel_core_types::fuel_crypto::Message::new(b"foo");
MitchTurner marked this conversation as resolved.
Show resolved Hide resolved
let ecr1_secret = p256::ecdsa::SigningKey::random(&mut rand::thread_rng());
let ecr1_signature = secp256r1::sign_prehashed(&ecr1_secret, &message)
.expect("Failed to sign with secp256r1");

run(
"crypto/ecr1 opcode",
group,
[
op::gtf_args(0x20, 0x00, GTFArgs::ScriptData),
op::addi(
0x21,
0x20,
ecr1_signature.as_ref().len().try_into().unwrap(),
),
op::movi(0x10, PublicKey::LEN.try_into().unwrap()),
op::aloc(0x10),
op::move_(0x11, RegId::HP),
op::ecr1(0x11, 0x20, 0x21),
op::jmpb(RegId::ZERO, 0),
]
.to_vec(),
ecr1_signature
.as_ref()
.iter()
.chain(message.as_ref())
.copied()
.collect(),
);

let message = fuel_core_types::fuel_crypto::Message::new(b"foo");
MitchTurner marked this conversation as resolved.
Show resolved Hide resolved
let ed19_keypair =
ed25519_dalek::Keypair::generate(&mut ed25519_dalek_old_rand::rngs::OsRng {});
let ed19_signature = ed19_keypair.sign(&*message);

run(
"crypto/ed19 opcode",
group,
[
op::gtf_args(0x20, 0x00, GTFArgs::ScriptData),
op::addi(
0x21,
0x20,
ed19_keypair.public.as_ref().len().try_into().unwrap(),
),
op::addi(
0x22,
0x21,
ed19_signature.as_ref().len().try_into().unwrap(),
),
op::addi(0x22, 0x21, message.as_ref().len().try_into().unwrap()),
op::movi(0x10, ed25519_dalek::PUBLIC_KEY_LENGTH.try_into().unwrap()),
op::aloc(0x10),
op::move_(0x11, RegId::HP),
op::ed19(0x20, 0x21, 0x22),
op::jmpb(RegId::ZERO, 0),
]
.to_vec(),
ed19_keypair
.public
.as_ref()
.iter()
.chain(ed19_signature.as_ref())
.chain(message.as_ref())
.copied()
.collect(),
);

for i in generate_linear_costs() {
let id = format!("crypto/s256 opcode {:?}", i);
run(
&id,
group,
[
op::movi(0x11, 32),
op::aloc(0x11),
op::movi(0x10, i),
op::s256(RegId::HP, RegId::ZERO, 0x10),
op::jmpb(RegId::ZERO, 0),
]
.to_vec(),
vec![],
)
}

for i in generate_linear_costs() {
let id = format!("crypto/k256 opcode {:?}", i);
run(
&id,
group,
[
op::movi(0x11, 32),
op::aloc(0x11),
op::movi(0x10, i),
op::k256(RegId::HP, RegId::ZERO, 0x10),
op::jmpb(RegId::ZERO, 0),
]
.to_vec(),
vec![],
)
}
}
2 changes: 2 additions & 0 deletions benches/benches/block_target_gas_set/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
pub mod alu;

pub mod crypto;
12 changes: 12 additions & 0 deletions benches/benches/utils.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use core::iter::successors;
use ethnum::U256;
use fuel_core_types::fuel_asm::{
op,
Expand Down Expand Up @@ -25,3 +26,14 @@ pub fn make_u128(reg: u8, v: u128) -> Vec<Instruction> {
pub fn make_u256(reg: u8, v: U256) -> Vec<Instruction> {
aloc_bytearray(reg, v.to_be_bytes())
}

pub fn generate_linear_costs() -> Vec<u32> {
let mut linear = vec![1, 10, 100, 1000, 10_000];
let mut l = successors(Some(100_000.0f64), |n| Some(n / 1.5))
.take(5)
.map(|f| f as u32)
.collect::<Vec<_>>();
l.sort_unstable();
linear.extend(l);
linear
}
2 changes: 1 addition & 1 deletion benches/benches/vm_set/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ pub fn run(c: &mut Criterion) {
),
);

let linear = super::generate_linear_costs();
let linear = super::utils::generate_linear_costs();

let mut bench_k256 = c.benchmark_group("k256");
for i in &linear {
Expand Down
3 changes: 2 additions & 1 deletion benches/benches/vm_set/mem.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::run_group_ref;

use crate::utils::generate_linear_costs;
use criterion::{
Criterion,
Throughput,
Expand Down Expand Up @@ -53,7 +54,7 @@ pub fn run(c: &mut Criterion) {
]),
);

let linear = super::generate_linear_costs();
let linear = generate_linear_costs();

run_group_ref(
&mut c.benchmark_group("cfei"),
Expand Down
12 changes: 0 additions & 12 deletions benches/benches/vm_set/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,3 @@ pub mod mem;
pub use super::run_group_ref;

use super::utils;
use core::iter::successors;

fn generate_linear_costs() -> Vec<u32> {
let mut linear = vec![1, 10, 100, 1000, 10_000];
let mut l = successors(Some(100_000.0f64), |n| Some(n / 1.5))
.take(5)
.map(|f| f as u32)
.collect::<Vec<_>>();
l.sort_unstable();
linear.extend(l);
linear
}
Loading