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

chore: forge fmt #76

Merged
merged 1 commit into from
Nov 1, 2023
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
11 changes: 8 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: test

on: workflow_dispatch
on: [pull_request]

env:
FOUNDRY_PROFILE: ci
Expand All @@ -10,7 +10,7 @@ jobs:
strategy:
fail-fast: true

name: Foundry project
name: Foundry
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -22,9 +22,14 @@ jobs:
with:
version: nightly

- name: Run Forge build
- name: Run Forge fmt
run: |
forge --version
forge fmt --check
id: fmt

- name: Run Forge build
run: |
forge build --sizes
id: build

Expand Down
102 changes: 24 additions & 78 deletions src/bridge/BitcoinTx.sol
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,6 @@ library BitcoinTx {
/// @dev `lock_time` from raw Bitcoin transaction data.
/// Encoded as 4-bytes unsigned integer, little endian.
bytes4 locktime;
// This struct doesn't contain `__gap` property as the structure is not
// stored, it is used as a function's calldata argument.
}

/// @notice Represents data needed to perform a Bitcoin SPV proof.
Expand All @@ -128,44 +126,26 @@ library BitcoinTx {
/// @notice Single byte-string of 80-byte bitcoin headers,
/// lowest height first.
bytes bitcoinHeaders;
// This struct doesn't contain `__gap` property as the structure is not
// stored, it is used as a function's calldata argument.
}

