diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9282e8294..77ed4d7ca 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,6 @@ name: test -on: workflow_dispatch +on: [pull_request] env: FOUNDRY_PROFILE: ci @@ -25,6 +25,7 @@ jobs: - name: Run Forge build run: | forge --version + forge fmt --check forge build --sizes id: build diff --git a/src/bridge/BitcoinTx.sol b/src/bridge/BitcoinTx.sol index 71c89dc2c..1e4616e08 100644 --- a/src/bridge/BitcoinTx.sol +++ b/src/bridge/BitcoinTx.sol @@ -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. @@ -128,8 +126,6 @@ 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. @@ -137,35 +133,19 @@ library BitcoinTx { /// @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" ); @@ -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; @@ -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; @@ -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, @@ -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. @@ -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 @@ -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. @@ -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 @@ -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) { diff --git a/src/bridge/WitnessTx.sol b/src/bridge/WitnessTx.sol index a961c559d..c564771a0 100644 --- a/src/bridge/WitnessTx.sol +++ b/src/bridge/WitnessTx.sol @@ -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. @@ -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( @@ -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(); diff --git a/src/relay/LightRelay.sol b/src/relay/LightRelay.sol index 6a412fd87..46a98cd2a 100644 --- a/src/relay/LightRelay.sol +++ b/src/relay/LightRelay.sol @@ -34,20 +34,11 @@ interface ILightRelay is IRelay { view returns (uint256 startingHeaderTimestamp, uint256 headerCount); - function getBlockDifficulty(uint256 blockNumber) - external - view - returns (uint256); + function getBlockDifficulty(uint256 blockNumber) external view returns (uint256); - function getEpochDifficulty(uint256 epochNumber) - external - view - returns (uint256); + function getEpochDifficulty(uint256 epochNumber) external view returns (uint256); - function getRelayRange() - external - view - returns (uint256 relayGenesis, uint256 currentEpochEnd); + function getRelayRange() external view returns (uint256 relayGenesis, uint256 currentEpochEnd); } library RelayUtils { @@ -59,11 +50,7 @@ library RelayUtils { /// @return The timestamp of the header. /// @dev Assumes that the specified position contains a valid header. /// Performs no validation whatsoever. - function extractTimestampAt(bytes memory headers, uint256 at) - internal - pure - returns (uint32) - { + function extractTimestampAt(bytes memory headers, uint256 at) internal pure returns (uint32) { return BTCUtils.reverseUint32(uint32(headers.slice4(68 + at))); } } @@ -119,19 +106,15 @@ contract LightRelay is Ownable, ILightRelay { /// @dev If the relay is used by querying the current and previous epoch /// difficulty, at least one retarget needs to be provided after genesis; /// otherwise the prevEpochDifficulty will be uninitialised and zero. - function genesis( - bytes calldata genesisHeader, - uint256 genesisHeight, - uint64 genesisProofLength - ) external onlyOwner { + function genesis(bytes calldata genesisHeader, uint256 genesisHeight, uint64 genesisProofLength) + external + onlyOwner + { require(!ready, "Genesis already performed"); require(genesisHeader.length == 80, "Invalid genesis header length"); - require( - genesisHeight % 2016 == 0, - "Invalid height of relay genesis block" - ); + require(genesisHeight % 2016 == 0, "Invalid height of relay genesis block"); require(genesisProofLength < 2016, "Proof length excessive"); require(genesisProofLength > 0, "Proof length may not be zero"); @@ -140,10 +123,7 @@ contract LightRelay is Ownable, ILightRelay { currentEpoch = genesisEpoch; uint256 genesisTarget = genesisHeader.extractTarget(); uint256 genesisTimestamp = genesisHeader.extractTimestamp(); - epochs[genesisEpoch] = Epoch( - uint32(genesisTimestamp), - uint224(genesisTarget) - ); + epochs[genesisEpoch] = Epoch(uint32(genesisTimestamp), uint224(genesisTarget)); proofLength = genesisProofLength; currentEpochDifficulty = BTCUtils.calculateDifficulty(genesisTarget); ready = true; @@ -228,23 +208,15 @@ contract LightRelay is Ownable, ILightRelay { // Validate old chain for (uint256 i = 0; i < proofLength; i++) { - ( - bytes32 currentDigest, - uint256 currentHeaderTarget - ) = validateHeader(headers, i * 80, previousHeaderDigest); + (bytes32 currentDigest, uint256 currentHeaderTarget) = validateHeader(headers, i * 80, previousHeaderDigest); - require( - currentHeaderTarget == oldTarget, - "Invalid target in pre-retarget headers" - ); + require(currentHeaderTarget == oldTarget, "Invalid target in pre-retarget headers"); previousHeaderDigest = currentDigest; } // get timestamp of retarget block - uint256 epochEndTimestamp = headers.extractTimestampAt( - (proofLength - 1) * 80 - ); + uint256 epochEndTimestamp = headers.extractTimestampAt((proofLength - 1) * 80); // An attacker could produce blocks with timestamps in the future, // in an attempt to reduce the difficulty after the retarget @@ -261,25 +233,17 @@ contract LightRelay is Ownable, ILightRelay { ); // Expected target is the full-length target - uint256 expectedTarget = BTCUtils.retargetAlgorithm( - oldTarget, - latest.timestamp, - epochEndTimestamp - ); + uint256 expectedTarget = BTCUtils.retargetAlgorithm(oldTarget, latest.timestamp, epochEndTimestamp); // Mined target is the header-encoded target uint256 minedTarget = 0; - uint256 epochStartTimestamp = headers.extractTimestampAt( - proofLength * 80 - ); + uint256 epochStartTimestamp = headers.extractTimestampAt(proofLength * 80); // validate new chain for (uint256 j = proofLength; j < proofLength * 2; j++) { - ( - bytes32 _currentDigest, - uint256 _currentHeaderTarget - ) = validateHeader(headers, j * 80, previousHeaderDigest); + (bytes32 _currentDigest, uint256 _currentHeaderTarget) = + validateHeader(headers, j * 80, previousHeaderDigest); if (minedTarget == 0) { // The new target has not been set, so check its correctness @@ -299,16 +263,12 @@ contract LightRelay is Ownable, ILightRelay { // We can't compare the precise and less precise representations together // so we first mask them to obtain the less precise version: // (full & truncated) == truncated - _currentHeaderTarget == - (expectedTarget & _currentHeaderTarget), + _currentHeaderTarget == (expectedTarget & _currentHeaderTarget), "Invalid target in new epoch" ); } else { // The new target has been set, so remaining targets should match. - require( - _currentHeaderTarget == minedTarget, - "Unexpected target change after retarget" - ); + require(_currentHeaderTarget == minedTarget, "Unexpected target change after retarget"); } previousHeaderDigest = _currentDigest; @@ -316,10 +276,7 @@ contract LightRelay is Ownable, ILightRelay { currentEpoch = currentEpoch + 1; - epochs[currentEpoch] = Epoch( - uint32(epochStartTimestamp), - uint224(minedTarget) - ); + epochs[currentEpoch] = Epoch(uint32(epochStartTimestamp), uint224(minedTarget)); uint256 oldDifficulty = currentEpochDifficulty; uint256 newDifficulty = BTCUtils.calculateDifficulty(minedTarget); @@ -368,20 +325,14 @@ contract LightRelay is Ownable, ILightRelay { headerCount = headers.length / 80; - require( - headerCount > 1 && headerCount < 2016, - "Invalid number of headers" - ); + require(headerCount > 1 && headerCount < 2016, "Invalid number of headers"); startingHeaderTimestamp = headers.extractTimestamp(); // Short-circuit the first header's validation. // We validate the header here to get the target which is needed to // precisely identify the epoch. - ( - bytes32 previousHeaderDigest, - uint256 currentHeaderTarget - ) = validateHeader(headers, 0, bytes32(0)); + (bytes32 previousHeaderDigest, uint256 currentHeaderTarget) = validateHeader(headers, 0, bytes32(0)); Epoch memory nullEpoch = Epoch(0, 0); @@ -411,10 +362,7 @@ contract LightRelay is Ownable, ILightRelay { // by reaching the most recent epoch whose starting timestamp // or reached before the genesis where epoch slots are empty. // Therefore check that the timestamp is nonzero. - require( - startingEpoch.timestamp > 0, - "Cannot validate chains before relay genesis" - ); + require(startingEpoch.timestamp > 0, "Cannot validate chains before relay genesis"); // The targets don't match. This could be because the block is invalid, // or it could be because of timestamp inaccuracy. @@ -439,10 +387,7 @@ contract LightRelay is Ownable, ILightRelay { // We have failed to find a match, // therefore the target has to be invalid. - require( - currentHeaderTarget == startingEpoch.target, - "Invalid target in header chain" - ); + require(currentHeaderTarget == startingEpoch.target, "Invalid target in header chain"); } } @@ -450,11 +395,7 @@ contract LightRelay is Ownable, ILightRelay { // Validate the rest. for (uint256 i = 1; i < headerCount; i++) { bytes32 currentDigest; - (currentDigest, currentHeaderTarget) = validateHeader( - headers, - i * 80, - previousHeaderDigest - ); + (currentDigest, currentHeaderTarget) = validateHeader(headers, i * 80, previousHeaderDigest); // If the header's target does not match the expected target, // check if a retarget is possible. @@ -469,14 +410,11 @@ contract LightRelay is Ownable, ILightRelay { // In this case the target must match the next epoch's target, // and the header's timestamp must match the epoch's start. if (currentHeaderTarget != startingEpoch.target) { - uint256 currentHeaderTimestamp = headers.extractTimestampAt( - i * 80 - ); + uint256 currentHeaderTimestamp = headers.extractTimestampAt(i * 80); require( - nextEpoch.timestamp != 0 && - currentHeaderTarget == nextEpoch.target && - currentHeaderTimestamp == nextEpoch.timestamp, + nextEpoch.timestamp != 0 && currentHeaderTarget == nextEpoch.target + && currentHeaderTimestamp == nextEpoch.timestamp, "Invalid target in header chain" ); @@ -495,11 +433,7 @@ contract LightRelay is Ownable, ILightRelay { /// range (at or after the relay genesis, and at or before the end of the /// most recent epoch proven to the relay). /// @return The difficulty of the epoch. - function getBlockDifficulty(uint256 blockNumber) - external - view - returns (uint256) - { + function getBlockDifficulty(uint256 blockNumber) external view returns (uint256) { return getEpochDifficulty(blockNumber / 2016); } @@ -514,11 +448,7 @@ contract LightRelay is Ownable, ILightRelay { /// included in header chains for the relay to validate. /// @return currentEpochEnd The height of the last block that can be /// included in header chains for the relay to validate. - function getRelayRange() - external - view - returns (uint256 relayGenesis, uint256 currentEpochEnd) - { + function getRelayRange() external view returns (uint256 relayGenesis, uint256 currentEpochEnd) { relayGenesis = genesisEpoch * 2016; currentEpochEnd = (currentEpoch * 2016) + 2015; } @@ -526,12 +456,7 @@ contract LightRelay is Ownable, ILightRelay { /// @notice Returns the difficulty of the current epoch. /// @dev returns 0 if the relay is not ready. /// @return The difficulty of the current epoch. - function getCurrentEpochDifficulty() - external - view - virtual - returns (uint256) - { + function getCurrentEpochDifficulty() external view virtual returns (uint256) { return currentEpochDifficulty; } @@ -542,11 +467,7 @@ contract LightRelay is Ownable, ILightRelay { return prevEpochDifficulty; } - function getCurrentAndPrevEpochDifficulty() - external - view - returns (uint256 current, uint256 previous) - { + function getCurrentAndPrevEpochDifficulty() external view returns (uint256 current, uint256 previous) { return (currentEpochDifficulty, prevEpochDifficulty); } @@ -554,21 +475,13 @@ contract LightRelay is Ownable, ILightRelay { return true; } - /// @notice Get the difficulty of the specified epoch. /// @param epochNumber The number of the epoch (the height of the first /// block of the epoch, divided by 2016). Must fall within the relay range. /// @return The difficulty of the epoch. - function getEpochDifficulty(uint256 epochNumber) - public - view - returns (uint256) - { + function getEpochDifficulty(uint256 epochNumber) public view returns (uint256) { require(epochNumber >= genesisEpoch, "Epoch is before relay genesis"); - require( - epochNumber <= currentEpoch, - "Epoch is not proven to the relay yet" - ); + require(epochNumber <= currentEpoch, "Epoch is not proven to the relay yet"); return BTCUtils.calculateDifficulty(epochs[epochNumber].target); } @@ -582,17 +495,14 @@ contract LightRelay is Ownable, ILightRelay { /// @return target The PoW target of the header. /// @dev Throws an exception if the header's chain or PoW are invalid. /// Performs no other validation. - function validateHeader( - bytes memory headers, - uint256 start, - bytes32 prevDigest - ) internal view returns (bytes32 digest, uint256 target) { + function validateHeader(bytes memory headers, uint256 start, bytes32 prevDigest) + internal + view + returns (bytes32 digest, uint256 target) + { // If previous block digest has been provided, require that it matches if (prevDigest != bytes32(0)) { - require( - headers.validateHeaderPrevHash(start, prevDigest), - "Invalid chain" - ); + require(headers.validateHeaderPrevHash(start, prevDigest), "Invalid chain"); } // Require that the header has sufficient work for its stated target diff --git a/src/swap/Btc_Marketplace.sol b/src/swap/Btc_Marketplace.sol index da7d15dd2..505442e16 100644 --- a/src/swap/Btc_Marketplace.sol +++ b/src/swap/Btc_Marketplace.sol @@ -30,89 +30,74 @@ contract BtcMarketPlace { // todo: should we merge buy&sell structs? They're structurally identical except for the // bitcoinaddress location. - event placeBtcSellOrderEvent( - uint indexed orderId, - uint256 amountBtc, - address buyingToken, - uint buyAmount - ); + event placeBtcSellOrderEvent(uint256 indexed orderId, uint256 amountBtc, address buyingToken, uint256 buyAmount); event acceptBtcSellOrderEvent( - uint indexed id, - uint indexed acceptId, + uint256 indexed id, + uint256 indexed acceptId, BitcoinAddress bitcoinAddress, uint256 amountBtc, uint256 ercAmount, address ercToken ); - event proofBtcSellOrderEvent(uint id); - event withdrawBtcSellOrderEvent(uint id); - event cancelAcceptedBtcSellOrderEvent(uint id); + event proofBtcSellOrderEvent(uint256 id); + event withdrawBtcSellOrderEvent(uint256 id); + event cancelAcceptedBtcSellOrderEvent(uint256 id); event placeBtcBuyOrderEvent( - uint256 amountBtc, - BitcoinAddress bitcoinAddress, - address sellingToken, - uint saleAmount + uint256 amountBtc, BitcoinAddress bitcoinAddress, address sellingToken, uint256 saleAmount ); event acceptBtcBuyOrderEvent( - uint indexed orderId, - uint indexed acceptId, - uint256 amountBtc, - uint256 ercAmount, - address ercToken + uint256 indexed orderId, uint256 indexed acceptId, uint256 amountBtc, uint256 ercAmount, address ercToken ); - event proofBtcBuyOrderEvent(uint id); - event withdrawBtcBuyOrderEvent(uint id); - event cancelAcceptedBtcBuyOrderEvent(uint id); + event proofBtcBuyOrderEvent(uint256 id); + event withdrawBtcBuyOrderEvent(uint256 id); + event cancelAcceptedBtcBuyOrderEvent(uint256 id); struct BtcSellOrder { uint256 amountBtc; address askingToken; - uint askingAmount; + uint256 askingAmount; address requester; } struct AcceptedBtcSellOrder { - uint orderId; + uint256 orderId; BitcoinAddress bitcoinAddress; uint256 amountBtc; address ercToken; - uint ercAmount; + uint256 ercAmount; address requester; address accepter; - uint acceptTime; + uint256 acceptTime; } struct BtcBuyOrder { uint256 amountBtc; BitcoinAddress bitcoinAddress; address offeringToken; - uint offeringAmount; + uint256 offeringAmount; address requester; } struct AcceptedBtcBuyOrder { - uint orderId; + uint256 orderId; uint256 amountBtc; address ercToken; - uint ercAmount; + uint256 ercAmount; address requester; address accepter; - uint acceptTime; + uint256 acceptTime; } struct BitcoinAddress { string bitcoinAddress; // todo: use the right type } + struct TransactionProof { // todo: fields here uint256 dummy; } - function placeBtcSellOrder( - uint256 amountBtc, - address buyingToken, - uint buyAmount - ) public { + function placeBtcSellOrder(uint256 amountBtc, address buyingToken, uint256 buyAmount) public { require(buyingToken != address(0x0)); require(amountBtc > 0); require(buyAmount > 0); @@ -128,28 +113,23 @@ contract BtcMarketPlace { emit placeBtcSellOrderEvent(id, amountBtc, buyingToken, buyAmount); } - function acceptBtcSellOrder( - uint id, - BitcoinAddress calldata bitcoinAddress, - uint256 amountBtc - ) public returns (uint) { + function acceptBtcSellOrder(uint256 id, BitcoinAddress calldata bitcoinAddress, uint256 amountBtc) + public + returns (uint256) + { BtcSellOrder storage order = btcSellOrders[id]; require(amountBtc > 0); require(amountBtc <= order.amountBtc); - uint sellAmount = (amountBtc * order.askingAmount) / order.amountBtc; + uint256 sellAmount = (amountBtc * order.askingAmount) / order.amountBtc; assert(sellAmount > 0); assert(order.askingAmount >= sellAmount); order.askingAmount -= sellAmount; order.amountBtc -= amountBtc; // "lock" selling token by transferring to contract - IERC20(order.askingToken).safeTransferFrom( - msg.sender, - address(this), - sellAmount - ); + IERC20(order.askingToken).safeTransferFrom(msg.sender, address(this), sellAmount); uint256 acceptId = nextOrderId++; acceptedBtcSellOrders[acceptId] = AcceptedBtcSellOrder({ @@ -163,39 +143,27 @@ contract BtcMarketPlace { acceptTime: block.timestamp }); - emit acceptBtcSellOrderEvent( - id, - acceptId, - bitcoinAddress, - amountBtc, - sellAmount, - order.askingToken - ); + emit acceptBtcSellOrderEvent(id, acceptId, bitcoinAddress, amountBtc, sellAmount, order.askingToken); return acceptId; } - function proofBtcSellOrder( - uint id, - BitcoinTx.Info calldata transaction, - BitcoinTx.Proof calldata proof - ) public { + function proofBtcSellOrder(uint256 id, BitcoinTx.Info calldata transaction, BitcoinTx.Proof calldata proof) + public + { AcceptedBtcSellOrder storage accept = acceptedBtcSellOrders[id]; require(accept.requester == msg.sender); relay.validateProof(transaction, proof); - IERC20(accept.ercToken).safeTransfer( - accept.requester, - accept.ercAmount - ); + IERC20(accept.ercToken).safeTransfer(accept.requester, accept.ercAmount); delete acceptedBtcSellOrders[id]; emit proofBtcSellOrderEvent(id); } - function withdrawBtcSellOrder(uint id) public { + function withdrawBtcSellOrder(uint256 id) public { BtcSellOrder storage order = btcSellOrders[id]; require(order.requester == msg.sender); @@ -205,12 +173,10 @@ contract BtcMarketPlace { emit withdrawBtcSellOrderEvent(id); } - function cancelAcceptedBtcSellOrder(uint id) public { + function cancelAcceptedBtcSellOrder(uint256 id) public { AcceptedBtcSellOrder storage order = acceptedBtcSellOrders[id]; - require( - block.timestamp > order.acceptTime + REQUEST_EXPIRATION_SECONDS - ); + require(block.timestamp > order.acceptTime + REQUEST_EXPIRATION_SECONDS); require(order.accepter == msg.sender); // give accepter its tokens back @@ -225,16 +191,12 @@ contract BtcMarketPlace { uint256 amountBtc, BitcoinAddress calldata bitcoinAddress, address sellingToken, - uint saleAmount + uint256 saleAmount ) public { require(sellingToken != address(0x0)); // "lock" selling token by transferring to contract - IERC20(sellingToken).safeTransferFrom( - msg.sender, - address(this), - saleAmount - ); + IERC20(sellingToken).safeTransferFrom(msg.sender, address(this), saleAmount); uint256 id = nextOrderId++; btcBuyOrders[id] = BtcBuyOrder({ @@ -245,25 +207,17 @@ contract BtcMarketPlace { requester: msg.sender }); - emit placeBtcBuyOrderEvent( - amountBtc, - bitcoinAddress, - sellingToken, - saleAmount - ); + emit placeBtcBuyOrderEvent(amountBtc, bitcoinAddress, sellingToken, saleAmount); } - function acceptBtcBuyOrder( - uint id, - uint256 amountBtc - ) public returns (uint) { + function acceptBtcBuyOrder(uint256 id, uint256 amountBtc) public returns (uint256) { BtcBuyOrder storage order = btcBuyOrders[id]; require(amountBtc <= order.amountBtc); require(amountBtc > 0); // todo: make safe - uint buyAmount = (amountBtc * order.offeringAmount) / order.amountBtc; + uint256 buyAmount = (amountBtc * order.offeringAmount) / order.amountBtc; assert(buyAmount > 0); @@ -281,26 +235,16 @@ contract BtcMarketPlace { acceptTime: block.timestamp }); - uint acceptId = nextOrderId++; + uint256 acceptId = nextOrderId++; acceptedBtcBuyOrders[acceptId] = accept; - emit acceptBtcBuyOrderEvent( - id, - acceptId, - amountBtc, - buyAmount, - order.offeringToken - ); + emit acceptBtcBuyOrderEvent(id, acceptId, amountBtc, buyAmount, order.offeringToken); return acceptId; } - function proofBtcBuyOrder( - uint id, - BitcoinTx.Info calldata transaction, - BitcoinTx.Proof calldata proof - ) public { + function proofBtcBuyOrder(uint256 id, BitcoinTx.Info calldata transaction, BitcoinTx.Proof calldata proof) public { AcceptedBtcBuyOrder storage accept = acceptedBtcBuyOrders[id]; require(accept.accepter == msg.sender); @@ -314,30 +258,25 @@ contract BtcMarketPlace { emit proofBtcBuyOrderEvent(id); } - function withdrawBtcBuyOrder(uint id) public { + function withdrawBtcBuyOrder(uint256 id) public { BtcBuyOrder storage order = btcBuyOrders[id]; require(order.requester == msg.sender); // release the locked erc20s - IERC20(order.offeringToken).safeTransfer( - msg.sender, - order.offeringAmount - ); + IERC20(order.offeringToken).safeTransfer(msg.sender, order.offeringAmount); delete btcBuyOrders[id]; emit withdrawBtcBuyOrderEvent(id); } - function cancelAcceptedBtcBuyOrder(uint id) public { + function cancelAcceptedBtcBuyOrder(uint256 id) public { AcceptedBtcBuyOrder storage accept = acceptedBtcBuyOrders[id]; require(accept.requester == msg.sender); - require( - block.timestamp > accept.acceptTime + REQUEST_EXPIRATION_SECONDS - ); + require(block.timestamp > accept.acceptTime + REQUEST_EXPIRATION_SECONDS); // release the locked erc20s IERC20(accept.ercToken).safeTransfer(msg.sender, accept.ercAmount); @@ -350,22 +289,18 @@ contract BtcMarketPlace { emit cancelAcceptedBtcBuyOrderEvent(id); } - function getOpenBtcSellOrders() - external - view - returns (BtcSellOrder[] memory, uint[] memory) - { - uint numOpenOrders = 0; - for (uint i = 0; i < nextOrderId; i++) { + function getOpenBtcSellOrders() external view returns (BtcSellOrder[] memory, uint256[] memory) { + uint256 numOpenOrders = 0; + for (uint256 i = 0; i < nextOrderId; i++) { if (btcSellOrders[i].requester != address(0x0)) { numOpenOrders++; } } BtcSellOrder[] memory ret = new BtcSellOrder[](numOpenOrders); - uint[] memory identifiers = new uint[](numOpenOrders); - uint numPushed = 0; - for (uint i = 0; i < nextOrderId; i++) { + uint256[] memory identifiers = new uint[](numOpenOrders); + uint256 numPushed = 0; + for (uint256 i = 0; i < nextOrderId; i++) { if (btcSellOrders[i].requester != address(0x0)) { ret[numPushed] = btcSellOrders[i]; identifiers[numPushed] = i; @@ -375,13 +310,9 @@ contract BtcMarketPlace { return (ret, identifiers); } - function getOpenAcceptedBtcSellOrders() - external - view - returns (AcceptedBtcSellOrder[] memory, uint[] memory) - { - uint numOpenOrders = 0; - for (uint i = 0; i < nextOrderId; i++) { + function getOpenAcceptedBtcSellOrders() external view returns (AcceptedBtcSellOrder[] memory, uint256[] memory) { + uint256 numOpenOrders = 0; + for (uint256 i = 0; i < nextOrderId; i++) { if (acceptedBtcSellOrders[i].amountBtc > 0) { numOpenOrders++; } @@ -390,9 +321,9 @@ contract BtcMarketPlace { AcceptedBtcSellOrder[] memory ret = new AcceptedBtcSellOrder[]( numOpenOrders ); - uint[] memory identifiers = new uint[](numOpenOrders); - uint numPushed = 0; - for (uint i = 0; i < nextOrderId; i++) { + uint256[] memory identifiers = new uint[](numOpenOrders); + uint256 numPushed = 0; + for (uint256 i = 0; i < nextOrderId; i++) { if (acceptedBtcSellOrders[i].amountBtc > 0) { ret[numPushed] = acceptedBtcSellOrders[i]; identifiers[numPushed] = i; @@ -402,22 +333,18 @@ contract BtcMarketPlace { return (ret, identifiers); } - function getOpenBtcBuyOrders() - external - view - returns (BtcBuyOrder[] memory, uint[] memory) - { - uint numOpenOrders = 0; - for (uint i = 0; i < nextOrderId; i++) { + function getOpenBtcBuyOrders() external view returns (BtcBuyOrder[] memory, uint256[] memory) { + uint256 numOpenOrders = 0; + for (uint256 i = 0; i < nextOrderId; i++) { if (btcBuyOrders[i].requester != address(0x0)) { numOpenOrders++; } } BtcBuyOrder[] memory ret = new BtcBuyOrder[](numOpenOrders); - uint[] memory identifiers = new uint[](numOpenOrders); - uint numPushed = 0; - for (uint i = 0; i < nextOrderId; i++) { + uint256[] memory identifiers = new uint[](numOpenOrders); + uint256 numPushed = 0; + for (uint256 i = 0; i < nextOrderId; i++) { if (btcBuyOrders[i].requester != address(0x0)) { ret[numPushed] = btcBuyOrders[i]; identifiers[numPushed] = i; @@ -427,13 +354,9 @@ contract BtcMarketPlace { return (ret, identifiers); } - function getOpenAcceptedBtcBuyOrders() - external - view - returns (AcceptedBtcBuyOrder[] memory, uint[] memory) - { - uint numOpenOrders = 0; - for (uint i = 0; i < nextOrderId; i++) { + function getOpenAcceptedBtcBuyOrders() external view returns (AcceptedBtcBuyOrder[] memory, uint256[] memory) { + uint256 numOpenOrders = 0; + for (uint256 i = 0; i < nextOrderId; i++) { if (acceptedBtcBuyOrders[i].amountBtc > 0) { numOpenOrders++; } @@ -442,9 +365,9 @@ contract BtcMarketPlace { AcceptedBtcBuyOrder[] memory ret = new AcceptedBtcBuyOrder[]( numOpenOrders ); - uint[] memory identifiers = new uint[](numOpenOrders); - uint numPushed = 0; - for (uint i = 0; i < nextOrderId; i++) { + uint256[] memory identifiers = new uint[](numOpenOrders); + uint256 numPushed = 0; + for (uint256 i = 0; i < nextOrderId; i++) { if (acceptedBtcBuyOrders[i].amountBtc > 0) { ret[numPushed] = acceptedBtcBuyOrders[i]; identifiers[numPushed] = i; diff --git a/test/BitcoinTx.t.sol b/test/BitcoinTx.t.sol index 736e544d5..800887c27 100644 --- a/test/BitcoinTx.t.sol +++ b/test/BitcoinTx.t.sol @@ -23,14 +23,8 @@ contract BitcoinTxTest is Test { locktime: hex"00000000" }); - bytes32 txHash = abi - .encodePacked( - txInfo.version, - txInfo.inputVector, - txInfo.outputVector, - txInfo.locktime - ) - .hash256View(); + bytes32 txHash = + abi.encodePacked(txInfo.version, txInfo.inputVector, txInfo.outputVector, txInfo.locktime).hash256View(); console2.logBytes32(txHash); assertEq(txId, txHash); @@ -40,14 +34,11 @@ contract BitcoinTxTest is Test { bytes32 txId = hex"3557632d64a45dd8ae2abc4e6d5d1a977e98db24c6a0ae186c265ed972011bb6"; bytes32 merkleRoot = hex"01c6691023f17fd78f2dffc85d9db21b84eb6e77352f494f9437168820dbfb90"; - bytes memory merkleProof = hex"ace8423f874c95f5f9042d7cda6b9f0727251f3059ef827f373a56831cc621a371db6dfce8daed1d809275e0862441b3cdfd314eceea5a79ee7aeec69cc70f614082c8b474ccf00906a1e61694fdf0b717790ac3bdf850b36afb8df107aca93b96e7dea43442a944a6ab4f8bed0d25d3d372a836a6042375bc57fee5c5425f67a3920a489b23f9133fc84d7987d990acc7c2569a81b547a5f65385856d90100e54ec14dd40c23c3cf1e61a2a16a03aea0e85d236942ad538262528d6748d20dc6ca7c40d75ba7b782bc3d1302633c6def1531573c6420b99840ecffc0125f8e0f12ec4aa1d74fd5ec8d9a57c154267cb6ff0276835592cb8500d8c3c5650e84b83e73e9094de0c2bdaa4d661a3b1adacfae0f3c0f8007ab1b2be8dbf32f073068979a263152d6c234ad0f4b70f697168502d62ead0c0194bcf77321a85a1e127afc4477dcc3c3636a7818601d9ff43f837b15ef74d387c688fc0a45b79aec0b6"; - uint txIndexInBlock = 407; + bytes memory merkleProof = + hex"ace8423f874c95f5f9042d7cda6b9f0727251f3059ef827f373a56831cc621a371db6dfce8daed1d809275e0862441b3cdfd314eceea5a79ee7aeec69cc70f614082c8b474ccf00906a1e61694fdf0b717790ac3bdf850b36afb8df107aca93b96e7dea43442a944a6ab4f8bed0d25d3d372a836a6042375bc57fee5c5425f67a3920a489b23f9133fc84d7987d990acc7c2569a81b547a5f65385856d90100e54ec14dd40c23c3cf1e61a2a16a03aea0e85d236942ad538262528d6748d20dc6ca7c40d75ba7b782bc3d1302633c6def1531573c6420b99840ecffc0125f8e0f12ec4aa1d74fd5ec8d9a57c154267cb6ff0276835592cb8500d8c3c5650e84b83e73e9094de0c2bdaa4d661a3b1adacfae0f3c0f8007ab1b2be8dbf32f073068979a263152d6c234ad0f4b70f697168502d62ead0c0194bcf77321a85a1e127afc4477dcc3c3636a7818601d9ff43f837b15ef74d387c688fc0a45b79aec0b6"; + uint256 txIndexInBlock = 407; require( - txId.prove( - merkleRoot, - merkleProof, - txIndexInBlock - ), + txId.prove(merkleRoot, merkleProof, txIndexInBlock), "Tx merkle proof is not valid for provided header and tx hash" ); } diff --git a/test/WitnessTx.t.sol b/test/WitnessTx.t.sol index 10b9679c4..818fcf58e 100644 --- a/test/WitnessTx.t.sol +++ b/test/WitnessTx.t.sol @@ -26,17 +26,15 @@ contract WitnessTxTest is Test { witnessVector: hex"03406c00eb3c4d35fedd257051333b4ca81d1a25a37a9af4891f1fec2869edd56b14180eafbda8851d63138a724c9b15384bc5f0536de658bd294d426a36212e6f08a5209e2849b90a2353691fccedd467215c88eec89a5d0dcf468e6cf37abed344d746ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38004c5e7b200a20202270223a20226272632d3230222c0a2020226f70223a20226465706c6f79222c0a2020227469636b223a20226f726469222c0a2020226d6178223a20223231303030303030222c0a2020226c696d223a202231303030220a7d6821c19e2849b90a2353691fccedd467215c88eec89a5d0dcf468e6cf37abed344d746" }); - bytes32 wTxHash = 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 wTxHash = abi.encodePacked( + txInfo.info.version, + WitnessTx.SEGWIT_MARKER, + WitnessTx.SEGWIT_FLAG, + txInfo.info.inputVector, + txInfo.info.outputVector, + txInfo.witnessVector, + txInfo.info.locktime + ).hash256View(); console2.logBytes32(wTxHash); assertEq(wTxId, wTxHash); @@ -46,15 +44,12 @@ contract WitnessTxTest is Test { bytes32 txId = hex"6b374690d8e2dbca4187f443cddd293536400d431f43a643b263ce59c4f9a3eb"; bytes32 witnessMerkleRoot = hex"7cee5e99c8f0fc25fb115b7d7d00befca61f59a8544adaf3980f52132baf61ae"; - bytes memory witnessMerkleProof = hex"6034ddf453f5dd20de449b29b1221dede67ccae56f00528e0767e2ab506db31c4d2946e88f7efa3e94bb17bbd10f3f44172b59c48f2eb6bd7f67a88d149373ee4082c8b474ccf00906a1e61694fdf0b717790ac3bdf850b36afb8df107aca93b7c3c4f91ddf49c7f74244336c5833377d40760ae09dd1fba83063ace480f94cca3920a489b23f9133fc84d7987d990acc7c2569a81b547a5f65385856d90100e84878b4f305a3909a9420293cdc741109864c9338ea326449a7a303b227f2b10490bc4343355e1a391f51c42918a894c2980012cca5ffd4b56a6702abd98497802de83f5889b2ad5bd157762a58505948f32f42b9fa886c93bf30fef6144a64666843a28ef13184f9e7ac3c34b5741f58c8895a0167f496e0157e7d0a97f4041f97b8df4d8aee81d20d0d062ed3ee0f9b0afb196bdf5373712883cacdfd8349b739c0e6e41d650d05727ea5faec197bfa563d19b0150fba718ba1981aea9ef90"; - - uint txIndexInBlock = 407; + bytes memory witnessMerkleProof = + hex"6034ddf453f5dd20de449b29b1221dede67ccae56f00528e0767e2ab506db31c4d2946e88f7efa3e94bb17bbd10f3f44172b59c48f2eb6bd7f67a88d149373ee4082c8b474ccf00906a1e61694fdf0b717790ac3bdf850b36afb8df107aca93b7c3c4f91ddf49c7f74244336c5833377d40760ae09dd1fba83063ace480f94cca3920a489b23f9133fc84d7987d990acc7c2569a81b547a5f65385856d90100e84878b4f305a3909a9420293cdc741109864c9338ea326449a7a303b227f2b10490bc4343355e1a391f51c42918a894c2980012cca5ffd4b56a6702abd98497802de83f5889b2ad5bd157762a58505948f32f42b9fa886c93bf30fef6144a64666843a28ef13184f9e7ac3c34b5741f58c8895a0167f496e0157e7d0a97f4041f97b8df4d8aee81d20d0d062ed3ee0f9b0afb196bdf5373712883cacdfd8349b739c0e6e41d650d05727ea5faec197bfa563d19b0150fba718ba1981aea9ef90"; + + uint256 txIndexInBlock = 407; require( - txId.prove( - witnessMerkleRoot, - witnessMerkleProof, - txIndexInBlock - ), + txId.prove(witnessMerkleRoot, witnessMerkleProof, txIndexInBlock), "Tx witness merkle proof is not valid for provided header and tx hash" ); @@ -84,7 +79,7 @@ contract WitnessTxTest is Test { bitcoinHeaders: hex"00805d2b0952221f3dcd1e4f9053ea87b238327d99813a8879f00300000000000000000001c6691023f17fd78f2dffc85d9db21b84eb6e77352f494f9437168820dbfb901f0c0864a38906173a54d852", merkleProof: hex"44b53cd654829638a7e1e4681b5d05c2049d1e5f936d159e6c4fca704d3c035127b84e765049e0211073bb8c8aa3df37266cc299a196e78b1a6883c82d7f7f0df32ba0fe04ea9e33eb2bbb1c4fffa4cd02a1af9e4d5f634530704f10dc88bf900eed8b1fb030e0c49316facf0ba1d98fb8979ed899d045192419801453448eaff73bc4f56d8ab7eb379732e4db73a33f2ad9b2ca35aba5fd45ea76aa843edac88292b730b6c07a4396fcb2995e41db66f176f5de25a18f52aeb7c27706c6c7288300c854a83dbb34ae626c3e8a127367c12150c0eac8ee7c436dbc1e0673ba3c7e4a4a552cc6df73919d28e5144e2b8f03a8b53a59fba1136f7f3bb278b1e1c1f9c380b9a9b0278d88b10289a8d14aca3152883256923cf163fd20ac13dfcb858979a263152d6c234ad0f4b70f697168502d62ead0c0194bcf77321a85a1e127afc4477dcc3c3636a7818601d9ff43f837b15ef74d387c688fc0a45b79aec0b6", txIndexInBlock: 0 - }), + }), paymentProof: BitcoinTx.Proof({ // we don't pass the block headers here since we need the merkle root // calculated using the witness tx ids, only the coinbase headers are needed