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

fix: resolve conflict descendants #3264

Merged
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
7 changes: 4 additions & 3 deletions devtools/doc/rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ def handle_starttag(self, tag, attrs):
if self.next_variant is None:
if tag == 'div':
attrs_dict = dict(attrs)
if 'id' in attrs_dict and attrs_dict['id'].startswith('variant.'):
if 'id' in attrs_dict and attrs_dict['id'].startswith('variant.') and ('class', 'variant small-section-header') in attrs:
self.next_variant = camel_to_snake(
attrs_dict['id'].split('.')[1])
elif self.variant_parser is None:
Expand All @@ -521,13 +521,14 @@ def handle_starttag(self, tag, attrs):
self.variant_parser.handle_starttag(tag, attrs)

def handle_endtag(self, tag):
if self.variant_parser is not None:
if self.next_variant is not None and self.variant_parser is not None:
self.variant_parser.handle_endtag(tag)
if self.variant_parser.completed():
if self.next_variant not in [v[0] for v in self.variants]:
self.variants.append((self.next_variant, self.variant_parser))
self.variant_parser = None
self.next_variant = None
self.variant_parser = None


def handle_data(self, data):
if self.variant_parser is not None:
Expand Down
23 changes: 14 additions & 9 deletions rpc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3116,6 +3116,10 @@ Pool rejects a large package of chained transactions to avoid certain kinds of D

For example, a cellbase transaction is not allowed in `send_transaction` RPC.

### Error `TransactionExpired`

(-1109): The transaction is expired from tx-pool after `expiry_hours`.


## RPC Types

Expand Down Expand Up @@ -3725,7 +3729,7 @@ An enum to represent the two kinds of dao withdrawal amount calculation option.
`DaoWithdrawingCalculationKind` is equivalent to `"withdrawing_header_hash" | "withdrawing_out_point"`.

