Skip to content

Implement Block.get_strippedsize() and Transaction.get_vsize()#626

Merged
dr-orlovsky merged 6 commits intorust-bitcoin:masterfrom
visvirial:impl-vsize
Sep 10, 2021
Merged

Implement Block.get_strippedsize() and Transaction.get_vsize()#626
dr-orlovsky merged 6 commits intorust-bitcoin:masterfrom
visvirial:impl-vsize

Conversation

@visvirial
Copy link
Copy Markdown
Contributor

@visvirial visvirial commented Jun 28, 2021

I implemented the following methods:

  • block.get_strippedsize(): gets the "strippedsize" of a block.
  • tx.get_vsize(): gets the "virtual" size of a transaction.

The test values added for this code match the values comes from Bitcoin Core.

Edit: block.get_strippedsize() will call tx.get_scaled_size() twice for every transactions in a block, which cannot be prevented without exposing an extra function in Transaction which produces the non-segwit data bytes. This will impact the performance, while the exposed API kept simple.

Edit 2: If you prefer performance over smaller API change, let me know. I will modify my PR (will add tx.get_strippedsize()).

Copy link
Copy Markdown
Member

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

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

What's the specific use-case for this? I honestly can't think of a use for the stripped size of anything now that basically every part of bitcoin supports segwit.


/// Gets the "vsize" of this transaction. Will be `ceil(weight / 4.0)`.
#[inline]
pub fn get_vsize(&self) -> usize {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why do we need to expose this? Users can just divide-rounding-up themselves, and I'm not sure how much users should/do have a use for this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

tx.get_vsize() is readable and handy. Any reason for the user not to use such this instead of comming up with their own version?

#[inline]
pub fn get_vsize(&self) -> usize {
let weight = self.get_weight();
(weight / WITNESS_SCALE_FACTOR) + if weight % WITNESS_SCALE_FACTOR == 0 { 0 } else { 1 }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The usual (and more efficient) way to divide-rounding-up is to add one minus the divisor and then divide, instead of branching.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for a comment. I fixed this.


/// Get the strippedsize of the block
pub fn get_strippedsize(&self) -> usize {
let txs_size: usize = self.txdata.iter().map(|tx| {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes, I think we should expose a utility in transaction to get the stripped size, it would simplify this and may have value itself.

sgeisler
sgeisler previously approved these changes Jun 28, 2021
Copy link
Copy Markdown
Contributor

@sgeisler sgeisler left a comment

Choose a reason for hiding this comment

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

utACK 2085dc3

The tests seem good, I checked the stripped size and vsize that differ from the normal size against bitcoind.

@TheBlueMatt if it's in bitcoin core RPC replies then it's probably also ok to include it in rust-bitcoin imo.

@visvirial
Copy link
Copy Markdown
Contributor Author

What's the specific use-case for this? I honestly can't think of a use for the stripped size of anything now that basically every part of bitcoin supports segwit.

  1. Since Bitcoin Core emits strippedsize, rust-bitcoin also should be capable of computing this.
  2. For use case, we should ask Bitcoin Core devs for why they are exposing strippedsize. I'm implemented this for building a blockexplorer which is compatible with Bitcoin Core's JSON API.

@TheBlueMatt
Copy link
Copy Markdown
Member

Since Bitcoin Core emits strippedsize, rust-bitcoin also should be capable of computing this.

I don't think this is a good argument for doing anything. Bitcoin Core has lots of backwards compatibility concerns and ultimately are just developers, like us. Whether they decided to add anything or not isn't something above question.

I'm implemented this for building a blockexplorer which is compatible with Bitcoin Core's JSON API.

Now that's a use-case! Given this is quite simple it seems reasonable to add it for something like this.

}

/// Gets the size of this transaction excluding the witness data.
pub fn get_strippedsize(&self) -> usize {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you add a test with a few sample transactions where you call get_strippedsize on the original transaction, then remove the witness, then check that the new vsize/weight/size methods all return the same as the original strippedsize (mod multipliers).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't think it is needed because test_nonsegwit_transaction() covers this, but I added this in cdf7be4.

Copy link
Copy Markdown
Member

@apoelstra apoelstra left a comment

Choose a reason for hiding this comment

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

ack cdf7be4

I'd prefer the fixups appear in the commits that they fix, but these commits are all small enough that it's fine.

Copy link
Copy Markdown
Collaborator

@dr-orlovsky dr-orlovsky left a comment

Choose a reason for hiding this comment

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

utACK cdf7be4

80 + VarInt(self.txdata.len() as u64).len()
}

/// Get the size of the block
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nit: this may be #[inline] (but do not discard ACKs b/c of just this)

}

/// Get the strippedsize of the block
pub fn get_strippedsize(&self) -> usize {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
pub fn get_strippedsize(&self) -> usize {
pub fn get_stripped_size(&self) -> usize {

just to be consistent with other function names?

}

/// Gets the size of this transaction excluding the witness data.
pub fn get_strippedsize(&self) -> usize {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Ibid on naming

@dr-orlovsky
Copy link
Copy Markdown
Collaborator

I do not think my nits worth discarding existing reviews. Merging now (3 ACKs) and feel free to propose post-merge PR with these nit fixed, if you'd like to.

@dr-orlovsky dr-orlovsky merged commit 697e8f5 into rust-bitcoin:master Sep 10, 2021
@visvirial visvirial deleted the impl-vsize branch September 12, 2021 11:09
@visvirial
Copy link
Copy Markdown
Contributor Author

Thank you for merging my PR!

Copy link
Copy Markdown
Contributor

@darosior darosior left a comment

Choose a reason for hiding this comment

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

Sorry for seeing this only now. The get_vsize transaction isn't correct as per Bitcoin Core's definition [0]. We already have a correct one in src/policy:

/// The virtual transaction size, as computed by default by bitcoind node.
pub fn get_virtual_tx_size(weight: i64, n_sigops: i64) -> i64 {
(cmp::max(weight, n_sigops * DEFAULT_BYTES_PER_SIGOP as i64) + WITNESS_SCALE_FACTOR as i64 - 1)
/ WITNESS_SCALE_FACTOR as i64
}

Sure, we could assume that users don't actually need the sigop-aware version if they are not going to craft clumsy transactions but:

  • get_vsize could just call get_virtual_tx_size
  • we could at least document the divergence

[0] Arguably there is both a BIP141 and a Core definition, and it's correct as per the former

@dr-orlovsky
Copy link
Copy Markdown
Collaborator

@darosior thanks for spotting. Probably we need a dedicated issue?

@darosior
Copy link
Copy Markdown
Contributor

@dr-orlovsky opened a PR directly going for the simplest alternative: just document the behaviour divergence

@dr-orlovsky dr-orlovsky added this to the 0.28.0 milestone Sep 25, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants