Skip to content

Commit

Permalink
Refactor: Separate authority and chain owner keys. (#3366)
Browse files Browse the repository at this point in the history
## Motivation

In order to sign blocks with secp256k1 signatures (while keeping ed25519
for chain owners/block proposals) we need to be able to differentiate
between different type of keys. At the moment we have two types of
actors in the system: validators and chain owners. Fact that both use
the same public-key cryptography (ed25519) is orthoganal and will change
soon. Authorities (validators) will need to be able to act as chain
owners in the cases where they become _fallback_ chain owners. They will
also receive rewards for their work so they need to be able to transfer
these tokens.

## Proposal

Use type aliases `Authority*` and `Account*` (`*PrivateKey`,
`*PublicKey`, `*Signature`) for validators and chain owners
respectively. Right now it's a type alias (without a newtype wrapper) as
these types will become enums in the near future.

## Test Plan

CI should catch any regressions (but there should not be any, it's just
a rename).

## Release Plan

- Nothing to do / These changes follow the usual release cycle.


## Links
- [reviewer
checklist](https://github.com/linera-io/linera-protocol/blob/main/CONTRIBUTING.md#reviewer-checklist)
  • Loading branch information
deuszx authored Feb 19, 2025
1 parent ab7d748 commit a18d2df
Show file tree
Hide file tree
Showing 43 changed files with 363 additions and 313 deletions.
10 changes: 5 additions & 5 deletions examples/hex-game/tests/hex_game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@

use hex_game::{HexAbi, Operation, Timeouts};
use linera_sdk::{
base::{Amount, ChainDescription, SigningKey, TimeDelta},
base::{AccountSecretKey, Amount, ChainDescription, TimeDelta},
test::{ActiveChain, QueryOutcome, TestValidator},
};

#[test_log::test(tokio::test)]
async fn hex_game() {
let key_pair1 = SigningKey::generate();
let key_pair2 = SigningKey::generate();
let key_pair1 = AccountSecretKey::generate();
let key_pair2 = AccountSecretKey::generate();

let (validator, app_id, creation_chain) =
TestValidator::with_current_application::<HexAbi, _, _>((), Timeouts::default()).await;
Expand Down Expand Up @@ -74,8 +74,8 @@ async fn hex_game() {

#[tokio::test]
async fn hex_game_clock() {
let key_pair1 = SigningKey::generate();
let key_pair2 = SigningKey::generate();
let key_pair1 = AccountSecretKey::generate();
let key_pair2 = AccountSecretKey::generate();

let timeouts = Timeouts {
start_time: TimeDelta::from_secs(60),
Expand Down
19 changes: 16 additions & 3 deletions linera-base/src/crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,27 @@ mod secp256k1;
use std::{io, num::ParseIntError};

use alloy_primitives::FixedBytes;
pub use ed25519::{
Ed25519PublicKey as PublicKey, Ed25519SecretKey as SigningKey, Ed25519Signature as Signature,
};
use ed25519_dalek::{self as dalek};
pub use hash::*;
use serde::{Deserialize, Serialize};
use thiserror::Error;

/// The public key of a validator.
pub type ValidatorPublicKey = ed25519::Ed25519PublicKey;
/// The private key of a validator.
pub type ValidatorSecretKey = ed25519::Ed25519SecretKey;
/// The signature of a validator.
pub type ValidatorSignature = ed25519::Ed25519Signature;

/// The public key of a chain owner.
/// The corresponding private key is allowed to propose blocks
/// on the chain and transfer account's tokens.
pub type AccountPublicKey = ed25519::Ed25519PublicKey;
/// The private key of a chain owner.
pub type AccountSecretKey = ed25519::Ed25519SecretKey;
/// The signature of a chain owner.
pub type AccountSignature = ed25519::Ed25519Signature;

/// Error type for cryptographic errors.
#[derive(Error, Debug)]
#[allow(missing_docs)]
Expand Down
6 changes: 3 additions & 3 deletions linera-base/src/ownership.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,13 @@ pub enum ChangeApplicationPermissionsError {
#[cfg(test)]
mod tests {
use super::*;
use crate::crypto::SigningKey;
use crate::crypto::AccountSecretKey;

#[test]
fn test_ownership_round_timeouts() {
let super_pub_key = SigningKey::generate().public();
let super_pub_key = AccountSecretKey::generate().public();
let super_owner = Owner::from(super_pub_key);
let pub_key = SigningKey::generate().public();
let pub_key = AccountSecretKey::generate().public();
let owner = Owner::from(pub_key);

let ownership = ChainOwnership {
Expand Down
4 changes: 2 additions & 2 deletions linera-base/src/unit_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use linera_witty::{Layout, WitLoad, WitStore};
use test_case::test_case;

use crate::{
crypto::{CryptoHash, PublicKey},
crypto::{AccountPublicKey, CryptoHash},
data_types::{Amount, BlockHeight, Resources, SendMessageRequest, TimeDelta, Timestamp},
identifiers::{
Account, AccountOwner, ApplicationId, BytecodeId, ChainId, ChannelName, Destination,
Expand All @@ -20,7 +20,7 @@ use crate::{

/// Test roundtrip of types used in the WIT interface.
#[test_case(CryptoHash::test_hash("hash"); "of_crypto_hash")]
#[test_case(PublicKey::test_key(255); "of_public_key")]
#[test_case(AccountPublicKey::test_key(255); "of_public_key")]
#[test_case(Amount::from_tokens(500); "of_amount")]
#[test_case(BlockHeight(1095); "of_block_height")]
#[test_case(Timestamp::from(6_400_003); "of_timestamp")]
Expand Down
4 changes: 2 additions & 2 deletions linera-chain/src/certificate/confirmed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// SPDX-License-Identifier: Apache-2.0

use linera_base::{
crypto::Signature,
crypto::ValidatorSignature,
data_types::Round,
hashed::Hashed,
identifiers::{BlobId, ChainId, MessageId},
Expand Down Expand Up @@ -92,7 +92,7 @@ impl<'de> Deserialize<'de> for GenericCertificate<ConfirmedBlock> {
struct Helper {
value: Hashed<ConfirmedBlock>,
round: Round,
signatures: Vec<(ValidatorName, Signature)>,
signatures: Vec<(ValidatorName, ValidatorSignature)>,
}

let helper = Helper::deserialize(deserializer)?;
Expand Down
16 changes: 8 additions & 8 deletions linera-chain/src/certificate/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use custom_debug_derive::Debug;
use linera_base::{
crypto::{CryptoHash, Signature},
crypto::{CryptoHash, ValidatorSignature},
data_types::Round,
hashed::Hashed,
};
Expand All @@ -18,14 +18,14 @@ use crate::{data_types::LiteValue, ChainError};
pub struct GenericCertificate<T> {
value: Hashed<T>,
pub round: Round,
signatures: Vec<(ValidatorName, Signature)>,
signatures: Vec<(ValidatorName, ValidatorSignature)>,
}

impl<T> GenericCertificate<T> {
pub fn new(
value: Hashed<T>,
round: Round,
mut signatures: Vec<(ValidatorName, Signature)>,
mut signatures: Vec<(ValidatorName, ValidatorSignature)>,
) -> Self {
signatures.sort_by_key(|&(validator_name, _)| validator_name);

Expand Down Expand Up @@ -61,25 +61,25 @@ impl<T> GenericCertificate<T> {
self.value.hash()
}

pub fn destructure(self) -> (Hashed<T>, Round, Vec<(ValidatorName, Signature)>) {
pub fn destructure(self) -> (Hashed<T>, Round, Vec<(ValidatorName, ValidatorSignature)>) {
(self.value, self.round, self.signatures)
}

pub fn signatures(&self) -> &Vec<(ValidatorName, Signature)> {
pub fn signatures(&self) -> &Vec<(ValidatorName, ValidatorSignature)> {
&self.signatures
}

#[cfg(with_testing)]
pub fn signatures_mut(&mut self) -> &mut Vec<(ValidatorName, Signature)> {
pub fn signatures_mut(&mut self) -> &mut Vec<(ValidatorName, ValidatorSignature)> {
&mut self.signatures
}

/// Adds a signature to the certificate's list of signatures
/// It's the responsibility of the caller to not insert duplicates
pub fn add_signature(
&mut self,
signature: (ValidatorName, Signature),
) -> &Vec<(ValidatorName, Signature)> {
signature: (ValidatorName, ValidatorSignature),
) -> &Vec<(ValidatorName, ValidatorSignature)> {
let index = self
.signatures
.binary_search_by(|(name, _)| name.cmp(&signature.0))
Expand Down
6 changes: 3 additions & 3 deletions linera-chain/src/certificate/lite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use std::borrow::Cow;

use linera_base::{crypto::Signature, data_types::Round, hashed::Hashed};
use linera_base::{crypto::ValidatorSignature, data_types::Round, hashed::Hashed};
use linera_execution::committee::{Committee, ValidatorName};
use serde::{Deserialize, Serialize};

Expand All @@ -23,14 +23,14 @@ pub struct LiteCertificate<'a> {
/// The round in which the value was certified.
pub round: Round,
/// Signatures on the value.
pub signatures: Cow<'a, [(ValidatorName, Signature)]>,
pub signatures: Cow<'a, [(ValidatorName, ValidatorSignature)]>,
}

impl<'a> LiteCertificate<'a> {
pub fn new(
value: LiteValue,
round: Round,
mut signatures: Vec<(ValidatorName, Signature)>,
mut signatures: Vec<(ValidatorName, ValidatorSignature)>,
) -> Self {
signatures.sort_by_key(|&(validator_name, _)| validator_name);

Expand Down
4 changes: 2 additions & 2 deletions linera-chain/src/certificate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use std::collections::BTreeSet;

pub use generic::GenericCertificate;
use linera_base::{
crypto::Signature,
crypto::ValidatorSignature,
data_types::{BlockHeight, Round},
identifiers::{BlobId, ChainId},
};
Expand Down Expand Up @@ -76,7 +76,7 @@ impl Certificate {
}
}

pub fn signatures(&self) -> &Vec<(ValidatorName, Signature)> {
pub fn signatures(&self) -> &Vec<(ValidatorName, ValidatorSignature)> {
match self {
Certificate::Validated(cert) => cert.signatures(),
Certificate::Confirmed(cert) => cert.signatures(),
Expand Down
4 changes: 2 additions & 2 deletions linera-chain/src/certificate/timeout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Copyright (c) Zefchain Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use linera_base::{crypto::Signature, data_types::Round, hashed::Hashed};
use linera_base::{crypto::ValidatorSignature, data_types::Round, hashed::Hashed};
use linera_execution::committee::ValidatorName;
use serde::{
ser::{Serialize, SerializeStruct, Serializer},
Expand Down Expand Up @@ -49,7 +49,7 @@ impl<'de> Deserialize<'de> for GenericCertificate<Timeout> {
struct Inner {
value: Hashed<Timeout>,
round: Round,
signatures: Vec<(ValidatorName, Signature)>,
signatures: Vec<(ValidatorName, ValidatorSignature)>,
}
let inner = Inner::deserialize(deserializer)?;
if !crate::data_types::is_strictly_ordered(&inner.signatures) {
Expand Down
6 changes: 4 additions & 2 deletions linera-chain/src/certificate/validated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// Copyright (c) Zefchain Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use linera_base::{crypto::Signature, data_types::Round, hashed::Hashed, identifiers::BlobId};
use linera_base::{
crypto::ValidatorSignature, data_types::Round, hashed::Hashed, identifiers::BlobId,
};
use linera_execution::committee::ValidatorName;
use serde::{
ser::{Serialize, SerializeStruct, Serializer},
Expand Down Expand Up @@ -65,7 +67,7 @@ impl<'de> Deserialize<'de> for GenericCertificate<ValidatedBlock> {
struct Inner {
value: Hashed<ValidatedBlock>,
round: Round,
signatures: Vec<(ValidatorName, Signature)>,
signatures: Vec<(ValidatorName, ValidatorSignature)>,
}
let inner = Inner::deserialize(deserializer)?;
if !crate::data_types::is_strictly_ordered(&inner.signatures) {
Expand Down
37 changes: 20 additions & 17 deletions linera-chain/src/data_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ use async_graphql::SimpleObject;
use custom_debug_derive::Debug;
use linera_base::{
bcs,
crypto::{BcsHashable, BcsSignable, CryptoError, CryptoHash, PublicKey, Signature, SigningKey},
crypto::{
AccountPublicKey, AccountSecretKey, AccountSignature, BcsHashable, BcsSignable,
CryptoError, CryptoHash, ValidatorSecretKey, ValidatorSignature,
},
data_types::{Amount, BlockHeight, Event, OracleResponse, Round, Timestamp},
doc_scalar, ensure,
hashed::Hashed,
Expand Down Expand Up @@ -286,8 +289,8 @@ pub enum Medium {
pub struct BlockProposal {
pub content: ProposalContent,
pub owner: Owner,
pub public_key: PublicKey,
pub signature: Signature,
pub public_key: AccountPublicKey,
pub signature: AccountSignature,
#[debug(skip_if = Option::is_none)]
pub validated_block_certificate: Option<LiteCertificate<'static>>,
}
Expand Down Expand Up @@ -423,17 +426,17 @@ pub struct Vote<T> {
pub value: Hashed<T>,
pub round: Round,
pub validator: ValidatorName,
pub signature: Signature,
pub signature: ValidatorSignature,
}

impl<T> Vote<T> {
/// Use signing key to create a signed object.
pub fn new(value: Hashed<T>, round: Round, key_pair: &SigningKey) -> Self
pub fn new(value: Hashed<T>, round: Round, key_pair: &ValidatorSecretKey) -> Self
where
T: CertificateValue,
{
let hash_and_round = VoteValue(value.hash(), round, T::KIND);
let signature = Signature::new(&hash_and_round, key_pair);
let signature = ValidatorSignature::new(&hash_and_round, key_pair);
Self {
value,
round,
Expand Down Expand Up @@ -468,7 +471,7 @@ pub struct LiteVote {
pub value: LiteValue,
pub round: Round,
pub validator: ValidatorName,
pub signature: Signature,
pub signature: ValidatorSignature,
}

impl LiteVote {
Expand Down Expand Up @@ -734,13 +737,13 @@ pub struct ProposalContent {
}

impl BlockProposal {
pub fn new_initial(round: Round, block: ProposedBlock, secret: &SigningKey) -> Self {
pub fn new_initial(round: Round, block: ProposedBlock, secret: &AccountSecretKey) -> Self {
let content = ProposalContent {
round,
block,
outcome: None,
};
let signature = Signature::new(&content, secret);
let signature = AccountSignature::new(&content, secret);
Self {
content,
public_key: secret.public(),
Expand All @@ -753,7 +756,7 @@ impl BlockProposal {
pub fn new_retry(
round: Round,
validated_block_certificate: ValidatedBlockCertificate,
secret: &SigningKey,
secret: &AccountSecretKey,
) -> Self {
let lite_cert = validated_block_certificate.lite_certificate().cloned();
let block = validated_block_certificate.into_inner().into_inner();
Expand All @@ -763,7 +766,7 @@ impl BlockProposal {
round,
outcome: Some(executed_block.outcome),
};
let signature = Signature::new(&content, secret);
let signature = AccountSignature::new(&content, secret);
Self {
content,
public_key: secret.public(),
Expand Down Expand Up @@ -814,9 +817,9 @@ impl BlockProposal {

impl LiteVote {
/// Uses the signing key to create a signed object.
pub fn new(value: LiteValue, round: Round, key_pair: &SigningKey) -> Self {
pub fn new(value: LiteValue, round: Round, key_pair: &ValidatorSecretKey) -> Self {
let hash_and_round = VoteValue(value.value_hash, round, value.kind);
let signature = Signature::new(&hash_and_round, key_pair);
let signature = ValidatorSignature::new(&hash_and_round, key_pair);
Self {
value,
round,
Expand Down Expand Up @@ -856,7 +859,7 @@ impl<'a, T> SignatureAggregator<'a, T> {
pub fn append(
&mut self,
validator: ValidatorName,
signature: Signature,
signature: ValidatorSignature,
) -> Result<Option<GenericCertificate<T>>, ChainError>
where
T: CertificateValue,
Expand Down Expand Up @@ -887,7 +890,7 @@ impl<'a, T> SignatureAggregator<'a, T> {

// Checks if the array slice is strictly ordered. That means that if the array
// has duplicates, this will return False, even if the array is sorted
pub(crate) fn is_strictly_ordered(values: &[(ValidatorName, Signature)]) -> bool {
pub(crate) fn is_strictly_ordered(values: &[(ValidatorName, ValidatorSignature)]) -> bool {
values.windows(2).all(|pair| pair[0].0 < pair[1].0)
}

Expand All @@ -896,7 +899,7 @@ pub(crate) fn check_signatures(
value_hash: CryptoHash,
certificate_kind: CertificateKind,
round: Round,
signatures: &[(ValidatorName, Signature)],
signatures: &[(ValidatorName, ValidatorSignature)],
committee: &Committee,
) -> Result<(), ChainError> {
// Check the quorum.
Expand All @@ -920,7 +923,7 @@ pub(crate) fn check_signatures(
);
// All that is left is checking signatures!
let hash_and_round = VoteValue(value_hash, round, certificate_kind);
Signature::verify_batch(&hash_and_round, signatures.iter().map(|(v, s)| (&v.0, s)))?;
ValidatorSignature::verify_batch(&hash_and_round, signatures.iter().map(|(v, s)| (&v.0, s)))?;
Ok(())
}

Expand Down
Loading

0 comments on commit a18d2df

Please sign in to comment.