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

Basic implementation of scripting functionality #2240

Draft
wants to merge 29 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
13f108f
chore: update secp256k1 to 0.22.1
tmpolaczyk Jun 6, 2022
53b07f2
feat: enable global secp256k1 context
tmpolaczyk Jun 6, 2022
75970ea
feat(data_structures): Add redeem_script field to Input
tmpolaczyk Jun 3, 2022
f5c62a9
feat(stack): Include witnet_stack package with multiSig OPCODES and i…
tmpolaczyk Jun 3, 2022
9a2bf33
refactor(data_structures): Refactored vtt signatures field to witness
tmpolaczyk Jun 3, 2022
8a8fe9e
feat(validations): Include script validations
tmpolaczyk Jun 3, 2022
7a90e70
feat(cli): Implement createMultiSigAddress CLI method
tmpolaczyk Jun 3, 2022
8105aae
feat(cli): Implement CLI methods createOpenedMultiSig, signTransacion…
tmpolaczyk Jun 3, 2022
dea1c2e
fix(validations): Handle case when there is no signatures in the witn…
tmpolaczyk Jun 3, 2022
b4fb7c0
feat(stack): Create MyValue::Signature type to differ from MyValue::B…
tmpolaczyk Jun 3, 2022
785964d
feat(data_structures): Implement redeem script weight in Input
tmpolaczyk Jun 3, 2022
4eb8395
feat: Allow using custom change address in sendvtt
tmpolaczyk Jun 3, 2022
5d8a827
fix(tests): Fix broken tests
tmpolaczyk Jun 3, 2022
19e110a
chore: Format includes
lrubiorod Jun 3, 2022
e9b6b40
feat(stack): implement control flow
tmpolaczyk Jun 7, 2022
4f6ab16
feat(stack): allow context in machine operate function
tmpolaczyk Jun 8, 2022
1b4bd8a
feat: implement CheckTimelock scripting operator
tmpolaczyk Jun 8, 2022
8fa2628
feat(stack): Sha256 operator
tmpolaczyk Jun 8, 2022
61bd93f
feat: add error handling to script encode and decode
tmpolaczyk Jun 8, 2022
2261313
feat(node): avoid unused call to signature_mngr in BuildScriptTx
tmpolaczyk Jun 13, 2022
1ae9db3
feat(stack): remove MyControlFlow enum, use simple Result instead
tmpolaczyk Jun 13, 2022
6944527
feat(stack): implement CheckSig operator
tmpolaczyk Jun 14, 2022
0295df4
test(stack): add atomic swap use case test
tmpolaczyk Jun 15, 2022
e913516
test(stack): add another atomic swap test case
tmpolaczyk Jun 20, 2022
3df70a9
feat: scriptful 0.4
tmpolaczyk Jun 17, 2022
4b16e47
feat(stack): encode signatures as concatenated bytes
tmpolaczyk Aug 17, 2022
7f00e25
refactor(stack): helper function to encode pkh as bytes
tmpolaczyk Aug 17, 2022
e6adc69
refactor: turn witnet_stack crate into module inside witnet_data_stru…
tmpolaczyk Aug 17, 2022
5b8c089
feat: encode legacy signatures as script with one element
tmpolaczyk Aug 17, 2022
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
22 changes: 16 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ hmac = "0.7.1"
memzero = "0.1.0"
rand = "0.7.3"
ring = "0.16.11"
secp256k1 = "0.20.3"
secp256k1 = { version = "0.22.1", features = ["global-context"] }
serde = { version = "1.0.104", optional = true }
sha2 = "0.8.1"
tiny-bip39 = "0.7.0"
Expand Down
44 changes: 9 additions & 35 deletions crypto/src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use bech32::{FromBase32, ToBase32 as _};
use byteorder::{BigEndian, ReadBytesExt as _};
use failure::Fail;
use hmac::{Hmac, Mac};
use secp256k1::{PublicKey, Secp256k1, SecretKey, SignOnly, Signing, VerifyOnly};
use secp256k1::{PublicKey, SecretKey};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -160,22 +160,6 @@ pub type SK = SecretKey;
/// Public Key
pub type PK = PublicKey;

/// The secp256k1 engine, used to execute all signature operations.
///
/// `Engine::new()`: all capabilities
/// `Engine::signing_only()`: only be used for signing
/// `Engine::verification_only()`: only be used for verification
pub type Engine<C> = Secp256k1<C>;