* the assumed reference block hash for withdrawing phase 1 transaction
* Returns a copy of the value. [Read more](#method-clone)
* the out point of the withdrawing phase 1 transaction


### Type `DepType`
Expand Down Expand Up @@ -4192,19 +4196,20 @@ TX reject message

`PoolTransactionReject` is a JSON object with following fields.

* `type`: `"LowFeeRate" | "ExceededMaximumAncestorsCount" | "Full" | "Duplicated" | "Malformed" | "DeclaredWrongCycles" | "Resolve" | "Verification"` - Reject type.
* `type`: `"LowFeeRate" | "ExceededMaximumAncestorsCount" | "Full" | "Duplicated" | "Malformed" | "DeclaredWrongCycles" | "Resolve" | "Verification" | "Expiry"` - Reject type.
* `description`: `string` - Detailed description about why the transaction is rejected.

Different reject types:

* `LowFeeRate`: Transaction fee lower than config
* `ExceededMaximumAncestorsCount`: Transaction pool exceeded maximum size or cycles limit,
* `Full`: Transaction already exist in transaction_pool
* `Duplicated`: Malformed transaction
* `Malformed`: Declared wrong cycles
* `DeclaredWrongCycles`: Resolve failed
* `Resolve`: Verification failed
* `Verification`: Returns a copy of the value. [Read more](#method-clone)
* `ExceededMaximumAncestorsCount`: Transaction exceeded maximum ancestors count limit
* `Full`: Transaction pool exceeded maximum size or cycles limit,
* `Duplicated`: Transaction already exist in transaction_pool
* `Malformed`: Malformed transaction
* `DeclaredWrongCycles`: Declared wrong cycles
* `Resolve`: Resolve failed
* `Verification`: Verification failed
* `Expiry`: Transaction expired


### Type `ProposalShortId`
Expand Down
3 changes: 3 additions & 0 deletions rpc/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ pub enum RPCError {
///
/// For example, a cellbase transaction is not allowed in `send_transaction` RPC.
PoolRejectedMalformedTransaction = -1108,
/// (-1109): The transaction is expired from tx-pool after `expiry_hours`.
TransactionExpired = -1109,
}

impl RPCError {
Expand Down Expand Up @@ -167,6 +169,7 @@ impl RPCError {
Reject::DeclaredWrongCycles(..) => RPCError::PoolRejectedMalformedTransaction,
Reject::Resolve(_) => RPCError::TransactionFailedToResolve,
Reject::Verification(_) => RPCError::TransactionFailedToVerify,
Reject::Expiry(_) => RPCError::TransactionExpired,
};
RPCError::custom_with_error(code, reject)
}
Expand Down
1 change: 1 addition & 0 deletions test/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ fn all_specs() -> Vec<Box<dyn Spec>> {
Box::new(ConflictInPending),
Box::new(ConflictInGap),
Box::new(ConflictInProposed),
Box::new(RemoveConflictFromPending),
Box::new(SubmitConflict),
Box::new(DAOVerify),
Box::new(AvoidDuplicatedProposalsWithUncles),
Expand Down
54 changes: 49 additions & 5 deletions test/src/specs/tx_pool/collision.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use crate::util::{check::is_transaction_committed, mining::mine};
use crate::util::{
check::{is_transaction_committed, is_transaction_pending, is_transaction_unknown},
mining::mine,
};
use crate::utils::{assert_send_transaction_fail, blank, commit, propose};
use crate::{Node, Spec};
use ckb_types::bytes::Bytes;
use ckb_types::core::{Capacity, TransactionView};
use ckb_types::core::{capacity_bytes, Capacity, TransactionView};
use ckb_types::prelude::*;

// Convention:
Expand Down Expand Up @@ -145,14 +148,49 @@ impl Spec for SubmitConflict {
}
}

fn conflict_transactions(node: &Node) -> (TransactionView, TransactionView) {
pub struct RemoveConflictFromPending;

impl Spec for RemoveConflictFromPending {
fn run(&self, nodes: &mut Vec<Node>) {
let node = &nodes[0];
let window = node.consensus().tx_proposal_window();
mine(node, window.farthest() + 2);

let (txa, txb) =
conflict_transactions_with_capacity(node, Bytes::new(), capacity_bytes!(1000));
let txc = node.new_transaction_with_since_capacity(txb.hash(), 0, capacity_bytes!(100));
node.submit_transaction(&txa);
node.submit_transaction(&txb);
node.submit_transaction(&txc);

assert!(is_transaction_pending(node, &txa));
assert!(is_transaction_pending(node, &txb));
assert!(is_transaction_pending(node, &txc));

node.submit_block(&propose(node, &[&txa]));
(0..window.closest()).for_each(|_| {
node.submit_block(&blank(node));
});
node.submit_block(&commit(node, &[&txa]));
node.wait_for_tx_pool();

assert!(is_transaction_committed(node, &txa));
assert!(is_transaction_unknown(node, &txb));
assert!(is_transaction_unknown(node, &txc));
}
}

fn conflict_transactions_with_capacity(
node: &Node,
output_data: Bytes,
cap: Capacity,
) -> (TransactionView, TransactionView) {
let txa = node.new_transaction_spend_tip_cellbase();
let output_data = Bytes::from(b"b0b".to_vec());
let output = txa
.output(0)
.unwrap()
.as_builder()
.build_exact_capacity(Capacity::bytes(output_data.len()).unwrap())
.build_exact_capacity(cap)
.unwrap();
let txb = txa
.as_advanced_builder()
Expand All @@ -163,6 +201,12 @@ fn conflict_transactions(node: &Node) -> (TransactionView, TransactionView) {
(txa, txb)
}

fn conflict_transactions(node: &Node) -> (TransactionView, TransactionView) {
let output_data = Bytes::from(b"b0b".to_vec());
let cap = Capacity::bytes(output_data.len()).unwrap();
conflict_transactions_with_capacity(node, output_data, cap)
}

fn cousin_txs_with_same_hash_different_witness_hash(
node: &Node,
) -> (TransactionView, TransactionView) {
Expand Down
Loading