mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-01-02 13:23:10 +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 {
|
||||
// TODO: implement
|
||||
// TODO: Check specs
|
||||
Address::new(public_key.0)
|
||||
}
|
||||
|
||||
|
||||
@ -29,4 +29,39 @@ impl Message {
|
||||
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();
|
||||
for ((signature, public_key), nonce) in witness_set.iter_signatures().zip(&message.nonces) {
|
||||
// Check the signature is valid
|
||||
if !signature.is_valid_for(message, public_key) {
|
||||
return Err(NssaError::InvalidInput(
|
||||
"Invalid signature for given message and public key".into(),
|
||||
));
|
||||
}
|
||||
// Check the signatures are valid
|
||||
if !witness_set.is_valid_for(message) {
|
||||
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 address = Address::from_public_key(public_key);
|
||||
let current_nonce = state.get_account_by_address(&address).nonce;
|
||||
let signer_addresses = self.signer_addresses();
|
||||
// Check nonces corresponds to the current nonces on the public state.
|
||||
for (address, nonce) in signer_addresses.iter().zip(&message.nonces) {
|
||||
let current_nonce = state.get_account_by_address(address).nonce;
|
||||
if current_nonce != *nonce {
|
||||
return Err(NssaError::InvalidInput("Nonce mismatch".into()));
|
||||
}
|
||||
|
||||
authorized_addresses.push(address);
|
||||
}
|
||||
|
||||
// Build pre_states for execution
|
||||
@ -97,7 +94,7 @@ impl PublicTransaction {
|
||||
.iter()
|
||||
.map(|address| AccountWithMetadata {
|
||||
account: state.get_account_by_address(address),
|
||||
is_authorized: authorized_addresses.contains(address),
|
||||
is_authorized: signer_addresses.contains(address),
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
||||
@ -7,44 +7,9 @@ pub struct WitnessSet {
|
||||
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 {
|
||||
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
|
||||
.iter()
|
||||
.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)> {
|
||||
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]
|
||||
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 key2 = PrivateKey::try_new([2; 32]).unwrap();
|
||||
let address2 = Address::from_public_key(&PublicKey::new(&key2));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user