-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathmulti_sig_account.rs
More file actions
170 lines (135 loc) · 6.21 KB
/
Copy pathmulti_sig_account.rs
File metadata and controls
170 lines (135 loc) · 6.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//! Example: Multi-signature (M-of-N) accounts
//!
//! This example demonstrates how to:
//! 1. Create a multi-Ed25519 account (M-of-N threshold)
//! 2. Sign with multiple keys
//! 3. Aggregate signatures from multiple parties
//! 4. Submit multi-sig transactions
//!
//! Run with: `cargo run --example multi_sig_account --features "ed25519,faucet"`
use aptos_sdk::{
Aptos, AptosConfig,
account::MultiEd25519Account,
crypto::Ed25519PrivateKey,
transaction::{EntryFunction, TransactionBuilder, TransactionPayload},
};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
println!("=== Multi-Signature Account Example ===\n");
// Connect to devnet
let aptos = Aptos::new(AptosConfig::devnet())?;
println!("Connected to devnet (chain_id: {})", aptos.chain_id().id());
// ==== Part 1: Creating a 2-of-3 Multi-Sig Account ====
println!("\n--- Part 1: Create 2-of-3 Multi-Sig Account ---");
// Generate 3 keys for our multi-sig
let key1 = Ed25519PrivateKey::generate();
let key2 = Ed25519PrivateKey::generate();
let key3 = Ed25519PrivateKey::generate();
println!("Generated 3 Ed25519 keys:");
println!(" Key 1: {}", key1.public_key());
println!(" Key 2: {}", key2.public_key());
println!(" Key 3: {}", key3.public_key());
// Create a multi-sig account where we own all keys (2-of-3)
let multi_account = MultiEd25519Account::new(
vec![key1.clone(), key2.clone(), key3.clone()],
2, // Threshold: 2 signatures required
)?;
println!("\nMulti-sig account created:");
println!(" Address: {}", multi_account.address());
println!(
" Threshold: {}-of-{}",
multi_account.threshold(),
multi_account.num_keys()
);
println!(" Can sign: {}", multi_account.can_sign());
// ==== Part 2: Funding and Using the Multi-Sig ====
println!("\n--- Part 2: Fund Multi-Sig Account ---");
// Fund the multi-sig account
aptos
.fund_account(multi_account.address(), 100_000_000)
.await?;
println!("Funded multi-sig account with 1 APT");
// Wait for funding
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
// Check balance
let balance = aptos.get_balance(multi_account.address()).await?;
println!("Balance: {} APT", balance as f64 / 100_000_000.0);
// ==== Part 3: Signing with Multi-Sig ====
println!("\n--- Part 3: Sign and Submit Transaction ---");
// Create a simple transfer
let recipient = Ed25519PrivateKey::generate().public_key().to_address();
let payload = EntryFunction::apt_transfer(recipient, 1_000_000)?;
// Get sequence number and build transaction
let seq_num = aptos.get_sequence_number(multi_account.address()).await?;
let raw_txn = TransactionBuilder::new()
.sender(multi_account.address())
.sequence_number(seq_num)
.payload(TransactionPayload::EntryFunction(payload))
.chain_id(aptos.chain_id())
.build()?;
// Create signed transaction (signs with the multi-sig account using threshold keys automatically)
let signed = aptos_sdk::transaction::builder::sign_transaction(&raw_txn, &multi_account)?;
println!(
"Signed with threshold {}-of-{}",
multi_account.threshold(),
multi_account.num_keys()
);
// Submit and wait
let result = aptos.submit_and_wait(&signed, None).await?;
let success = result
.data
.get("success")
.and_then(serde_json::value::Value::as_bool);
println!("Transaction success: {success:?}");
// ==== Part 4: Distributed Signing (Multiple Parties) ====
println!("\n--- Part 4: Distributed Signing Scenario ---");
// In a real scenario, each party would have only their key.
// Let's simulate this:
let pub_keys = vec![key1.public_key(), key2.public_key(), key3.public_key()];
// Party 1 only has key1
let party1 = MultiEd25519Account::from_keys(pub_keys.clone(), vec![(0, key1.clone())], 2)?;
println!("Party 1: owns key index 0, can_sign={}", party1.can_sign());
// Party 2 only has key2
let party2 = MultiEd25519Account::from_keys(pub_keys.clone(), vec![(1, key2.clone())], 2)?;
println!("Party 2: owns key index 1, can_sign={}", party2.can_sign());
// Party 3 only has key3
let party3 = MultiEd25519Account::from_keys(pub_keys.clone(), vec![(2, key3.clone())], 2)?;
println!("Party 3: owns key index 2, can_sign={}", party3.can_sign());
// Create another transaction for distributed signing demo
let payload2 = EntryFunction::apt_transfer(recipient, 500_000)?;
let seq_num2 = aptos.get_sequence_number(multi_account.address()).await?;
let raw_txn2 = TransactionBuilder::new()
.sender(multi_account.address())
.sequence_number(seq_num2)
.payload(TransactionPayload::EntryFunction(payload2))
.chain_id(aptos.chain_id())
.build()?;
let message = raw_txn2.signing_message()?;
// Each party creates their signature contribution
let contrib1 = party1.create_signature_contribution(&message, 0)?;
println!("Party 1 signed at index {}", contrib1.0);
let contrib3 = party3.create_signature_contribution(&message, 2)?;
println!("Party 3 signed at index {}", contrib3.0);
// Aggregate the signatures (anyone can do this)
let aggregated = MultiEd25519Account::aggregate_signatures(vec![contrib1, contrib3])?;
println!("Aggregated {} signatures", aggregated.num_signatures());
// Verify the aggregated signature
let multi_pk = multi_account.public_key();
match multi_pk.verify(&message, &aggregated) {
Ok(()) => println!("✓ Aggregated signature verified!"),
Err(e) => println!("✗ Verification failed: {e}"),
}
// ==== Part 5: View-Only Account ====
println!("\n--- Part 5: View-Only Multi-Sig ---");
// Create a view-only account (for monitoring, not signing)
let view_only = MultiEd25519Account::view_only(pub_keys, 2)?;
println!("View-only account created:");
println!(" Address: {}", view_only.address());
println!(" Can sign: {}", view_only.can_sign());
println!(
" Same address as full account: {}",
view_only.address() == multi_account.address()
);
println!("\n✓ Multi-signature example completed!");
Ok(())
}