Skip to content

Commit

Permalink
crypto: Add multisig support
Browse files Browse the repository at this point in the history
  • Loading branch information
joyqvq committed Jan 6, 2023
1 parent fa4b7fc commit 201620a
Show file tree
Hide file tree
Showing 15 changed files with 609 additions and 39 deletions.
2 changes: 1 addition & 1 deletion crates/sui-core/src/unit_tests/authority_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -893,7 +893,7 @@ async fn test_handle_transfer_transaction_bad_signature() {
bad_signature_transfer_transaction
.data_mut_for_testing()
.tx_signature =
Signature::new_secure(&transfer_transaction.data().intent_message, &unknown_key);
Signature::new_secure(&transfer_transaction.data().intent_message, &unknown_key).into();

assert!(client
.handle_transaction(bad_signature_transfer_transaction)
Expand Down
5 changes: 3 additions & 2 deletions crates/sui-json-rpc-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use sui_types::base_types::{
};
use sui_types::coin::CoinMetadata;
use sui_types::committee::EpochId;
use sui_types::crypto::{AuthorityStrongQuorumSignInfo, Signature};
use sui_types::crypto::AuthorityStrongQuorumSignInfo;
use sui_types::dynamic_field::DynamicFieldInfo;
use sui_types::error::{ExecutionError, SuiError};
use sui_types::event::{BalanceChangeType, Event, EventID};
Expand All @@ -52,6 +52,7 @@ use sui_types::messages::{
};
use sui_types::messages_checkpoint::CheckpointSequenceNumber;
use sui_types::move_package::{disassemble_modules, MovePackage};
use sui_types::multisig::GenericSignature;
use sui_types::object::{
Data, MoveObject, Object, ObjectFormatOptions, ObjectRead, Owner, PastObjectRead,
};
Expand Down Expand Up @@ -1774,7 +1775,7 @@ pub struct SuiCertifiedTransaction {
pub transaction_digest: TransactionDigest,
pub data: SuiTransactionData,
/// tx_signature is signed by the transaction sender, committing to the intent message containing the transaction data and intent.
pub tx_signature: Signature,
pub tx_signature: GenericSignature,
/// authority signature information, if available, is signed by an authority, applied on `data`.
pub auth_sign_info: AuthorityStrongQuorumSignInfo,
}
Expand Down
106 changes: 105 additions & 1 deletion crates/sui-open-rpc/spec/openrpc.json
Original file line number Diff line number Diff line change
Expand Up @@ -2742,7 +2742,7 @@
"description": "tx_signature is signed by the transaction sender, committing to the intent message containing the transaction data and intent.",
"allOf": [
{
"$ref": "#/components/schemas/Signature"
"$ref": "#/components/schemas/GenericSignature"
}
]
}
Expand Down Expand Up @@ -2842,6 +2842,58 @@
}
}
},
"CompressedSignature": {
"oneOf": [
{
"type": "object",
"required": [
"Ed25519"
],
"properties": {
"Ed25519": {
"$ref": "#/components/schemas/Base64"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"Secp256k1"
],
"properties": {
"Secp256k1": {
"$ref": "#/components/schemas/Base64"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"Secp256r1"
],
"properties": {
"Secp256r1": {
"$ref": "#/components/schemas/Base64"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"BLS12381"
],
"properties": {
"BLS12381": {
"$ref": "#/components/schemas/Base64"
}
},
"additionalProperties": false
}
]
},
"Data": {
"oneOf": [
{
Expand Down Expand Up @@ -3856,6 +3908,16 @@
}
}
},
"GenericSignature": {
"anyOf": [
{
"$ref": "#/components/schemas/MultiSignature"
},
{
"$ref": "#/components/schemas/Signature"
}
]
},
"Hex": {
"description": "Hex string encoding.",
"type": "string"
Expand Down Expand Up @@ -4092,6 +4154,48 @@
}
]
},
"MultiPublicKey": {
"type": "object",
"required": [
"pks",
"threshold"
],
"properties": {
"pks": {
"type": "array",
"items": {
"$ref": "#/components/schemas/PublicKey"
}
},
"threshold": {
"type": "integer",
"format": "uint",
"minimum": 0.0
}
}
},
"MultiSignature": {
"type": "object",
"required": [
"bitmap",
"multi_pk",
"sigs"
],
"properties": {
"bitmap": {
"$ref": "#/components/schemas/Base64"
},
"multi_pk": {
"$ref": "#/components/schemas/MultiPublicKey"
},
"sigs": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CompressedSignature"
}
}
}
},
"Object": {
"type": "object",
"required": [
Expand Down
28 changes: 15 additions & 13 deletions crates/sui-open-rpc/src/examples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use sui_types::base_types::{
ObjectDigest, ObjectID, ObjectType, SequenceNumber, SuiAddress, TransactionDigest,
};
use sui_types::crypto::{
get_key_pair_from_rng, AccountKeyPair, AuthorityKeyPair, AuthorityPublicKeyBytes, Signature,
get_key_pair_from_rng, AccountKeyPair, AuthorityKeyPair, AuthorityPublicKeyBytes,
};
use sui_types::crypto::{AuthorityQuorumSignInfo, SuiSignature};
use sui_types::event::EventID;
Expand All @@ -37,6 +37,7 @@ use sui_types::messages::{
CallArg, ExecuteTransactionRequestType, MoveCall, SingleTransactionKind, TransactionData,
TransactionKind, TransferObject,
};
use sui_types::multisig::GenericSignature;
use sui_types::object::{Owner, PACKAGE_VERSION};
use sui_types::query::EventQuery;
use sui_types::query::TransactionQuery;
Expand Down Expand Up @@ -168,7 +169,10 @@ impl RpcExampleProvider {

fn execute_transaction_example(&mut self) -> Examples {
let (data, signature, _, _, result, _) = self.get_transfer_data_response();

let s = match signature {
GenericSignature::Signature(s) => s,
_ => panic!("Unexpected signature type"),
};
Examples::new(
"sui_executeTransaction",
vec![ExamplePairing::new(
Expand All @@ -178,15 +182,9 @@ impl RpcExampleProvider {
"tx_bytes",
json!(Base64::from_bytes(bcs::to_bytes(&data).unwrap().as_slice())),
),
("sig_scheme", json!(signature.scheme())),
(
"signature",
json!(Base64::from_bytes(signature.signature_bytes())),
),
(
"pub_key",
json!(Base64::from_bytes(signature.public_key_bytes())),
),
("sig_scheme", json!(s.scheme())),
("signature", json!(Base64::from_bytes(s.signature_bytes()))),
("pub_key", json!(Base64::from_bytes(s.public_key_bytes()))),
(
"request_type",
json!(ExecuteTransactionRequestType::WaitForLocalExecution),
Expand All @@ -199,6 +197,10 @@ impl RpcExampleProvider {

fn execute_transaction_serialized_sig_example(&mut self) -> Examples {
let (data, signature, _, _, result, _) = self.get_transfer_data_response();
let s = match signature {
GenericSignature::Signature(s) => s,
_ => panic!("Unexpected signature type"),
};
let tx_bytes = TransactionBytes::from_data(data).unwrap();

Examples::new(
Expand All @@ -207,7 +209,7 @@ impl RpcExampleProvider {
"Execute an transaction with serialized signature",
vec![
("tx_bytes", json!(tx_bytes.tx_bytes)),
("signature", json!(Base64::from_bytes(signature.as_ref()))),
("signature", json!(Base64::from_bytes(s.as_ref()))),
(
"request_type",
json!(ExecuteTransactionRequestType::WaitForLocalExecution),
Expand Down Expand Up @@ -443,7 +445,7 @@ impl RpcExampleProvider {
&mut self,
) -> (
TransactionData,
Signature,
GenericSignature,
SuiAddress,
ObjectID,
SuiTransactionResponse,
Expand Down
18 changes: 18 additions & 0 deletions crates/sui-types/src/base_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use crate::error::ExecutionError;
use crate::error::ExecutionErrorKind;
use crate::error::SuiError;
use crate::gas_coin::GasCoin;
use crate::multisig::MultiPublicKey;
use crate::object::{Object, Owner};
use crate::sui_serde::Readable;
use fastcrypto::encoding::{Base58, Base64, Encoding, Hex};
Expand Down Expand Up @@ -261,6 +262,23 @@ impl From<&PublicKey> for SuiAddress {
}
}

impl From<MultiPublicKey> for SuiAddress {
fn from(multi_pk: MultiPublicKey) -> Self {
let mut hasher = Sha3_256::default();
hasher.update(multi_pk.threshold().to_be_bytes());
multi_pk.pubkeys().iter().for_each(|(pk, i)| {
hasher.update(pk.as_ref());
hasher.update(i.to_be_bytes());
});
let g_arr = hasher.finalize();

let mut res = [0u8; SUI_ADDRESS_LENGTH];
// OK to access slice because Sha3_256 should never be shorter than SUI_ADDRESS_LENGTH.
res.copy_from_slice(&AsRef::<[u8]>::as_ref(&g_arr)[..SUI_ADDRESS_LENGTH]);
SuiAddress(res)
}
}

impl TryFrom<&[u8]> for SuiAddress {
type Error = SuiError;

Expand Down
65 changes: 64 additions & 1 deletion crates/sui-types/src/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,13 @@ pub enum SuiKeyPair {
Secp256r1(Secp256r1KeyPair),
}

#[derive(Debug, Clone, PartialEq, Eq, From)]
#[derive(Debug, Clone, PartialEq, Eq, From, JsonSchema)]
pub enum PublicKey {
#[schemars(with = "Base64")]
Ed25519(Ed25519PublicKey),
#[schemars(with = "Base64")]
Secp256k1(Secp256k1PublicKey),
#[schemars(with = "Base64")]
Secp256r1(Secp256r1PublicKey),
}

Expand Down Expand Up @@ -677,6 +680,41 @@ impl Signature {
{
secret.sign(&bcs::to_bytes(&value).expect("Message serialization should not fail"))
}
/// Parse CompressedSignature from the SuiSignature `flag || sig || pk`.
/// This is useful for the multisig to combine partial signature into a multi-public key.
pub fn to_compressed(&self) -> CompressedSignature {
let bytes = self.signature_bytes();
match self.scheme() {
SignatureScheme::ED25519 => {
CompressedSignature::Ed25519(Ed25519Signature::from_bytes(bytes).unwrap())
}
SignatureScheme::Secp256k1 => {
CompressedSignature::Secp256k1(Secp256k1Signature::from_bytes(bytes).unwrap())
}
SignatureScheme::Secp256r1 => {
CompressedSignature::Secp256r1(Secp256r1Signature::from_bytes(bytes).unwrap())
}
_ => todo!("Unsupported signature scheme in multisig"),
}
}

/// Parse PublicKey from the SuiSignature `flag || sig || pk`.
/// This is useful for the multisig to combine partial signature into a multi-public key.
pub fn to_public_key(&self) -> PublicKey {
let bytes = self.public_key_bytes();
match self.scheme() {
SignatureScheme::ED25519 => {
PublicKey::Ed25519(Ed25519PublicKey::from_bytes(bytes).unwrap())
}
SignatureScheme::Secp256k1 => {
PublicKey::Secp256k1(Secp256k1PublicKey::from_bytes(bytes).unwrap())
}
SignatureScheme::Secp256r1 => {
PublicKey::Secp256r1(Secp256r1PublicKey::from_bytes(bytes).unwrap())
}
_ => todo!("Unsupported signature scheme in multisig"),
}
}
}

impl AsRef<[u8]> for Signature {
Expand Down Expand Up @@ -1550,3 +1588,28 @@ impl SignatureScheme {
}
}
}

/// Unlike `enum Signature`, `enum CompressedSignature` does not contain public key.
#[derive(Debug, From, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
pub enum CompressedSignature {
#[schemars(with = "Base64")]
Ed25519(Ed25519Signature),
#[schemars(with = "Base64")]
Secp256k1(Secp256k1Signature),
#[schemars(with = "Base64")]
Secp256r1(Secp256r1Signature),
}

// impl FromStr for Signature {
// type Err = eyre::Report;
// fn from_str(s: &str) -> Result<Self, Self::Err> {
// Self::decode_base64(s).map_err(|e| eyre!("Fail to decode base64 {}", e.to_string()))
// }
// }

// impl FromStr for PublicKey {
// type Err = eyre::Report;
// fn from_str(s: &str) -> Result<Self, Self::Err> {
// Self::decode_base64(s).map_err(|e| eyre!("Fail to decode base64 {}", e.to_string()))
// }
// }
4 changes: 2 additions & 2 deletions crates/sui-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub mod committee;
pub mod crypto;
pub mod dynamic_field;
pub mod event;
pub mod filter;
pub mod gas;
pub mod gas_coin;
pub mod id;
Expand All @@ -39,6 +40,7 @@ pub mod message_envelope;
pub mod messages;
pub mod messages_checkpoint;
pub mod move_package;
pub mod multisig;
pub mod object;
pub mod query;
pub mod quorum_driver_types;
Expand All @@ -48,8 +50,6 @@ pub mod sui_serde;
pub mod sui_system_state;
pub mod temporary_store;

pub mod filter;

#[path = "./unit_tests/utils.rs"]
pub mod utils;

Expand Down
Loading

0 comments on commit 201620a

Please sign in to comment.