Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow signer plugin to open its own rpc services #8

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions integration-test/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions integration-test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,28 @@
- create tables and indexes

```bash
cd integration
cd integration-test
psql -h localhost -U postgres -d mercury-otx-dev -f devtools/create_table.sql
```

### Init CKB

```bash
cd integration
cd integration-test
rm -rf ./dev_chain/dev/data ./free-space
```

### Run integration tests

```bash
cd integration
cd integration-test
cargo run
```

or

```bash
cd integration
cd integration-test
cargo run -- -t test_service_rpc
```

Expand Down Expand Up @@ -151,7 +151,7 @@ If you need to deploy contract scripts on the dev chain, you need to do the foll
- run integration tests

```bash
cd integration
cd integration-test
cargo run
```

6 changes: 4 additions & 2 deletions integration-test/src/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,16 @@ pub(crate) fn start_mercury(ckb: Child) -> (Child, Child) {
panic!("Setup test environment failed");
}

pub(crate) fn start_otx_pool(address: Address, pk: H256) {
pub(crate) fn start_otx_pool(address: Address, pk: Option<H256>) {
let mut lock = CURRENT_OTX_POOL_SERVICE_PROCESS.lock().unwrap();
if let Some(child) = lock.as_mut() {
child.kill().unwrap();
}

env::set_var("PRIVKEY", pk.to_string());
env::set_var("DEFAUT_ADDRESS", address.to_string());
if let Some(pk) = pk {
env::set_var("PRIVKEY", pk.to_string());
}

let service = run_command_spawn(
"cargo",
Expand Down
1 change: 1 addition & 0 deletions integration-test/src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod helper;
mod payment;
mod rpc;
mod signer;
mod swap;

#[derive(Debug)]
Expand Down
4 changes: 2 additions & 2 deletions integration-test/src/tests/payment/dust_collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fn test_payment_dust_collect_ckb() {
let (dust_collector_address, pk) = generate_rand_secp_address_pk_pair();
prepare_ckb_capacity(&dust_collector_address, 200_0000_0000u64).unwrap();
prepare_udt_1(200u128, &dust_collector_address).unwrap();
start_otx_pool(dust_collector_address.clone(), pk);
start_otx_pool(dust_collector_address.clone(), Some(pk));

// check dust collector assets
let payload = GetBalancePayload {
Expand Down Expand Up @@ -107,7 +107,7 @@ fn test_payment_dust_collect_ckb() {
assert_eq!(200u128, response.balances[1].free.into());
}

fn build_pay_ckb_otx(
pub(crate) fn build_pay_ckb_otx(
payer: &str,
prepare_capacity: usize,
remain_capacity: usize,
Expand Down
2 changes: 1 addition & 1 deletion integration-test/src/tests/payment/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
mod dust_collector;
pub mod dust_collector;
8 changes: 5 additions & 3 deletions integration-test/src/tests/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ inventory::submit!(IntegrationTest {
});
fn test_service_rpc() {
let (address, pk) = generate_rand_secp_address_pk_pair();
start_otx_pool(address, pk);
start_otx_pool(address, Some(pk));

let service_client = OtxPoolRpcClient::new(OTX_POOL_URI.to_string());
let ret = service_client.submit_otx(JsonBytes::default());
Expand All @@ -32,7 +32,7 @@ inventory::submit!(IntegrationTest {
});
fn test_service_rpc_submit_otx() {
let (address, pk) = generate_rand_secp_address_pk_pair();
start_otx_pool(address, pk);
start_otx_pool(address, Some(pk));

let tx_info = build_pay_ckb_signed_otx("alice", 151, 100, 51).unwrap();
let tx_view = tx_info.tx;
Expand All @@ -47,6 +47,8 @@ fn test_service_rpc_submit_otx() {
let status = service_client.query_otx_status_by_id(id).unwrap().unwrap();
assert_eq!(status, OpenTxStatus::Pending);

let ret = service_client.query_otx_status_by_id(H256::default()).unwrap();
let ret = service_client
.query_otx_status_by_id(H256::default())
.unwrap();
assert!(ret.is_none());
}
131 changes: 131 additions & 0 deletions integration-test/src/tests/signer/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
use crate::const_definition::{CKB_URI, MERCURY_URI, OTX_POOL_URI};
use crate::help::start_otx_pool;
use crate::tests::payment::dust_collector::build_pay_ckb_otx;
use crate::utils::client::mercury_client::MercuryRpcClient;
use crate::utils::instruction::ckb::aggregate_transactions_into_blocks;
use crate::utils::instruction::mercury::{prepare_ckb_capacity, prepare_udt_1};
use crate::utils::lock::secp::generate_rand_secp_address_pk_pair;
use crate::IntegrationTest;

use otx_format::jsonrpc_types::tx_view::tx_view_to_basic_otx;
use otx_format::types::OpenTxStatus;
use utils::aggregator::SignInfo;
use utils::client::otx_pool_client::OtxPoolRpcClient;

use ckb_jsonrpc_types::JsonBytes;
use ckb_types::prelude::Entity;
use core_rpc_types::{GetBalancePayload, JsonItem};
use utils::config::CkbConfig;

use std::collections::HashSet;
use std::thread::sleep;
use std::time::Duration;

inventory::submit!(IntegrationTest {
name: "test_plugin_rpc_get_plugin_info",
test_fn: test_plugin_rpc_get_plugin_info
});
fn test_plugin_rpc_get_plugin_info() {
let (address, pk) = generate_rand_secp_address_pk_pair();
start_otx_pool(address, Some(pk));

let service_client = OtxPoolRpcClient::new(OTX_POOL_URI.to_string());
let plugin_info = service_client.get_signer_info().unwrap();
assert_eq!(plugin_info.name, "singer");
}

inventory::submit!(IntegrationTest {
name: "test_plugin_rpc_get_pending_sign_otxs",
test_fn: test_plugin_rpc_get_pending_sign_otxs
});
fn test_plugin_rpc_get_pending_sign_otxs() {
let (address, pk) = generate_rand_secp_address_pk_pair();
start_otx_pool(address.clone(), Some(pk));

let service_client = OtxPoolRpcClient::new(OTX_POOL_URI.to_string());
let otxs = service_client
.get_pending_sign_otxs(address.to_string())
.unwrap();
assert_eq!(otxs.len(), 0);
}

inventory::submit!(IntegrationTest {
name: "test_plugin_rpc_get_pending_sign_otxs_with_one_otx",
test_fn: test_plugin_rpc_get_pending_sign_otxs_with_one_otx
});
fn test_plugin_rpc_get_pending_sign_otxs_with_one_otx() {
// run otx pool
let (dust_collector_address, pk) = generate_rand_secp_address_pk_pair();
prepare_ckb_capacity(&dust_collector_address, 200_0000_0000u64).unwrap();
prepare_udt_1(200u128, &dust_collector_address).unwrap();
start_otx_pool(dust_collector_address.clone(), None);

// check dust collector assets
let payload = GetBalancePayload {
item: JsonItem::Address(dust_collector_address.to_string()),
asset_infos: HashSet::new(),
extra: None,
tip_block_number: None,
};
let mercury_client = MercuryRpcClient::new(MERCURY_URI.to_string());
let response = mercury_client.get_balance(payload).unwrap();
assert_eq!(response.balances.len(), 2);
assert_eq!(200_0000_0000u128, response.balances[0].free.into());
assert_eq!(142_0000_0000u128, response.balances[0].occupied.into());
assert_eq!(200u128, response.balances[1].free.into());

// build otxs
let alice_otx = build_pay_ckb_otx("alice", 151, 100, 51).unwrap();
let bob_otx = build_pay_ckb_otx("bob", 202, 200, 2).unwrap();

// submit otxs
let service_client = OtxPoolRpcClient::new(OTX_POOL_URI.to_string());
let _alice_otx_id = service_client
.submit_otx(JsonBytes::from_bytes(alice_otx.as_bytes()))
.unwrap();
let _bob_otx_id = service_client
.submit_otx(JsonBytes::from_bytes(bob_otx.as_bytes()))
.unwrap();

// query otx after a few secs
sleep(Duration::from_secs(12));
aggregate_transactions_into_blocks().unwrap();

let otxs = service_client
.get_pending_sign_otxs(dust_collector_address.to_string())
.unwrap();
assert_eq!(otxs.len(), 1);

// sign
let ckb_tx = if let Ok(tx) = otxs[0].clone().try_into() {
tx
} else {
log::error!("open tx converts to Ckb tx failed.");
return;
};
let sign_info = SignInfo::new(
&dust_collector_address,
&pk,
CkbConfig::new("ckb_dev", CKB_URI),
);
let tx_view = sign_info.sign_ckb_tx(ckb_tx).unwrap();
let otx = tx_view_to_basic_otx(tx_view).unwrap();

// send signed tx to otx pool
let ret = service_client.send_signed_otx(otx.clone());
println!("ret: {:?}", ret);
assert!(ret.is_ok());

// query otx after a few secs
sleep(Duration::from_secs(12));
let otxs = service_client
.get_pending_sign_otxs(dust_collector_address.to_string())
.unwrap();
assert_eq!(otxs.len(), 0);

let status = service_client
.query_otx_status_by_id(otx.get_tx_hash().unwrap())
.unwrap()
.unwrap();
assert!(matches!(status, OpenTxStatus::Committed(_)));
}
2 changes: 1 addition & 1 deletion integration-test/src/tests/swap/atomic_swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ fn test_swap_udt_to_udt() {
// run otx pool
let (address, pk) = generate_rand_secp_address_pk_pair();
prepare_ckb_capacity(&address, 200_0000_0000u64).unwrap();
start_otx_pool(address, pk);
start_otx_pool(address, Some(pk));

// alice build otxs
// pay 10 UDT-1, get 10 UDT-2, pay fee 1 CKB
Expand Down
3 changes: 3 additions & 0 deletions otx-format/src/constant/extra_keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,6 @@ pub const OTX_LOCATING_INPUT_TYPE_HASH_TYPE: u32 = 0x10055;
pub const OTX_LOCATING_INPUT_TYPE_ARGS: u32 = 0x10056;
pub const OTX_LOCATING_INPUT_TYPE_SCRIPT_HASH: u32 = 0x10057;
pub const OTX_LOCATING_INPUT_DATA_HASH: u32 = 0x10058;

/// Signing (0x10060)
pub const OTX_SIGNING_WITNESS_SIGHASH_ALL_SCRIPT: u32 = 0x10060;
4 changes: 4 additions & 0 deletions otx-format/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ pub enum OtxFormatError {

#[display(fmt = "locate input cell failed: {}", _0)]
LocateInputFailed(String),

#[display(fmt = "witness index error: {}", _0)]
WitnessIndexError(usize),
}

impl OtxError for OtxFormatError {
Expand All @@ -35,6 +38,7 @@ impl OtxError for OtxFormatError {
OtxFormatError::OtxMapParseMissingField(_) => -13012,
OtxFormatError::OtxMapParseFailed(_) => -13013,
OtxFormatError::LocateInputFailed(_) => -13014,
OtxFormatError::WitnessIndexError(_) => -13015,
}
}

Expand Down
59 changes: 57 additions & 2 deletions otx-format/src/jsonrpc_types/opentx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::constant::extra_keys::{
OTX_ACCOUNTING_META_INPUT_CKB, OTX_ACCOUNTING_META_INPUT_SUDT, OTX_ACCOUNTING_META_INPUT_XUDT,
OTX_ACCOUNTING_META_OUTPUT_CKB, OTX_ACCOUNTING_META_OUTPUT_SUDT,
OTX_ACCOUNTING_META_OUTPUT_XUDT, OTX_IDENTIFYING_META_AGGREGATE_COUNT,
OTX_IDENTIFYING_META_TX_HASH,
OTX_IDENTIFYING_META_TX_HASH, OTX_SIGNING_WITNESS_SIGHASH_ALL_SCRIPT,
};
use crate::error::OtxFormatError;
use crate::types::packed::{self, OpenTransactionBuilder, OtxMapBuilder, OtxMapVecBuilder};
Expand All @@ -23,7 +23,9 @@ use ckb_jsonrpc_types::{
use ckb_types::bytes::Bytes;
use ckb_types::constants::TX_VERSION;
use ckb_types::core::{self, ScriptHashType, TransactionBuilder};
use ckb_types::packed::{Byte32, OutPointBuilder, Uint128, Uint64, WitnessArgs};
use ckb_types::packed::{
Byte32, OutPointBuilder, Script as PackedScript, Uint128, Uint64, WitnessArgs,
};
use ckb_types::{self, prelude::*, H256};
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -76,6 +78,20 @@ impl From<packed::OtxKeyPair> for OtxKeyPair {
#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq, Hash, Debug)]
pub struct OtxMapVec(Vec<OtxMap>);

impl OtxMapVec {
pub fn push(&mut self, map: OtxMap) {
self.0.push(map)
}

pub fn len(&self) -> usize {
self.0.len()
}

pub fn is_empty(&self) -> bool {
self.len() == 0
}
}

impl IntoIterator for OtxMapVec {
type Item = OtxMap;
type IntoIter = std::vec::IntoIter<Self::Item>;
Expand Down Expand Up @@ -287,6 +303,38 @@ impl OpenTransaction {
s_udt_amount,
})
}

pub fn add_pending_signature_script(
&mut self,
index: usize,
script: PackedScript,
) -> Result<(), OtxFormatError> {
let witness = OtxKeyPair::new(
OTX_SIGNING_WITNESS_SIGHASH_ALL_SCRIPT.into(),
None,
JsonBytes::from_bytes(script.as_bytes()),
);
if self.witnesses.len() != index {
return Err(OtxFormatError::WitnessIndexError(index));
}
let witness: OtxMap = vec![witness].into();
self.witnesses.push(witness);
Ok(())
}

pub fn get_pending_signature_locks(&self) -> Vec<(usize, PackedScript)> {
self.witnesses
.0
.iter()
.enumerate()
.filter_map(|(index, witness)| {
witness
.get_value(OTX_SIGNING_WITNESS_SIGHASH_ALL_SCRIPT.into(), None)
.and_then(|value: JsonBytes| PackedScript::from_slice(value.as_bytes()).ok())
.map(|script| (index, script))
})
.collect()
}
}

fn get_value_by_first_element(
Expand Down Expand Up @@ -424,6 +472,13 @@ impl OtxMap {
pub fn push(&mut self, key_pair: OtxKeyPair) {
self.0.push(key_pair)
}

pub fn get_value(&self, key_type: Uint32, key_data: Option<JsonBytes>) -> Option<JsonBytes> {
self.0
.iter()
.find(|k| k.key_type == key_type && k.key_data == key_data)
.map(|k| k.value_data.clone())
}
}

impl From<Vec<OtxKeyPair>> for OtxMap {
Expand Down
Loading