Skip to content

Commit

Permalink
test: 💍 Add test about open transaction buy udt
Browse files Browse the repository at this point in the history
  • Loading branch information
Liu Chuankai committed Nov 8, 2022
1 parent ec997b1 commit ed930b5
Show file tree
Hide file tree
Showing 6 changed files with 262 additions and 112 deletions.
6 changes: 1 addition & 5 deletions src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1024,11 +1024,7 @@ fn test_udt_transfer() {
ctx.add_live_cell(receiver_input, receiver_output.clone(), receiver_data, None);

let udt_receiver = UdtTargetReceiver::new(TransferAction::Update, receiver_acp_lock, 300);
let builder = UdtTransferBuilder {
type_script,
sender: sender.clone(),
receivers: vec![udt_receiver],
};
let builder = UdtTransferBuilder::new(type_script, sender.clone(), vec![udt_receiver]);
let placeholder_witness = WitnessArgs::new_builder()
.lock(Some(Bytes::from(vec![0u8; 65])).pack())
.build();
Expand Down
6 changes: 1 addition & 5 deletions src/tests/omni_lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1171,11 +1171,7 @@ fn test_omnilock_udt_transfer() {
ctx.add_live_cell(receiver_input, receiver_output.clone(), receiver_data, None);

let udt_receiver = UdtTargetReceiver::new(TransferAction::Update, receiver_acp_lock, 300);
let builder = UdtTransferBuilder {
type_script,
sender: sender.clone(),
receivers: vec![udt_receiver],
};
let builder = UdtTransferBuilder::new(type_script, sender.clone(), vec![udt_receiver]);
let placeholder_witness = WitnessArgs::new_builder()
.lock(Some(Bytes::from(vec![0u8; 65])).pack())
.build();
Expand Down
226 changes: 181 additions & 45 deletions src/tests/opentx.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
use ckb_hash::blake2b_256;
use ckb_jsonrpc_types as json_types;
use std::collections::HashMap;

use crate::{
constants::{ONE_CKB, SIGHASH_TYPE_HASH},
test_util::random_out_point,
tests::{
build_sighash_script, init_context,
omni_lock::{build_omnilock_script, build_omnilock_unlockers, OMNILOCK_BIN},
ACCOUNT0_ARG, ACCOUNT0_KEY, ACCOUNT1_ARG, ACCOUNT1_KEY, ACCOUNT2_ARG, ACCOUNT2_KEY,
ACCOUNT3_ARG, ACCOUNT3_KEY, FEE_RATE,
ACCOUNT3_ARG, ACCOUNT3_KEY, FEE_RATE, SUDT_BIN,
},
traits::{CellCollector, CellQueryOptions, SecpCkbRawKeySigner},
tx_builder::{
omni_lock::OmniLockTransferBuilder, transfer::CapacityTransferBuilderWithTransaction,
omni_lock::OmniLockTransferBuilder, transfer::CapacityTransferBuilder,
udt::UdtTransferBuilder,
},
unlock::{
opentx::OpentxWitness, MultisigConfig, OmniLockConfig, OmniUnlockMode, ScriptUnlocker,
Expand All @@ -24,35 +27,40 @@ use crate::{
use ckb_crypto::secp::{Pubkey, SECP256K1};
use ckb_types::{
bytes::Bytes,
packed::{CellOutput, WitnessArgs},
core::{Capacity, ScriptHashType},
packed::{CellInput, CellOutput, Script, WitnessArgs},
prelude::*,
H160, H256,
};
use rand::Rng;

use crate::tx_builder::{unlock_tx, CapacityBalancer, TxBuilder};
const ZERO_FEE_RATE: u64 = 0;

#[test]
fn test_omnilock_transfer_from_sighash() {
let sender_key = secp256k1::SecretKey::from_slice(ACCOUNT0_KEY.as_bytes())
fn build_simple_config(key: H256) -> OmniLockConfig {
let priv_key = secp256k1::SecretKey::from_slice(key.as_bytes())
.map_err(|err| format!("invalid sender secret key: {}", err))
.unwrap();
let pubkey = secp256k1::PublicKey::from_secret_key(&SECP256K1, &sender_key);
let cfg = OmniLockConfig::new_pubkey_hash(blake160(&pubkey.serialize()));
test_omnilock_simple_hash(cfg);
let pubkey = secp256k1::PublicKey::from_secret_key(&SECP256K1, &priv_key);
OmniLockConfig::new_pubkey_hash(blake160(&pubkey.serialize()))
}
#[test]
fn test_opentx_pay_from_sighash() {
let cfg = build_simple_config(ACCOUNT0_KEY);
test_opentx_pay_simple_hash(cfg);
}

#[test]
fn test_omnilock_transfer_from_ethereum() {
fn test_opentx_pay_from_ethereum() {
let account0_key = secp256k1::SecretKey::from_slice(ACCOUNT0_KEY.as_bytes()).unwrap();
let pubkey = secp256k1::PublicKey::from_secret_key(&SECP256K1, &account0_key);
let cfg = OmniLockConfig::new_ethereum(keccak160(Pubkey::from(pubkey).as_ref()));
test_omnilock_simple_hash(cfg);
test_opentx_pay_simple_hash(cfg);
}

/// account0(200) => account0(exchange 199) + open pay 1,
/// account2(100) => account2(101 - transaction fee)
fn test_omnilock_simple_hash(mut cfg: OmniLockConfig) {
fn test_opentx_pay_simple_hash(mut cfg: OmniLockConfig) {
cfg.set_opentx_mode();
let unlock_mode = OmniUnlockMode::Normal;
let sender = build_omnilock_script(&cfg);
Expand Down Expand Up @@ -135,7 +143,7 @@ fn test_omnilock_simple_hash(mut cfg: OmniLockConfig) {
// .lock(receiver.clone())
// .capacity((100 * ONE_CKB).pack())
// .build();
let builder = CapacityTransferBuilderWithTransaction::new(
let builder = CapacityTransferBuilder::new_with_transaction(
vec![/*(output.clone(), Bytes::default())*/],
tx,
);
Expand Down Expand Up @@ -165,7 +173,7 @@ fn test_omnilock_simple_hash(mut cfg: OmniLockConfig) {
/// multisig(200) => multisig(exchange 199) + open pay 1, locked by account0, account1, account2
/// account3(400) => account2(401 - transaction fee)
#[test]
fn test_omnilock_transfer_from_multisig() {
fn test_opentx_pay_from_multisig() {
let unlock_mode = OmniUnlockMode::Normal;
let lock_args = vec![
ACCOUNT0_ARG.clone(),
Expand Down Expand Up @@ -263,7 +271,7 @@ fn test_omnilock_transfer_from_multisig() {
// .lock(receiver.clone())
// .capacity((100 * ONE_CKB).pack())
// .build();
let builder = CapacityTransferBuilderWithTransaction::new(
let builder = CapacityTransferBuilder::new_with_transaction(
vec![/*(output.clone(), Bytes::default())*/],
tx,
);
Expand Down Expand Up @@ -296,36 +304,28 @@ fn test_omnilock_transfer_from_multisig() {
}

#[test]
fn test_omnilock_transfer_from_sighash_absolute_from_start() {
test_omnilock_transfer_from_sighash_absolute(true);
fn test_opentx_pay_receive_sighash_absolute_from_start() {
test_opentx_pay_receive_sighash_absolute(true);
}
#[test]
fn test_omnilock_transfer_from_sighash_absolute_self() {
test_omnilock_transfer_from_sighash_absolute(false);
fn test_opentx_pay_receive_sighash_absolute_self() {
test_opentx_pay_receive_sighash_absolute(false);
}
fn test_omnilock_transfer_from_sighash_absolute(from_start: bool) {
let cfgs: Vec<OmniLockConfig> = [ACCOUNT0_KEY, ACCOUNT2_KEY]
.iter()
.map(|key| {
let priv_key = secp256k1::SecretKey::from_slice(key.as_bytes())
.map_err(|err| format!("invalid sender secret key: {}", err))
.unwrap();
let pubkey = secp256k1::PublicKey::from_secret_key(&SECP256K1, &priv_key);
OmniLockConfig::new_pubkey_hash(blake160(&pubkey.serialize()))
})
.collect();
test_omnilock_simple_hash_absolute(cfgs[0].clone(), cfgs[1].clone(), from_start);
fn test_opentx_pay_receive_sighash_absolute(from_start: bool) {
let sender_cfg = build_simple_config(ACCOUNT0_KEY);
let receiver_cfg = build_simple_config(ACCOUNT2_KEY);
test_opentx_pay_receive_simple_hash_absolute(sender_cfg, receiver_cfg, from_start);
}

#[test]
fn test_omnilock_transfer_from_ethereum_absolute_from_start() {
test_omnilock_transfer_from_ethereum_absolute(true);
fn test_opentx_pay_receive_ethereum_absolute_from_start() {
test_opentx_pay_receive_ethereum_absolute(true);
}
#[test]
fn test_omnilock_transfer_from_ethereum_absolute_from_self() {
test_omnilock_transfer_from_ethereum_absolute(false);
fn test_opentx_pay_receive_ethereum_absolute_from_self() {
test_opentx_pay_receive_ethereum_absolute(false);
}
fn test_omnilock_transfer_from_ethereum_absolute(from_start: bool) {
fn test_opentx_pay_receive_ethereum_absolute(from_start: bool) {
let cfgs: Vec<OmniLockConfig> = [ACCOUNT0_KEY, ACCOUNT2_KEY]
.iter()
.map(|key| {
Expand All @@ -334,12 +334,12 @@ fn test_omnilock_transfer_from_ethereum_absolute(from_start: bool) {
OmniLockConfig::new_ethereum(keccak160(Pubkey::from(pubkey).as_ref()))
})
.collect();
test_omnilock_simple_hash_absolute(cfgs[0].clone(), cfgs[1].clone(), from_start);
test_opentx_pay_receive_simple_hash_absolute(cfgs[0].clone(), cfgs[1].clone(), from_start);
}

/// account0(200) => account0(exchange 199) + open pay 1,
/// account2(100) => account2(101 - transaction fee)
fn test_omnilock_simple_hash_absolute(
fn test_opentx_pay_receive_simple_hash_absolute(
mut sender_cfg: OmniLockConfig,
mut receiver_cfg: OmniLockConfig,
from_start: bool,
Expand Down Expand Up @@ -410,7 +410,7 @@ fn test_omnilock_simple_hash_absolute(
// If adjust the transaction fee later, the exchange may mot be enough to maintain the minimal capacity.
let balancer = CapacityBalancer::new_simple(receiver.clone(), placeholder_witness, FEE_RATE);

let builder = CapacityTransferBuilderWithTransaction::new(
let builder = CapacityTransferBuilder::new_with_transaction(
vec![/*(output.clone(), Bytes::default())*/],
tx,
);
Expand Down Expand Up @@ -464,18 +464,18 @@ fn test_omnilock_simple_hash_absolute(
ctx.verify(tx, FEE_RATE).unwrap();
}
#[test]
fn test_omnilock_transfer_from_multisig_absolute_from_start() {
test_omnilock_transfer_from_multisig_absolute(true);
fn test_opentx_pay_receive_multisig_absolute_from_start() {
test_opentx_pay_receive_multisig_absolute(true);
}

#[test]
fn test_omnilock_transfer_from_multisig_absolute_from_self() {
test_omnilock_transfer_from_multisig_absolute(false);
fn test_opentx_pay_receive_multisig_absolute_from_self() {
test_opentx_pay_receive_multisig_absolute(false);
}

/// multisig(200) => multisig(exchange 199) + open pay 1, locked by account0, account1, account2
/// account3(400) => account2(401 - transaction fee)
fn test_omnilock_transfer_from_multisig_absolute(from_start: bool) {
fn test_opentx_pay_receive_multisig_absolute(from_start: bool) {
let unlock_mode = OmniUnlockMode::Normal;
let lock_args = vec![
ACCOUNT0_ARG.clone(),
Expand Down Expand Up @@ -564,7 +564,7 @@ fn test_omnilock_transfer_from_multisig_absolute(from_start: bool) {
let placeholder_witness = receiver_cfg.placeholder_witness(unlock_mode).unwrap();
let balancer = CapacityBalancer::new_simple(receiver.clone(), placeholder_witness, FEE_RATE);

let builder = CapacityTransferBuilderWithTransaction::new(vec![], tx);
let builder = CapacityTransferBuilder::new_with_transaction(vec![], tx);
let mut tx = builder
.build_balanced(&mut cell_collector, &ctx, &ctx, &ctx, &balancer, &unlockers)
.unwrap();
Expand Down Expand Up @@ -613,3 +613,139 @@ fn test_omnilock_transfer_from_multisig_absolute(from_start: bool) {
assert_eq!(tx.witnesses().len(), 2);
ctx.verify(tx, FEE_RATE).unwrap();
}

#[test]
fn test_opentx_udt_open_buy() {
// ACCOUNT1(alice) will spend 50.01 CKB with fee to buy 1,000,000 SUDT
// ACCOUNT2(bob) collect the 50 CKB with the transfer 1,000,000 SUDT
let unlock_mode = OmniUnlockMode::Normal;
let mut alice_cfg = build_simple_config(ACCOUNT1_KEY);
alice_cfg.set_opentx_mode();
let alice = build_omnilock_script(&alice_cfg);
let bob = build_sighash_script(ACCOUNT2_ARG);

let mut ctx = init_context(
vec![(OMNILOCK_BIN, true), (SUDT_BIN, false)],
vec![
(alice.clone(), Some(300 * ONE_CKB)),
(bob.clone(), Some(400 * ONE_CKB)),
],
);
let sudt_data_hash = H256::from(blake2b_256(SUDT_BIN));
let owner = build_sighash_script(H160::default());
let type_script = Script::new_builder()
.code_hash(sudt_data_hash.pack())
.hash_type(ScriptHashType::Data1.into())
.args(owner.calc_script_hash().as_bytes().pack())
.build();
let sudt_input = CellInput::new(random_out_point(), 0);
let sudt_output = CellOutput::new_builder()
.capacity(ONE_CKB.pack())
.lock(bob.clone())
.type_(Some(type_script.clone()).pack())
.build();
let sudt_capacity = sudt_output
.occupied_capacity(Capacity::bytes(16).unwrap())
.unwrap()
.as_u64();
println!("sudt_capacity: {}", sudt_capacity);
let sudt_output = sudt_output
.as_builder()
.capacity(sudt_capacity.pack())
.build();
let sudt_data = Bytes::from(1_000_000u128.to_le_bytes().to_vec());
ctx.add_live_cell(sudt_input, sudt_output, sudt_data.clone(), None);

let fee = 100_0000u64;
// build opentx alice's input
let builder = OmniLockTransferBuilder::new_open(
(50 * ONE_CKB + sudt_capacity + fee).into(),
vec![],
alice_cfg.clone(),
None,
);
let placeholder_witness = alice_cfg.placeholder_witness(unlock_mode).unwrap();
let balancer = CapacityBalancer::new_simple(alice.clone(), placeholder_witness, ZERO_FEE_RATE);

let mut cell_collector = ctx.to_live_cells_context();
let alice_key = secp256k1::SecretKey::from_slice(ACCOUNT1_KEY.as_bytes()).unwrap();
let unlockers = build_omnilock_unlockers(alice_key, alice_cfg.clone(), unlock_mode);
let mut tx = builder
.build_balanced(&mut cell_collector, &ctx, &ctx, &ctx, &balancer, &unlockers)
.unwrap();
// add sudt output
let sudt_output = CellOutput::new_builder()
.capacity((sudt_capacity).pack())
.lock(alice.clone())
.type_(Some(type_script.clone()).pack())
.build();
tx = tx
.as_advanced_builder()
.output(sudt_output.clone())
.output_data(sudt_data.pack())
.build();
// update opentx input list
let mut rng = rand::thread_rng();
let salt: u32 = rng.gen();
let wit = OpentxWitness::new_sig_all_relative(&tx, Some(salt)).unwrap();
alice_cfg.set_opentx_input(wit);
tx = OmniLockTransferBuilder::update_opentx_witness(
tx,
&alice_cfg,
OmniUnlockMode::Normal,
&ctx,
&alice,
)
.unwrap();
// config updated, so unlockers must rebuilt.
let unlockers = build_omnilock_unlockers(alice_key, alice_cfg.clone(), unlock_mode);
let (new_tx, new_locked_groups) = unlock_tx(tx.clone(), &ctx, &unlockers).unwrap();
assert!(new_locked_groups.is_empty());
tx = new_tx;
println!(
"> tx: {}",
serde_json::to_string_pretty(&json_types::TransactionView::from(tx.clone())).unwrap()
);
// use opentx
let builder = UdtTransferBuilder::new_with_transaction(type_script, bob.clone(), vec![], tx);
let placeholder_witness = WitnessArgs::new_builder()
.lock(Some(Bytes::from(vec![0u8; 65])).pack())
.build();
let balancer = CapacityBalancer::new_simple(bob, placeholder_witness, FEE_RATE);

let bob_key = secp256k1::SecretKey::from_slice(ACCOUNT2_KEY.as_bytes()).unwrap();
let signer = SecpCkbRawKeySigner::new_with_secret_keys(vec![bob_key]);
let script_unlocker = SecpSighashUnlocker::from(Box::new(signer) as Box<_>);
let mut unlockers: HashMap<ScriptId, Box<dyn ScriptUnlocker>> = HashMap::default();
unlockers.insert(
ScriptId::new_type(SIGHASH_TYPE_HASH.clone()),
Box::new(script_unlocker),
);

let mut cell_collector = ctx.to_live_cells_context();
let (tx, locked_groups) = builder
.build_unlocked(&mut cell_collector, &ctx, &ctx, &ctx, &balancer, &unlockers)
.unwrap();
println!(
"> tx: {}",
serde_json::to_string_pretty(&json_types::TransactionView::from(tx.clone())).unwrap()
);
assert_eq!(locked_groups.len(), 1);
assert_eq!(tx.header_deps().len(), 0);
assert_eq!(tx.cell_deps().len(), 3);
assert_eq!(tx.inputs().len(), 3);
let outputs = tx.outputs().into_iter().collect::<Vec<_>>();
assert_eq!(outputs.len(), 4);
assert_eq!(outputs[1], sudt_output);
let expected_outputs_data = vec![
Bytes::from(1_000_000u128.to_le_bytes().to_vec()),
Bytes::from(0u128.to_le_bytes().to_vec()),
];
let outputs_data = tx
.outputs_data()
.into_iter()
.map(|d| d.raw_data())
.collect::<Vec<_>>();
assert_eq!(outputs_data[1..3], expected_outputs_data);
ctx.verify(tx, FEE_RATE).unwrap();
}
4 changes: 2 additions & 2 deletions src/tx_builder/omni_lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ impl OmniLockTransferBuilder {
/// Create an OmniLockTransferBuilder with open out in the output list.
/// After the transaction built, the open out should be removed.
pub fn new_open(
open_capacity: HumanCapacity,
open_out_capacity: HumanCapacity,
mut outputs: Vec<(CellOutput, Bytes)>,
cfg: OmniLockConfig,
rce_cells: Option<Vec<OutPoint>>,
) -> OmniLockTransferBuilder {
let tmp_out = OmniLockTransferBuilder::build_tmp_open_out(open_capacity);
let tmp_out = OmniLockTransferBuilder::build_tmp_open_out(open_out_capacity);
outputs.push((tmp_out, Bytes::default()));
OmniLockTransferBuilder {
outputs,
Expand Down
Loading

0 comments on commit ed930b5

Please sign in to comment.