/// Secp256k1 engine that can only be used for signing.
pub type SignEngine = Secp256k1<SignOnly>;

/// Secp256k1 engine that can only be used for verifying.
pub type VerifyEngine = Secp256k1<VerifyOnly>;

/// Secp256k1 engine that can be used for signing and for verifying.
pub type CryptoEngine = Secp256k1<secp256k1::All>;

/// Extended Key is just a Key with a Chain Code
#[derive(Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
Expand Down Expand Up @@ -292,25 +276,17 @@ impl ExtendedSK {
}

/// Try to derive an extended private key from a given path
pub fn derive<C: Signing>(
&self,
engine: &Engine<C>,
path: &KeyPath,
) -> Result<ExtendedSK, KeyDerivationError> {
pub fn derive(&self, path: &KeyPath) -> Result<ExtendedSK, KeyDerivationError> {
let mut extended_sk = self.clone();
for index in path.iter() {
extended_sk = extended_sk.child(engine, index)?
extended_sk = extended_sk.child(index)?
}

Ok(extended_sk)
}

/// Try to get a private child key from parent
pub fn child<C: Signing>(
&self,
engine: &Engine<C>,
index: &KeyPathIndex,
) -> Result<ExtendedSK, KeyDerivationError> {
pub fn child(&self, index: &KeyPathIndex) -> Result<ExtendedSK, KeyDerivationError> {
let mut hmac512: Hmac<sha2::Sha512> =
Hmac::new_varkey(&self.chain_code).map_err(|_| KeyDerivationError::InvalidKeyLength)?;
let index_bytes = index.as_ref().to_be_bytes();
Expand All @@ -319,7 +295,7 @@ impl ExtendedSK {
hmac512.input(&[0]); // BIP-32 padding that makes key 33 bytes long
hmac512.input(&self.secret_key[..]);
} else {
hmac512.input(&PublicKey::from_secret_key(engine, &self.secret_key).serialize());
hmac512.input(&PublicKey::from_secret_key_global(&self.secret_key).serialize());
}

let (chain_code, mut secret_key) = get_chain_code_and_secret(&index_bytes, hmac512)?;
Expand Down Expand Up @@ -355,12 +331,12 @@ pub struct ExtendedPK {

impl ExtendedPK {
/// Derive the public key from a private key.
pub fn from_secret_key<C: Signing>(engine: &Engine<C>, key: &ExtendedSK) -> Self {
pub fn from_secret_key(key: &ExtendedSK) -> Self {
let ExtendedSK {
secret_key,
chain_code,
} = key;
let key = PublicKey::from_secret_key(engine, secret_key);
let key = PublicKey::from_secret_key_global(secret_key);
Self {
key,
chain_code: chain_code.clone(),
Expand Down Expand Up @@ -577,8 +553,7 @@ mod tests {
.hardened(0) // account: hardened 0
.index(0) // change: 0
.index(0); // address: 0
let engine = SignEngine::signing_only();
let account = extended_sk.derive(&engine, &path).unwrap();
let account = extended_sk.derive(&path).unwrap();

let expected_account = [
137, 174, 230, 121, 4, 190, 53, 238, 47, 181, 52, 226, 109, 68, 153, 170, 112, 150, 84,
Expand All @@ -598,10 +573,9 @@ mod tests {
let mnemonic = bip39::Mnemonic::from_phrase(phrase.into()).unwrap();
let seed = mnemonic.seed(&"".into());
let master_key = MasterKeyGen::new(&seed).generate().unwrap();
let engine = Secp256k1::signing_only();

for (expected, keypath) in slip32_vectors() {
let key = master_key.derive(&engine, &keypath).unwrap();
let key = master_key.derive(&keypath).unwrap();
let xprv = key.to_slip32(&keypath).unwrap();

assert_eq!(expected, xprv);
Expand Down
34 changes: 14 additions & 20 deletions crypto/src/signature.rs
Original file line number Diff line number Diff line change
@@ -1,57 +1,52 @@
//! Signature module

use crate::key::CryptoEngine;
use secp256k1::{Error, Message, SecretKey};

/// Signature
pub type Signature = secp256k1::Signature;
pub type Signature = secp256k1::ecdsa::Signature;

/// PublicKey
pub type PublicKey = secp256k1::PublicKey;

/// Sign `data` with provided secret key. `data` must be the 32-byte output of a cryptographically
/// secure hash function, otherwise this function is not secure.
/// - Returns an Error if data is not a 32-byte array
pub fn sign(secp: &CryptoEngine, secret_key: SecretKey, data: &[u8]) -> Result<Signature, Error> {
pub fn sign(secret_key: SecretKey, data: &[u8]) -> Result<Signature, Error> {
let msg = Message::from_slice(data)?;

Ok(secp.sign(&msg, &secret_key))
Ok(secret_key.sign_ecdsa(msg))
}
/// Verify signature with a provided public key.
/// - Returns an Error if data is not a 32-byte array
pub fn verify(
secp: &CryptoEngine,
public_key: &PublicKey,
data: &[u8],
sig: &Signature,
) -> Result<(), Error> {
pub fn verify(public_key: &PublicKey, data: &[u8], sig: &Signature) -> Result<(), Error> {
let msg = Message::from_slice(data)?;

secp.verify(&msg, sig, public_key)
sig.verify(&msg, public_key)
}

#[cfg(test)]
mod tests {
use crate::hash::{calculate_sha256, Sha256};
use crate::signature::{sign, verify};
use secp256k1::{PublicKey, Secp256k1, SecretKey, Signature};
use crate::{
hash::{calculate_sha256, Sha256},
signature::{sign, verify},
};
use secp256k1::{ecdsa::Signature, PublicKey, SecretKey};

#[test]
fn test_sign_and_verify() {
let data = [0xab; 32];
let secp = &Secp256k1::new();
let secret_key = SecretKey::from_slice(&[0xcd; 32]).expect("32 bytes, within curve order");
let public_key = PublicKey::from_secret_key(secp, &secret_key);
let public_key = PublicKey::from_secret_key_global(&secret_key);

let signature = sign(secp, secret_key, &data).unwrap();
let signature = sign(secret_key, &data).unwrap();
let signature_expected = "3044\
0220\
3dc4fa74655c21b7ffc0740e29bfd88647e8dfe2b68c507cf96264e4e7439c1f\
0220\
7aa61261b18eebdfdb704ca7bab4c7bcf7961ae0ade5309f6f1398e21aec0f9f";
assert_eq!(signature_expected.to_string(), signature.to_string());

assert!(verify(secp, &public_key, &data, &signature).is_ok());
assert!(verify(&public_key, &data, &signature).is_ok());
}

#[test]
Expand Down Expand Up @@ -100,7 +95,6 @@ mod tests {

#[test]
fn test_sign_and_verify_before_hash() {
let secp = &Secp256k1::new();
let secret_key = SecretKey::from_slice(&[0xcd; 32]).expect("32 bytes, within curve order");

let i = 9;
Expand All @@ -111,7 +105,7 @@ mod tests {

let Sha256(hashed_data) = calculate_sha256(message.as_bytes());

let signature = sign(secp, secret_key, &hashed_data).unwrap();
let signature = sign(secret_key, &hashed_data).unwrap();

let r_s = signature.serialize_compact();
let (r, s) = r_s.split_at(32);
Expand Down
1 change: 1 addition & 0 deletions data_structures/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ serde = { version = "1.0.104", features = ["derive"] }
serde_cbor = "0.11.1"
serde_json = "1.0.48"
vrf = "0.2.3"
scriptful = { version = "0.4", features = ["use_serde"] }

witnet_crypto = { path = "../crypto" }
witnet_reputation = { path = "../reputation", features = ["serde"] }
Expand Down
5 changes: 3 additions & 2 deletions data_structures/examples/transactions_pool_overhead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use witnet_data_structures::chain::{
ValueTransferOutput,
};
use witnet_data_structures::transaction::{
DRTransaction, DRTransactionBody, Transaction, VTTransaction, VTTransactionBody,
vtt_signature_to_witness, DRTransaction, DRTransactionBody, Transaction, VTTransaction,
VTTransactionBody,
};

fn random_request() -> RADRequest {
Expand Down Expand Up @@ -95,7 +96,7 @@ fn random_transaction() -> (Transaction, u64) {
let t = if rng.gen() {
Transaction::ValueTransfer(VTTransaction {
body: VTTransactionBody::new(inputs, outputs),
signatures: vec![signature; num_inputs],
witness: vec![vtt_signature_to_witness(&signature); num_inputs],
})
} else {
let dr_output = random_dr_output();
Expand Down
Loading