Skip to content

Commit 75b0bf8

Browse files
author
Liu Chuankai
committed
test: 💍 Add test about opentx simple multisig transfer
1 parent 2c6c930 commit 75b0bf8

File tree

3 files changed

+181
-6
lines changed

3 files changed

+181
-6
lines changed

src/tests/opentx.rs

Lines changed: 138 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,6 @@ fn test_omnilock_simple_hash(mut cfg: OmniLockConfig) {
101101
let mut tx = builder
102102
.build_balanced(&mut cell_collector, &ctx, &ctx, &ctx, &balancer, &unlockers)
103103
.unwrap();
104-
tx = OmniLockTransferBuilder::remove_open_out(tx);
105-
106104
let mut rng = rand::thread_rng();
107105
let salt: u32 = rng.gen();
108106
let wit = OpentxWitness::new_sig_all_relative(&tx, Some(salt)).unwrap();
@@ -172,12 +170,149 @@ fn test_omnilock_simple_hash(mut cfg: OmniLockConfig) {
172170
assert_eq!(tx.inputs().len(), 2);
173171
assert_eq!(tx.outputs().len(), 2);
174172
assert_eq!(tx.output(0).unwrap(), output);
175-
assert_eq!(tx.output(1).unwrap().lock(), receiver);
173+
let output1 = tx.output(1).unwrap();
174+
assert_eq!(output1.lock(), receiver);
175+
let receiver_capacity: u64 = output1.capacity().unpack();
176+
assert!(receiver_capacity - 100 * ONE_CKB < ONE_CKB );
177+
let witnesses = tx
178+
.witnesses()
179+
.into_iter()
180+
.map(|w| w.raw_data())
181+
.collect::<Vec<_>>();
182+
assert_eq!(witnesses.len(), 2);
183+
ctx.verify(tx, FEE_RATE).unwrap();
184+
}
185+
186+
/// multisig(200) => multisig(exchange 199) + open pay 1, locked by account0, account1, account2
187+
/// account3(400) => account2(401 - transaction fee)
188+
#[test]
189+
fn test_omnilock_transfer_from_multisig() {
190+
let unlock_mode = OmniUnlockMode::Normal;
191+
let lock_args = vec![
192+
ACCOUNT0_ARG.clone(),
193+
ACCOUNT1_ARG.clone(),
194+
ACCOUNT2_ARG.clone(),
195+
];
196+
let multi_cfg = MultisigConfig::new_with(lock_args, 0, 2).unwrap();
197+
let mut cfg = OmniLockConfig::new_multisig(multi_cfg);
198+
cfg.set_opentx_mode();
199+
200+
let sender = build_omnilock_script(&cfg);
201+
let receiver = build_sighash_script(ACCOUNT3_ARG);
202+
203+
let ctx = init_context(
204+
vec![(OMNILOCK_BIN, true)],
205+
vec![
206+
(sender.clone(), Some(200 * ONE_CKB)),
207+
(sender.clone(), Some(300 * ONE_CKB)),
208+
(receiver.clone(), Some(400 * ONE_CKB)),
209+
(receiver.clone(), Some(500 * ONE_CKB)),
210+
(receiver.clone(), Some(600 * ONE_CKB)),
211+
],
212+
);
213+
214+
let output = CellOutput::new_builder()
215+
.capacity((199 * ONE_CKB).pack())
216+
.lock(receiver.clone())
217+
.build();
218+
let builder = OmniLockTransferBuilder::new_open(
219+
(1 * ONE_CKB).into(),
220+
vec![(output.clone(), Bytes::default())],
221+
cfg.clone(),
222+
None,
223+
);
224+
let placeholder_witness = cfg.placeholder_witness(unlock_mode).unwrap();
225+
let balancer =
226+
CapacityBalancer::new_simple(sender.clone(), placeholder_witness.clone(), ZERO_FEE_RATE);
227+
228+
let mut cell_collector = ctx.to_live_cells_context();
229+
let account0_key = secp256k1::SecretKey::from_slice(ACCOUNT0_KEY.as_bytes()).unwrap();
230+
let account2_key = secp256k1::SecretKey::from_slice(ACCOUNT2_KEY.as_bytes()).unwrap();
231+
let unlockers = build_omnilock_unlockers(account0_key, cfg.clone(), unlock_mode);
232+
let mut tx = builder
233+
.build_balanced(&mut cell_collector, &ctx, &ctx, &ctx, &balancer, &unlockers)
234+
.unwrap();
235+
// add opentx hash data
236+
let mut rng = rand::thread_rng();
237+
let salt: u32 = rng.gen();
238+
let wit = OpentxWitness::new_sig_all_relative(&tx, Some(salt)).unwrap();
239+
cfg.set_opentx_input(wit);
240+
tx = OmniLockTransferBuilder::update_opentx_witness(
241+
tx,
242+
&cfg,
243+
OmniUnlockMode::Normal,
244+
&ctx,
245+
&sender,
246+
)
247+
.unwrap();
248+
for key in [account0_key, account2_key] {
249+
let unlockers = build_omnilock_unlockers(key, cfg.clone(), unlock_mode);
250+
let (new_tx, new_locked_groups) = unlock_tx(tx.clone(), &ctx, &unlockers).unwrap();
251+
assert!(new_locked_groups.is_empty());
252+
tx = new_tx;
253+
}
254+
255+
println!(
256+
"> tx: {}",
257+
serde_json::to_string_pretty(&json_types::TransactionView::from(tx.clone())).unwrap()
258+
);
259+
// use the opentx
260+
261+
// Build ScriptUnlocker
262+
let account3_key = secp256k1::SecretKey::from_slice(ACCOUNT3_KEY.as_bytes()).unwrap();
263+
let signer = SecpCkbRawKeySigner::new_with_secret_keys(vec![account3_key]);
264+
let sighash_unlocker = SecpSighashUnlocker::from(Box::new(signer) as Box<_>);
265+
let sighash_script_id = ScriptId::new_type(SIGHASH_TYPE_HASH.clone());
266+
let mut unlockers = HashMap::default();
267+
unlockers.insert(
268+
sighash_script_id,
269+
Box::new(sighash_unlocker) as Box<dyn ScriptUnlocker>,
270+
);
271+
272+
// Build CapacityBalancer
273+
let placeholder_witness = WitnessArgs::new_builder()
274+
.lock(Some(Bytes::from(vec![0u8; 65])).pack())
275+
.build();
276+
let balancer =
277+
CapacityBalancer::new_simple(receiver.clone(), placeholder_witness.clone(), 1000);
278+
// // Build the transaction
279+
let query = CellQueryOptions::new_lock(receiver.clone());
280+
let (inputs, total_capacity) = cell_collector.collect_live_cells(&query, false).unwrap();
281+
let input = &inputs[0];
282+
let input_output = &input.out_point;
283+
println!("{:#x} total_capacity: {}", input_output, total_capacity);
284+
// let output = CellOutput::new_builder()
285+
// .lock(receiver.clone())
286+
// .capacity((100 * ONE_CKB).pack())
287+
// .build();
288+
let builder = CapacityTransferBuilderWithTransaction::new(
289+
vec![/*(output.clone(), Bytes::default())*/],
290+
tx,
291+
);
292+
let (tx, still_locked_groups) = builder
293+
.build_unlocked(&mut cell_collector, &ctx, &ctx, &ctx, &balancer, &unlockers)
294+
.unwrap();
295+
296+
println!(
297+
"> tx: {}",
298+
serde_json::to_string_pretty(&json_types::TransactionView::from(tx.clone())).unwrap()
299+
);
300+
assert_eq!(1, still_locked_groups.len());
301+
assert_eq!(tx.header_deps().len(), 0);
302+
assert_eq!(tx.cell_deps().len(), 2);
303+
assert_eq!(tx.inputs().len(), 2);
304+
assert_eq!(tx.outputs().len(), 2);
305+
assert_eq!(tx.output(0).unwrap(), output);
306+
let output1 = tx.output(1).unwrap();
307+
assert_eq!(output1.lock(), receiver);
308+
let receiver_capacity: u64 = output1.capacity().unpack();
309+
assert!(receiver_capacity - 400 * ONE_CKB < ONE_CKB );
176310
let witnesses = tx
177311
.witnesses()
178312
.into_iter()
179313
.map(|w| w.raw_data())
180314
.collect::<Vec<_>>();
181315
assert_eq!(witnesses.len(), 2);
316+
assert_eq!(witnesses[1].len(), placeholder_witness.as_slice().len());
182317
ctx.verify(tx, FEE_RATE).unwrap();
183318
}

src/tx_builder/omni_lock.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::HashSet;
1+
use std::collections::{HashSet, HashMap};
22

33
use ckb_types::{
44
bytes::Bytes,
@@ -8,10 +8,10 @@ use ckb_types::{
88
H256,
99
};
1010

11-
use super::{TxBuilder, TxBuilderError};
11+
use super::{TxBuilder, TxBuilderError, CapacityBalancer, fill_placeholder_witnesses, balance_tx_capacity};
1212
use crate::{
1313
traits::{CellCollector, CellDepResolver, HeaderDepResolver, TransactionDependencyProvider},
14-
unlock::{omni_lock::ConfigError, OmniLockConfig, OmniUnlockMode},
14+
unlock::{omni_lock::ConfigError, OmniLockConfig, OmniUnlockMode, ScriptUnlocker},
1515
};
1616
use crate::{types::ScriptId, HumanCapacity};
1717

@@ -206,4 +206,39 @@ impl TxBuilder for OmniLockTransferBuilder {
206206
.set_outputs_data(outputs_data)
207207
.build())
208208
}
209+
210+
/// Build balanced transaction that ready to sign:
211+
/// * Build base transaction
212+
/// * Fill placeholder witness for lock script
213+
/// * balance the capacity
214+
fn build_balanced(
215+
&self,
216+
cell_collector: &mut dyn CellCollector,
217+
cell_dep_resolver: &dyn CellDepResolver,
218+
header_dep_resolver: &dyn HeaderDepResolver,
219+
tx_dep_provider: &dyn TransactionDependencyProvider,
220+
balancer: &CapacityBalancer,
221+
unlockers: &HashMap<ScriptId, Box<dyn ScriptUnlocker>>,
222+
) -> Result<TransactionView, TxBuilderError> {
223+
let base_tx = self.build_base(
224+
cell_collector,
225+
cell_dep_resolver,
226+
header_dep_resolver,
227+
tx_dep_provider,
228+
)?;
229+
let (tx_filled_witnesses, _) =
230+
fill_placeholder_witnesses(base_tx, tx_dep_provider, unlockers)?;
231+
let mut tx = balance_tx_capacity(
232+
&tx_filled_witnesses,
233+
balancer,
234+
cell_collector,
235+
tx_dep_provider,
236+
cell_dep_resolver,
237+
header_dep_resolver,
238+
)?;
239+
if self.cfg.is_opentx_mode() {
240+
tx = OmniLockTransferBuilder::remove_open_out(tx);
241+
}
242+
Ok(tx)
243+
}
209244
}

src/unlock/omni_lock.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,11 @@ impl OmniLockConfig {
558558
self.omni_lock_flags.set(OmniLockFlags::OPENTX, true);
559559
}
560560

561+
/// Check if it contains open transaction mode
562+
pub fn is_opentx_mode(&self) -> bool {
563+
self.omni_lock_flags.contains(OmniLockFlags::OPENTX)
564+
}
565+
561566
/// Clear the open transaction input data, and clear OmniLockFlags::OPENTX from omni_lock_flags.
562567
pub fn clear_opentx_input(&mut self) {
563568
self.omni_lock_flags.set(OmniLockFlags::OPENTX, false);

0 commit comments

Comments
 (0)