Skip to content

Commit

Permalink
relax coin amount in fast-forward logic
Browse files Browse the repository at this point in the history
  • Loading branch information
arvidn committed Dec 14, 2023
1 parent e3ed6a4 commit f8e3ab2
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 45 deletions.
56 changes: 15 additions & 41 deletions src/fast_forward.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,6 @@ pub fn fast_forward_singleton(
return Err(Error::CoinAmountEven);
}

// in the case of fast-forwarding a spend, we require the amount to remain
// unchanged
if coin.amount != new_coin.amount || coin.amount != new_parent.amount {
return Err(Error::CoinAmountMismatch);
}

// we can only fast-forward spends of singletons whose puzzle hash doesn't
// change
if coin.puzzle_hash != new_parent.puzzle_hash || coin.puzzle_hash != new_coin.puzzle_hash {
Expand All @@ -126,14 +120,6 @@ pub fn fast_forward_singleton(
return Err(Error::NotSingletonModHash);
}

// we can only fast-forward if the coin amount stay the same
// this is to minimize the risk of producing an invalid spend, after
// fast-forward. e.g. we might end up attempting to spend more that the
// amount of the coin
if coin.amount != new_solution.lineage_proof.parent_amount || coin.amount != new_parent.amount {
return Err(Error::CoinAmountMismatch);
}

// given the parent's parent, the parent's inner puzzle and parent's amount,
// we can compute the hash of the curried inner puzzle for our parent coin
let parent_puzzle_hash = curry_and_treehash(
Expand Down Expand Up @@ -169,6 +155,8 @@ pub fn fast_forward_singleton(

// update the solution to use the new parent coin's information
new_solution.lineage_proof.parent_parent_coin_id = new_parent.parent_coin_info;
new_solution.lineage_proof.parent_amount = new_parent.amount;
new_solution.amount = new_coin.amount;

let expected_new_parent = new_parent.coin_id();

Expand Down Expand Up @@ -206,6 +194,8 @@ mod tests {
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
)]
new_parents_parent: &str,
#[values(0, 1, 3, 5)] new_amount: u64,
#[values(0, 1, 3, 5)] prev_amount: u64,
) {
let spend_bytes = fs::read(format!("ff-tests/{spend_file}.spend")).expect("read file");
let spend = CoinSpend::from_bytes(&spend_bytes).expect("parse CoinSpend");
Expand All @@ -219,13 +209,21 @@ mod tests {
let new_parent_coin = Coin {
parent_coin_info: new_parents_parent.as_slice().into(),
puzzle_hash,
amount: spend.coin.amount,
amount: if prev_amount == 0 {
spend.coin.amount
} else {
prev_amount
},
};

let new_coin = Coin {
parent_coin_info: new_parent_coin.coin_id().into(),
puzzle_hash,
amount: spend.coin.amount,
amount: if new_amount == 0 {
spend.coin.amount
} else {
new_amount
},
};

// perform fast-forward
Expand Down Expand Up @@ -345,30 +343,6 @@ mod tests {
);
}

#[test]
fn test_amount_mismatch() {
run_ff_test(
|_a, coin, _new_coin, _new_parent, _puzzle, _solution| {
coin.amount = 3;
},
Error::CoinAmountMismatch,
);

run_ff_test(
|_a, _coin, new_coin, _new_parent, _puzzle, _solution| {
new_coin.amount = 3;
},
Error::CoinAmountMismatch,
);

run_ff_test(
|_a, _coin, _new_coin, new_parent, _puzzle, _solution| {
new_parent.amount = 3;
},
Error::CoinAmountMismatch,
);
}

fn parse_solution(a: &mut Allocator, solution: &[u8]) -> SingletonSolution<NodePtr> {
let new_solution = node_from_bytes(a, solution).expect("parse solution");
SingletonSolution::from_clvm(a, new_solution).expect("parse solution")
Expand Down Expand Up @@ -423,7 +397,7 @@ mod tests {

*solution = serialize_solution(a, &new_solution);
},
Error::CoinAmountMismatch,
Error::ParentCoinMismatch,
);
}

Expand Down
10 changes: 6 additions & 4 deletions src/gen/conditions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,10 @@ impl SpendVisitor for MempoolVisitor {
// to look for something that looks like a singleton output, with the same
// puzzle hash as our input coin
if (spend.flags & ELIGIBLE_FOR_FF) != 0
&& !spend.create_coin.iter().any(|c| {
c.amount == spend.coin_amount && a.atom(spend.puzzle_hash) == c.puzzle_hash
})
&& !spend
.create_coin
.iter()
.any(|c| (c.amount & 1) == 1 && a.atom(spend.puzzle_hash) == c.puzzle_hash)
{
spend.flags &= !ELIGIBLE_FOR_FF;
}
Expand Down Expand Up @@ -2753,7 +2754,7 @@ fn test_multiple_create_coin() {
amount: 43_u64,
hint: a.null()
}));
assert_eq!(spend.flags, ELIGIBLE_FOR_DEDUP);
assert_eq!(spend.flags, ELIGIBLE_FOR_DEDUP | ELIGIBLE_FOR_FF);
}

#[test]
Expand Down Expand Up @@ -4311,6 +4312,7 @@ fn test_eligible_for_ff_even_amount() {
#[cfg(test)]
#[rstest]
#[case(123, "{h2}", true)]
#[case(121, "{h2}", true)]
#[case(122, "{h1}", false)]
#[case(1, "{h1}", false)]
#[case(123, "{h1}", false)]
Expand Down

0 comments on commit f8e3ab2

Please sign in to comment.