mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-01-08 16:23:12 +00:00
add signatures
This commit is contained in:
parent
703a127f0e
commit
5bc1c9c688
@ -13,7 +13,7 @@ impl Address {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_public_key(public_key: &PublicKey) -> Self {
|
pub fn from_public_key(public_key: &PublicKey) -> Self {
|
||||||
// TODO: implement
|
// TODO: Check specs
|
||||||
Address::new(public_key.0)
|
Address::new(public_key.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -29,4 +29,39 @@ impl Message {
|
|||||||
instruction_data,
|
instruction_data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Serializes a `Message` into bytes in the following layout:
|
||||||
|
/// TAG || <program_id> (bytes LE) * 8 || addresses_len (4 bytes LE) || addresses (32 bytes * N) || nonces_len (4 bytes LE) || nonces (16 bytes * M) || instruction_data_len || instruction_data (4 bytes * K)
|
||||||
|
/// Integers and words are encoded in little-endian byte order, and fields appear in the above order.
|
||||||
|
pub(crate) fn message_to_bytes(&self) -> Vec<u8> {
|
||||||
|
const MESSAGE_ENCODING_PREFIX: &[u8; 19] = b"NSSA/v0.1/TxMessage";
|
||||||
|
|
||||||
|
let mut bytes = MESSAGE_ENCODING_PREFIX.to_vec();
|
||||||
|
// program_id: [u32; 8]
|
||||||
|
for word in &self.program_id {
|
||||||
|
bytes.extend_from_slice(&word.to_le_bytes());
|
||||||
|
}
|
||||||
|
// addresses: Vec<[u8;32]>
|
||||||
|
// serialize length as u32 little endian, then all addresses concatenated
|
||||||
|
let addresses_len = self.addresses.len() as u32;
|
||||||
|
bytes.extend(&addresses_len.to_le_bytes());
|
||||||
|
for addr in &self.addresses {
|
||||||
|
bytes.extend_from_slice(addr.value());
|
||||||
|
}
|
||||||
|
// nonces: Vec<u128>
|
||||||
|
let nonces_len = self.nonces.len() as u32;
|
||||||
|
bytes.extend(&nonces_len.to_le_bytes());
|
||||||
|
for nonce in &self.nonces {
|
||||||
|
bytes.extend(&nonce.to_le_bytes());
|
||||||
|
}
|
||||||
|
// instruction_data: Vec<u32>
|
||||||
|
// serialize length as u32 little endian, then all addresses concatenated
|
||||||
|
let instr_len = self.instruction_data.len() as u32;
|
||||||
|
bytes.extend(&instr_len.to_le_bytes());
|
||||||
|
for word in &self.instruction_data {
|
||||||
|
bytes.extend(&word.to_le_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,23 +72,20 @@ impl PublicTransaction {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut authorized_addresses = Vec::new();
|
// Check the signatures are valid
|
||||||
for ((signature, public_key), nonce) in witness_set.iter_signatures().zip(&message.nonces) {
|
if !witness_set.is_valid_for(message) {
|
||||||
// Check the signature is valid
|
return Err(NssaError::InvalidInput(
|
||||||
if !signature.is_valid_for(message, public_key) {
|
"Invalid signature for given message and public key".into(),
|
||||||
return Err(NssaError::InvalidInput(
|
));
|
||||||
"Invalid signature for given message and public key".into(),
|
}
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the nonce corresponds to the current nonce on the public state.
|
let signer_addresses = self.signer_addresses();
|
||||||
let address = Address::from_public_key(public_key);
|
// Check nonces corresponds to the current nonces on the public state.
|
||||||
let current_nonce = state.get_account_by_address(&address).nonce;
|
for (address, nonce) in signer_addresses.iter().zip(&message.nonces) {
|
||||||
|
let current_nonce = state.get_account_by_address(address).nonce;
|
||||||
if current_nonce != *nonce {
|
if current_nonce != *nonce {
|
||||||
return Err(NssaError::InvalidInput("Nonce mismatch".into()));
|
return Err(NssaError::InvalidInput("Nonce mismatch".into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
authorized_addresses.push(address);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build pre_states for execution
|
// Build pre_states for execution
|
||||||
@ -97,7 +94,7 @@ impl PublicTransaction {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|address| AccountWithMetadata {
|
.map(|address| AccountWithMetadata {
|
||||||
account: state.get_account_by_address(address),
|
account: state.get_account_by_address(address),
|
||||||
is_authorized: authorized_addresses.contains(address),
|
is_authorized: signer_addresses.contains(address),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
|||||||
@ -7,44 +7,9 @@ pub struct WitnessSet {
|
|||||||
pub(crate) signatures_and_public_keys: Vec<(Signature, PublicKey)>,
|
pub(crate) signatures_and_public_keys: Vec<(Signature, PublicKey)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const MESSAGE_ENCODING_PREFIX: &[u8; 19] = b"NSSA/v0.1/TxMessage";
|
|
||||||
|
|
||||||
/// Serializes a `Message` into bytes in the following layout:
|
|
||||||
/// TAG || <program_id> (bytes LE) * 8 || addresses_len (4 bytes LE) || addresses (32 bytes * N) || nonces_len (4 bytes LE) || nonces (16 bytes * M) || instruction_data_len || instruction_data (4 bytes * K)
|
|
||||||
/// Integers and words are encoded in little-endian byte order, and fields appear in the above order.
|
|
||||||
fn message_to_bytes(message: &Message) -> Vec<u8> {
|
|
||||||
let mut bytes = MESSAGE_ENCODING_PREFIX.to_vec();
|
|
||||||
// program_id: [u32; 8]
|
|
||||||
for word in &message.program_id {
|
|
||||||
bytes.extend_from_slice(&word.to_le_bytes());
|
|
||||||
}
|
|
||||||
// addresses: Vec<[u8;32]>
|
|
||||||
// serialize length as u32 little endian, then all addresses concatenated
|
|
||||||
let addresses_len = message.addresses.len() as u32;
|
|
||||||
bytes.extend(&addresses_len.to_le_bytes());
|
|
||||||
for addr in &message.addresses {
|
|
||||||
bytes.extend_from_slice(addr.value());
|
|
||||||
}
|
|
||||||
// nonces: Vec<u128>
|
|
||||||
let nonces_len = message.nonces.len() as u32;
|
|
||||||
bytes.extend(&nonces_len.to_le_bytes());
|
|
||||||
for nonce in &message.nonces {
|
|
||||||
bytes.extend(&nonce.to_le_bytes());
|
|
||||||
}
|
|
||||||
// instruction_data: Vec<u32>
|
|
||||||
// serialize length as u32 little endian, then all addresses concatenated
|
|
||||||
let instr_len = message.instruction_data.len() as u32;
|
|
||||||
bytes.extend(&instr_len.to_le_bytes());
|
|
||||||
for word in &message.instruction_data {
|
|
||||||
bytes.extend(&word.to_le_bytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WitnessSet {
|
impl WitnessSet {
|
||||||
pub fn for_message(message: &Message, private_keys: &[&PrivateKey]) -> Self {
|
pub fn for_message(message: &Message, private_keys: &[&PrivateKey]) -> Self {
|
||||||
let message_bytes = message_to_bytes(message);
|
let message_bytes = message.message_to_bytes();
|
||||||
let signatures_and_public_keys = private_keys
|
let signatures_and_public_keys = private_keys
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&key| (Signature::new(key, &message_bytes), PublicKey::new(key)))
|
.map(|&key| (Signature::new(key, &message_bytes), PublicKey::new(key)))
|
||||||
@ -54,6 +19,16 @@ impl WitnessSet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_valid_for(&self, message: &Message) -> bool {
|
||||||
|
let message_bytes = message.message_to_bytes();
|
||||||
|
for (signature, public_key) in self.iter_signatures() {
|
||||||
|
if !signature.is_valid_for(&message_bytes, &public_key) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
pub fn iter_signatures(&self) -> impl Iterator<Item = &(Signature, PublicKey)> {
|
pub fn iter_signatures(&self) -> impl Iterator<Item = &(Signature, PublicKey)> {
|
||||||
self.signatures_and_public_keys.iter()
|
self.signatures_and_public_keys.iter()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,54 +0,0 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::{error::NssaError, public_transaction::Message};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
|
||||||
pub struct Signature(pub(crate) u8);
|
|
||||||
|
|
||||||
// TODO: Dummy impl. Replace by actual private key.
|
|
||||||
// TODO: Remove Debug, Clone, Serialize, Deserialize, PartialEq and Eq for security reasons
|
|
||||||
// TODO: Implement Zeroize
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
|
||||||
pub struct PrivateKey(pub(crate) [u8; 32]);
|
|
||||||
|
|
||||||
impl PrivateKey {
|
|
||||||
fn is_valid_key(value: [u8; 32]) -> bool {
|
|
||||||
secp256k1::SecretKey::from_byte_array(value).is_ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_new(value: [u8; 32]) -> Result<Self, NssaError> {
|
|
||||||
if Self::is_valid_key(value) {
|
|
||||||
Ok(Self(value))
|
|
||||||
} else {
|
|
||||||
Err(NssaError::InvalidPrivateKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Dummy impl. Replace by actual public key.
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
|
||||||
pub struct PublicKey(pub(crate) [u8; 32]);
|
|
||||||
|
|
||||||
impl PublicKey {
|
|
||||||
pub fn new(key: &PrivateKey) -> Self {
|
|
||||||
let value = {
|
|
||||||
let secret_key = secp256k1::SecretKey::from_byte_array(key.0).unwrap();
|
|
||||||
let public_key =
|
|
||||||
secp256k1::PublicKey::from_secret_key(&secp256k1::Secp256k1::new(), &secret_key);
|
|
||||||
let (x_only, _) = public_key.x_only_public_key();
|
|
||||||
x_only.serialize()
|
|
||||||
};
|
|
||||||
Self(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Signature {
|
|
||||||
pub(crate) fn new(key: &PrivateKey, _message: &[u8]) -> Self {
|
|
||||||
Signature(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_valid_for(&self, _message: &Message, _public_key: &PublicKey) -> bool {
|
|
||||||
// TODO: implement
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
7
nssa/src/signature/mod.rs
Normal file
7
nssa/src/signature/mod.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
mod private_key;
|
||||||
|
mod public_key;
|
||||||
|
mod signature;
|
||||||
|
|
||||||
|
pub use private_key::PrivateKey;
|
||||||
|
pub use public_key::PublicKey;
|
||||||
|
pub use signature::Signature;
|
||||||
25
nssa/src/signature/private_key.rs
Normal file
25
nssa/src/signature/private_key.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::error::NssaError;
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: Dummy impl. Replace by actual private key.
|
||||||
|
// TODO: Remove Debug, Clone, Serialize, Deserialize, PartialEq and Eq for security reasons
|
||||||
|
// TODO: Implement Zeroize
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
pub struct PrivateKey(pub(crate) [u8; 32]);
|
||||||
|
|
||||||
|
impl PrivateKey {
|
||||||
|
fn is_valid_key(value: [u8; 32]) -> bool {
|
||||||
|
secp256k1::SecretKey::from_byte_array(value).is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_new(value: [u8; 32]) -> Result<Self, NssaError> {
|
||||||
|
if Self::is_valid_key(value) {
|
||||||
|
Ok(Self(value))
|
||||||
|
} else {
|
||||||
|
Err(NssaError::InvalidPrivateKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
21
nssa/src/signature/public_key.rs
Normal file
21
nssa/src/signature/public_key.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::PrivateKey;
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: Dummy impl. Replace by actual public key.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
pub struct PublicKey(pub(crate) [u8; 32]);
|
||||||
|
|
||||||
|
impl PublicKey {
|
||||||
|
pub fn new(key: &PrivateKey) -> Self {
|
||||||
|
let value = {
|
||||||
|
let secret_key = secp256k1::SecretKey::from_byte_array(key.0).unwrap();
|
||||||
|
let public_key =
|
||||||
|
secp256k1::PublicKey::from_secret_key(&secp256k1::Secp256k1::new(), &secret_key);
|
||||||
|
let (x_only, _) = public_key.x_only_public_key();
|
||||||
|
x_only.serialize()
|
||||||
|
};
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
56
nssa/src/signature/signature.rs
Normal file
56
nssa/src/signature/signature.rs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
|
use crate::{PrivateKey, PublicKey, error::NssaError, public_transaction::Message};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct Signature {
|
||||||
|
value: [u8; 64],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Signature {
|
||||||
|
pub(crate) fn new(key: &PrivateKey, message: &[u8]) -> Self {
|
||||||
|
let value = {
|
||||||
|
let secp = secp256k1::Secp256k1::new();
|
||||||
|
let secret_key = secp256k1::SecretKey::from_byte_array(key.0).unwrap();
|
||||||
|
let keypair = secp256k1::Keypair::from_secret_key(&secp, &secret_key);
|
||||||
|
let signature = secp.sign_schnorr_no_aux_rand(message, &keypair);
|
||||||
|
signature.to_byte_array()
|
||||||
|
};
|
||||||
|
Self { value }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_valid_for(&self, bytes: &[u8], public_key: &PublicKey) -> bool {
|
||||||
|
let pk = secp256k1::XOnlyPublicKey::from_byte_array(public_key.0).unwrap();
|
||||||
|
let secp = secp256k1::Secp256k1::new();
|
||||||
|
let sig = secp256k1::schnorr::Signature::from_byte_array(self.value);
|
||||||
|
secp.verify_schnorr(&sig, &bytes, &pk).is_ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Signature {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
// Serialize as a slice
|
||||||
|
serializer.serialize_bytes(&self.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Signature {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let bytes: &[u8] = Deserialize::deserialize(deserializer)?;
|
||||||
|
if bytes.len() != 64 {
|
||||||
|
return Err(serde::de::Error::invalid_length(
|
||||||
|
bytes.len(),
|
||||||
|
&"expected 64 bytes",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let mut value = [0u8; 64];
|
||||||
|
value.copy_from_slice(bytes);
|
||||||
|
Ok(Signature { value })
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -87,7 +87,7 @@ fn transition_from_authenticated_transfer_program_invocation_non_default_account
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn transition_from_chained_authenticated_transfer_program_invocations() {
|
fn transition_from_chained_authenticated_transfer_program_invocations() {
|
||||||
let key1 = PrivateKey::try_new([1; 32]).unwrap();
|
let key1 = PrivateKey::try_new([8; 32]).unwrap();
|
||||||
let address1 = Address::from_public_key(&PublicKey::new(&key1));
|
let address1 = Address::from_public_key(&PublicKey::new(&key1));
|
||||||
let key2 = PrivateKey::try_new([2; 32]).unwrap();
|
let key2 = PrivateKey::try_new([2; 32]).unwrap();
|
||||||
let address2 = Address::from_public_key(&PublicKey::new(&key2));
|
let address2 = Address::from_public_key(&PublicKey::new(&key2));
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user