Skip to content

Commit

Permalink
Merge pull request #32 from mempool/mononaut/fix-fee-underflow
Browse files Browse the repository at this point in the history
fix missing prevout tx fee underflow
  • Loading branch information
wiz authored Aug 10, 2023
2 parents 92eb65e + 7a5a27c commit 572a1ec
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 23 deletions.
19 changes: 18 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,23 @@ jobs:
components: rustfmt
- run: cargo fmt --all -- --check

test:
name: Run Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- id: toolchain
uses: dtolnay/[email protected]
- name: Cache dependencies
uses: actions/cache@v3
with: # test cache key is different (adding test cfg is a re-compile)
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-test-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }}
- run: cargo test --package electrs --lib --all-features

clippy:
name: Linter
runs-on: ubuntu-latest
Expand Down Expand Up @@ -63,4 +80,4 @@ jobs:
target
key: ${{ runner.os }}-cargo-${{ steps.toolchain.outputs.cachekey }}-${{ hashFiles('**/Cargo.lock') }}
- name: Clippy with Features = ${{ matrix.features }}
run: cargo clippy ${{ matrix.features }}
run: cargo clippy ${{ matrix.features }} -- -D warnings
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ target
*.sublime*
*~
*.pyc
.vscode
4 changes: 4 additions & 0 deletions scripts/checks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,7 @@ cargo clippy $@ -q -F liquid
TESTNAME="Running cargo clippy check electrum-discovery + liquid"
echo "$TESTNAME"
cargo clippy $@ -q -F electrum-discovery,liquid

TESTNAME="Running cargo test with all features"
echo "$TESTNAME"
cargo test $@ -q --package electrs --lib --all-features
10 changes: 8 additions & 2 deletions src/electrum/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -531,20 +531,26 @@ mod tests {
const PROTOCOL_VERSION: ProtocolVersion = ProtocolVersion::new(1, 4);

#[test]
#[ignore = "This test requires external connection to server that no longer exists"]
fn test() -> Result<()> {
stderrlog::new().verbosity(4).init().unwrap();

#[cfg(feature = "liquid")]
let testnet = Network::LiquidTestnet;
#[cfg(not(feature = "liquid"))]
let testnet = Network::Testnet;

let features = ServerFeatures {
hosts: serde_json::from_str("{\"test.foobar.example\":{\"tcp_port\":60002}}").unwrap(),
server_version: VERSION_STRING.clone(),
genesis_hash: genesis_hash(Network::Testnet),
genesis_hash: genesis_hash(testnet),
protocol_min: PROTOCOL_VERSION,
protocol_max: PROTOCOL_VERSION,
hash_function: "sha256".into(),
pruning: None,
};
let discovery = Arc::new(DiscoveryManager::new(
Network::Testnet,
testnet,
features,
PROTOCOL_VERSION,
false,
Expand Down
2 changes: 1 addition & 1 deletion src/new_index/mempool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ impl Mempool {
for txid in txids {
let tx = self.txstore.get(&txid).expect("missing tx from txstore");

let prevouts = match extract_tx_prevouts(tx, &txos, false) {
let prevouts = match extract_tx_prevouts(tx, &txos) {
Ok(v) => v,
Err(e) => {
warn!("Skipping tx {txid} missing parent error: {e}");
Expand Down
1 change: 1 addition & 0 deletions src/new_index/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ impl Query {
.or_else(|| self.mempool().lookup_raw_txn(txid))
}

/// Not all OutPoints from mempool transactions are guaranteed to be included in the result
pub fn lookup_txos(&self, outpoints: &BTreeSet<OutPoint>) -> HashMap<OutPoint, TxOut> {
// the mempool lookup_txos() internally looks up confirmed txos as well
self.mempool().lookup_txos(outpoints)
Expand Down
27 changes: 19 additions & 8 deletions src/rest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,9 @@ impl TransactionValue {
blockid: Option<BlockId>,
txos: &HashMap<OutPoint, TxOut>,
config: &Config,
) -> Self {
let prevouts =
extract_tx_prevouts(&tx, txos, true).expect("Cannot Err when allow_missing is true");
) -> Result<Self, errors::Error> {
let prevouts = extract_tx_prevouts(&tx, txos)?;

let vins: Vec<TxInValue> = tx
.input
.iter()
Expand All @@ -174,9 +174,9 @@ impl TransactionValue {

let fee = get_tx_fee(&tx, &prevouts, config.network_type);

TransactionValue {
#[allow(clippy::unnecessary_cast)]
Ok(TransactionValue {
txid: tx.txid(),
#[allow(clippy::unnecessary_cast)]
version: tx.version as u32,
locktime: tx.lock_time,
vin: vins,
Expand All @@ -185,7 +185,7 @@ impl TransactionValue {
weight: tx.weight() as u32,
fee,
status: Some(TransactionStatus::from(blockid)),
}
})
}
}

Expand Down Expand Up @@ -519,6 +519,9 @@ fn ttl_by_depth(height: Option<usize>, query: &Query) -> u32 {
})
}

/// Prepare transactions to be serialized in a JSON response
///
/// Any transactions with missing prevouts will be filtered out of the response, rather than returned with incorrect data.
fn prepare_txs(
txs: Vec<(Transaction, Option<BlockId>)>,
query: &Query,
Expand All @@ -537,7 +540,7 @@ fn prepare_txs(
let prevouts = query.lookup_txos(&outpoints);

txs.into_iter()
.map(|(tx, blockid)| TransactionValue::new(tx, blockid, &prevouts, config))
.filter_map(|(tx, blockid)| TransactionValue::new(tx, blockid, &prevouts, config).ok())
.collect()
}

Expand Down Expand Up @@ -1003,7 +1006,15 @@ fn handle_request(

let mut tx = prepare_txs(vec![(tx, blockid)], query, config);

json_response(tx.remove(0), ttl)
if tx.is_empty() {
http_message(
StatusCode::INTERNAL_SERVER_ERROR,
"Transaction missing prevouts",
0,
)
} else {
json_response(tx.remove(0), ttl)
}
}
(&Method::GET, Some(&"tx"), Some(hash), Some(out_type @ &"hex"), None, None)
| (&Method::GET, Some(&"tx"), Some(hash), Some(out_type @ &"raw"), None, None) => {
Expand Down
18 changes: 7 additions & 11 deletions src/util/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,25 +85,21 @@ pub fn is_spendable(txout: &TxOut) -> bool {
pub fn extract_tx_prevouts<'a>(
tx: &Transaction,
txos: &'a HashMap<OutPoint, TxOut>,
allow_missing: bool,
) -> Result<HashMap<u32, &'a TxOut>, errors::Error> {
tx.input
.iter()
.enumerate()
.filter(|(_, txi)| has_prevout(txi))
.filter_map(|(index, txi)| {
Some(Ok((
.map(|(index, txi)| {
Ok((
index as u32,
match (allow_missing, txos.get(&txi.previous_output)) {
(_, Some(txo)) => txo,
(true, None) => return None,
(false, None) => {
return Some(Err(
format!("missing outpoint {:?}", txi.previous_output).into()
));
match txos.get(&txi.previous_output) {
Some(txo) => txo,
None => {
return Err(format!("missing outpoint {:?}", txi.previous_output).into());
}
},
)))
))
})
.collect()
}
Expand Down

0 comments on commit 572a1ec

Please sign in to comment.