Skip to content

Pending spaces list for unconfirmed #100

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions client/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ use colored::{Color, Colorize};
use jsonrpsee::core::Serialize;
use serde::Deserialize;
use spaces_protocol::{
bitcoin::{Amount, Network, OutPoint},
Covenant,
bitcoin::{Amount, Network, OutPoint}, Covenant
};
use spaces_wallet::{
address::SpaceAddress,
Expand Down Expand Up @@ -37,6 +36,12 @@ struct FormatRpcError {
message: String,
}

#[derive(Tabled)]
#[tabled(rename_all = "UPPERCASE")]
struct PendingSpaces {
space: String,
}

#[derive(Tabled)]
#[tabled(rename_all = "UPPERCASE")]
struct WinningSpaces {
Expand Down Expand Up @@ -272,9 +277,13 @@ pub fn print_list_spaces_response(
) {
match format {
Format::Text => {
let mut pendings = Vec::new();
let mut outbids = Vec::new();
let mut winnings = Vec::new();
let mut owned = Vec::new();
for slabel in response.pending {
pendings.push(PendingSpaces { space: slabel.to_string() });
}
for res in response.outbid {
let space = res.spaceout.space.as_ref().expect("space");
let mut outbid = OutbidSpaces {
Expand Down Expand Up @@ -342,6 +351,12 @@ pub fn print_list_spaces_response(
owned.push(registered);
}

if !pendings.is_empty() {
println!("⏳ PENDING ({} spaces): ", pendings.len().to_string().bold());
let table = ascii_table(pendings);
println!("{}", table);
}

if !outbids.is_empty() {
println!("⚠️ OUTBID ({} spaces): ", outbids.len().to_string().bold());
let table = ascii_table(outbids);
Expand Down
42 changes: 28 additions & 14 deletions client/src/wallets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ pub struct WalletInfoWithProgress {

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ListSpacesResponse {
pub pending: Vec<SLabel>,
pub winning: Vec<FullSpaceOut>,
pub outbid: Vec<FullSpaceOut>,
pub owned: Vec<FullSpaceOut>,
Expand Down Expand Up @@ -816,12 +817,15 @@ impl RpcWallet {
let unspent = wallet.list_unspent_with_details(state)?;
let recent_events = wallet.list_recent_events()?;

let mut res = ListSpacesResponse {
winning: vec![],
outbid: vec![],
owned: vec![],
};
let mut pending = vec![];
let mut outbid = vec![];
for (txid, event) in recent_events {
let tx = wallet.get_tx(txid);
if tx.as_ref().is_some_and(|tx| !tx.chain_position.is_confirmed()) {
pending.push(SLabel::from_str(event.space.as_ref().unwrap()).expect("valid space name"));
continue;
}

if unspent.iter().any(|out| {
out.space
.as_ref()
Expand All @@ -833,9 +837,12 @@ impl RpcWallet {
let spacehash = SpaceKey::from(Sha256::hash(name.as_ref()));
let space = state.get_space_info(&spacehash)?;
if let Some(space) = space {
let tx = wallet.get_tx(txid);
if space.spaceout.space.as_ref().unwrap().is_owned() {
continue;
}

if tx.is_none() {
res.outbid.push(space);
outbid.push(space);
continue;
}

Expand All @@ -845,10 +852,13 @@ impl RpcWallet {
{
continue;
}
res.outbid.push(space);

outbid.push(space);
}
}

let mut owned = vec![];
let mut winning = vec![];
for wallet_output in unspent.into_iter().filter(|output| output.space.is_some()) {
let entry = FullSpaceOut {
txid: wallet_output.output.outpoint.txid,
Expand All @@ -860,15 +870,19 @@ impl RpcWallet {
},
};

let space = entry.spaceout.space.as_ref().expect("space");
if matches!(space.covenant, spaces_protocol::Covenant::Bid { .. }) {
res.winning.push(entry);
} else if matches!(space.covenant, spaces_protocol::Covenant::Transfer { .. }) {
res.owned.push(entry);
if entry.spaceout.space.as_ref().expect("space").is_owned() {
owned.push(entry);
} else {
winning.push(entry);
}
}

Ok(res)
Ok(ListSpacesResponse {
pending,
winning,
outbid,
owned,
})
}

fn list_transactions(
Expand Down
10 changes: 9 additions & 1 deletion client/tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,13 @@ async fn it_should_open_a_space_for_auction(rig: &TestRig) -> anyhow::Result<()>
assert!(tx_res.error.is_none(), "expect no errors for simple open");
}
assert_eq!(response.result.len(), 2, "must be 2 transactions");
let alices_spaces = rig.spaced.client.wallet_list_spaces(ALICE).await?;
assert!(alices_spaces.pending.first().is_some_and(|s| s.to_string() == TEST_SPACE), "must be a pending space");

rig.mine_blocks(1, None).await?;
rig.wait_until_synced().await?;
let alices_spaces = rig.spaced.client.wallet_list_spaces(ALICE).await?;
assert!(alices_spaces.pending.is_empty(), "must have no pending spaces");

let fullspaceout = rig.spaced.client.get_space(TEST_SPACE).await?;
let fullspaceout = fullspaceout.expect("a fullspace out");
Expand Down Expand Up @@ -96,8 +100,11 @@ async fn it_should_allow_outbidding(rig: &TestRig) -> anyhow::Result<()> {
.expect("send request");

println!("{}", serde_json::to_string_pretty(&result).unwrap());
rig.mine_blocks(1, None).await?;

let bob_spaces_updated = rig.spaced.client.wallet_list_spaces(BOB).await?;
assert!(bob_spaces_updated.pending.first().is_some_and(|s| s.to_string() == TEST_SPACE), "must be a pending space");

rig.mine_blocks(1, None).await?;
rig.wait_until_synced().await?;
rig.wait_until_wallet_synced(BOB).await?;
rig.wait_until_wallet_synced(ALICE).await?;
Expand Down Expand Up @@ -125,6 +132,7 @@ async fn it_should_allow_outbidding(rig: &TestRig) -> anyhow::Result<()> {
alices_balance.balance + Amount::from_sat(TEST_INITIAL_BID + 662),
"alice must be refunded this exact amount"
);
assert!(bob_spaces_updated.pending.is_empty(), "must have no pending spaces");

let fullspaceout = rig.spaced.client.get_space(TEST_SPACE).await?;
let fullspaceout = fullspaceout.expect("a fullspace out");
Expand Down
5 changes: 2 additions & 3 deletions wallet/src/tx_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ impl TxEvent {
Ok(None)
}

/// Retrieve all spaces the wallet has bid on in the last 2 weeks
/// Retrieve all spaces the wallet has done any operation with
pub fn get_latest_events(
db_tx: &rusqlite::Transaction,
) -> rusqlite::Result<Vec<(Txid, TxEvent)>> {
Expand All @@ -216,8 +216,7 @@ impl TxEvent {
WHERE id IN (
SELECT MAX(id)
FROM {table}
WHERE type IN ('bid', 'open')
AND created_at >= strftime('%s', 'now', '-14 days')
WHERE space IS NOT NULL
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain why this change was necessary? this method was supposed to return recent events from last 14 days - the number of events could grow significantly for active wallets and its used in other parts of the code base.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is used only for list_spaces, and it returns only one event per space anyway (since GROUP BY).

  1. space IS NOT NULL is to capture all the activity for the pending list.
  2. No time limit is to make the user be able to see all the spaces he was interested in. This one I can revert is we don't want to show those in outbid list.

Also, if this table can grow big it can make sense to add indexes.

GROUP BY space
)
ORDER BY id DESC",
Expand Down
Loading