/// @notice Validates the SPV proof of the Bitcoin transaction.
/// Reverts in case the validation or proof verification fail.
/// @param txInfo Bitcoin transaction data.
/// @param proof Bitcoin proof data.
/// @return txHash Proven 32-byte transaction hash.
function validateProof(
BridgeState.Storage storage self,
Info memory txInfo,
Proof memory proof
) internal view returns (bytes32 txHash) {
require(
txInfo.inputVector.validateVin(),
"Invalid input vector provided"
);
require(
txInfo.outputVector.validateVout(),
"Invalid output vector provided"
);
function validateProof(BridgeState.Storage storage self, Info memory txInfo, Proof memory proof)
internal
view
returns (bytes32 txHash)
{
require(txInfo.inputVector.validateVin(), "Invalid input vector provided");
require(txInfo.outputVector.validateVout(), "Invalid output vector provided");

txHash = abi
.encodePacked(
txInfo.version,
txInfo.inputVector,
txInfo.outputVector,
txInfo.locktime
)
.hash256View();
txHash =
abi.encodePacked(txInfo.version, txInfo.inputVector, txInfo.outputVector, txInfo.locktime).hash256View();

require(
txHash.prove(
proof.bitcoinHeaders.extractMerkleRootLE(),
proof.merkleProof,
proof.txIndexInBlock
),
txHash.prove(proof.bitcoinHeaders.extractMerkleRootLE(), proof.merkleProof, proof.txIndexInBlock),
"Tx merkle proof is not valid for provided header and tx hash"
);

Expand All @@ -179,12 +159,9 @@ library BitcoinTx {
/// Reverts in case the evaluation fails.
/// @param bitcoinHeaders Bitcoin headers chain being part of the SPV
/// proof. Used to extract the observed proof difficulty.
function evaluateProofDifficulty(
BridgeState.Storage storage self,
bytes memory bitcoinHeaders
) internal view {
function evaluateProofDifficulty(BridgeState.Storage storage self, bytes memory bitcoinHeaders) internal view {
IRelay relay = self.relay;

// for testing
if (!relay.difficultyCheckEnabled()) {
return;
Expand All @@ -194,9 +171,7 @@ library BitcoinTx {
uint256 previousEpochDifficulty = relay.getPrevEpochDifficulty();

uint256 requestedDiff = 0;
uint256 firstHeaderDiff = bitcoinHeaders
.extractTarget()
.calculateDifficulty();
uint256 firstHeaderDiff = bitcoinHeaders.extractTarget().calculateDifficulty();

if (firstHeaderDiff == currentEpochDifficulty) {
requestedDiff = currentEpochDifficulty;
Expand All @@ -208,18 +183,9 @@ library BitcoinTx {

uint256 observedDiff = bitcoinHeaders.validateHeaderChain();

require(
observedDiff != ValidateSPV.getErrBadLength(),
"Invalid length of the headers chain"
);
require(
observedDiff != ValidateSPV.getErrInvalidChain(),
"Invalid headers chain"
);
require(
observedDiff != ValidateSPV.getErrLowWork(),
"Insufficient work in a header"
);
require(observedDiff != ValidateSPV.getErrBadLength(), "Invalid length of the headers chain");
require(observedDiff != ValidateSPV.getErrInvalidChain(), "Invalid headers chain");
require(observedDiff != ValidateSPV.getErrLowWork(), "Insufficient work in a header");

require(
observedDiff >= requestedDiff * self.txProofDifficultyFactor,
Expand All @@ -236,8 +202,6 @@ library BitcoinTx {
uint256 outputStartingIndex;
// The number of outputs in the transaction.
uint256 outputsCount;
// This struct doesn't contain `__gap` property as the structure is not
// stored, it is used as a function's memory argument.
}

/// @notice Processes the Bitcoin transaction output vector.
Expand All @@ -247,16 +211,10 @@ library BitcoinTx {
/// must be validated using e.g. `BTCUtils.validateVout` function
/// before it is passed here.
/// @return value Outcomes of the processing.
function getTxOutputValue(
bytes32 scriptPubKeyHash,
bytes memory txOutputVector
) internal returns (uint64 value) {
function getTxOutputValue(bytes32 scriptPubKeyHash, bytes memory txOutputVector) internal returns (uint64 value) {
// Determining the total number of transaction outputs in the same way as
// for number of inputs. See `BitcoinTx.outputVector` docs for more details.
(
uint256 outputsCompactSizeUintLength,
uint256 outputsCount
) = txOutputVector.parseVarInt();
(uint256 outputsCompactSizeUintLength, uint256 outputsCount) = txOutputVector.parseVarInt();

// To determine the first output starting index, we must jump over
// the compactSize uint which prepends the output vector. One byte
Expand All @@ -275,15 +233,9 @@ library BitcoinTx {
// docs in `BitcoinTx` library for more details.
uint256 outputStartingIndex = 1 + outputsCompactSizeUintLength;

return
getTxOutputValue(
scriptPubKeyHash,
txOutputVector,
TxOutputsProcessingInfo(
outputStartingIndex,
outputsCount
)
);
return getTxOutputValue(
scriptPubKeyHash, txOutputVector, TxOutputsProcessingInfo(outputStartingIndex, outputsCount)
);
}

/// @notice Processes all outputs from the transaction.
Expand All @@ -300,13 +252,10 @@ library BitcoinTx {
) internal returns (uint64 value) {
// Outputs processing loop.
for (uint256 i = 0; i < processInfo.outputsCount; i++) {
uint256 outputLength = txOutputVector
.determineOutputLengthAt(processInfo.outputStartingIndex);
uint256 outputLength = txOutputVector.determineOutputLengthAt(processInfo.outputStartingIndex);

// Extract the value from given output.
uint64 outputValue = txOutputVector.extractValueAt(
processInfo.outputStartingIndex
);
uint64 outputValue = txOutputVector.extractValueAt(processInfo.outputStartingIndex);

// The output consists of an 8-byte value and a variable length
// script. To hash that script we slice the output starting from
Expand All @@ -322,10 +271,7 @@ library BitcoinTx {
// by `outputScriptStart`. To load that position, we
// need to call `add(outputScriptStart, 32)` because
// `outputScriptStart` has 32 bytes.
outputScriptHash := keccak256(
add(txOutputVector, add(outputScriptStart, 32)),
scriptLength
)
outputScriptHash := keccak256(add(txOutputVector, add(outputScriptStart, 32)), scriptLength)
}

if (scriptPubKeyHash == outputScriptHash) {
Expand Down
70 changes: 28 additions & 42 deletions src/bridge/WitnessTx.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ library WitnessTx {
using SegWitUtils for bytes;
using ValidateSPV for bytes32;

bytes1 constant SEGWIT_MARKER = hex"00";
bytes1 constant SEGWIT_FLAG = hex"01";

/// @notice Represents a Bitcoin transaction with the witness data.
struct WitnessInfo {
/// @notice Bitcoin transaction info.
Expand Down Expand Up @@ -45,31 +48,21 @@ library WitnessTx {
/// @param txInfo Bitcoin transaction data.
/// @param proof Bitcoin proof data.
/// @return wTxHash Proven 32-byte transaction hash.
function validateWitnessProof(
WitnessInfo memory txInfo,
WitnessProof memory proof
) internal view returns (bytes32 wTxHash) {
require(
proof.coinbaseTx.outputVector.validateVout(),
"Invalid coinbase output vector provided"
);
require(
txInfo.info.inputVector.validateVin(),
"Invalid payment input vector provided"
);
require(
txInfo.info.outputVector.validateVout(),
"Invalid payment output vector provided"
);
function validateWitnessProof(WitnessInfo memory txInfo, WitnessProof memory proof)
internal
view
returns (bytes32 wTxHash)
{
require(proof.coinbaseTx.outputVector.validateVout(), "Invalid coinbase output vector provided");
require(txInfo.info.inputVector.validateVin(), "Invalid payment input vector provided");
require(txInfo.info.outputVector.validateVout(), "Invalid payment output vector provided");

bytes32 coinbaseTxHash = abi
.encodePacked(
proof.coinbaseTx.version,
proof.coinbaseTx.inputVector,
proof.coinbaseTx.outputVector,
proof.coinbaseTx.locktime
)
.hash256View();
bytes32 coinbaseTxHash = abi.encodePacked(
proof.coinbaseTx.version,
proof.coinbaseTx.inputVector,
proof.coinbaseTx.outputVector,
proof.coinbaseTx.locktime
).hash256View();

require(
coinbaseTxHash.prove(
Expand All @@ -80,32 +73,25 @@ library WitnessTx {
"Tx merkle proof is not valid for provided header and tx hash"
);

bytes32 paymentWTxId = abi
.encodePacked(
txInfo.info.version,
hex"00", // SEGWIT_MARKER
hex"01", // SEGWIT_FLAG
txInfo.info.inputVector,
txInfo.info.outputVector,
txInfo.witnessVector,
txInfo.info.locktime
)
.hash256View();
bytes32 paymentWTxId = abi.encodePacked(
txInfo.info.version,
SEGWIT_MARKER,
SEGWIT_FLAG,
txInfo.info.inputVector,
txInfo.info.outputVector,
txInfo.witnessVector,
txInfo.info.locktime
).hash256View();

require(
paymentWTxId.prove(
proof.paymentMerkleRoot,
proof.paymentProof.merkleProof,
proof.paymentProof.txIndexInBlock
proof.paymentMerkleRoot, proof.paymentProof.merkleProof, proof.paymentProof.txIndexInBlock
),
"Tx witness merkle proof is not valid for provided header and tx hash"
);

// witnessCommitment = SHA256(witnessMerkleRoot || witnessNonce)
bytes32 witnessCommitment = abi.encodePacked(
proof.paymentMerkleRoot,
proof.witnessNonce
).hash256View();
bytes32 witnessCommitment = abi.encodePacked(proof.paymentMerkleRoot, proof.witnessNonce).hash256View();

// extract coinbase commitment from tx out
bytes32 coinbaseWitnessCommitment = proof.coinbaseTx.outputVector.extractWitnessCommitment();
Expand Down
Loading