diff --git a/docs/docs/contracts/src/src/utils/BitcoinTx.sol/library.BitcoinTx.md b/docs/docs/contracts/src/src/utils/BitcoinTx.sol/library.BitcoinTx.md index d0bf5149..ffb574ab 100644 --- a/docs/docs/contracts/src/src/utils/BitcoinTx.sol/library.BitcoinTx.md +++ b/docs/docs/contracts/src/src/utils/BitcoinTx.sol/library.BitcoinTx.md @@ -58,7 +58,7 @@ function evaluateProofDifficulty(IRelay relay, uint256 txProofDifficultyFactor, ### processTxOutputs -Processes the Bitcoin transaction output vector. +Processes all outputs from the transaction. ```solidity @@ -74,39 +74,19 @@ function processTxOutputs(bytes memory txOutputVector, bytes32 scriptPubKeyHash) |`txOutputVector`|`bytes`|Bitcoin transaction output vector. This function assumes vector's structure is valid so it must be validated using e.g. `BTCUtils.validateVout` function before it is passed here.| |`scriptPubKeyHash`|`bytes32`|Expected Bitcoin scriptPubKey keccak256 hash.| -**Returns** - -|Name|Type|Description| -|----|----|-----------| -|`resultInfo`|`TxOutputsInfo`|Outcomes of the processing.| - - -### processTxOutputs -Processes all outputs from the transaction. +### extractEvmAddressFromOutput ```solidity -function processTxOutputs( - bytes memory txOutputVector, - bytes32 scriptPubKeyHash, - TxOutputsProcessingInfo memory processInfo -) internal pure returns (TxOutputsInfo memory resultInfo); +function extractEvmAddressFromOutput(bytes memory _output, uint256 _at) internal pure returns (address evmAddress); ``` -**Parameters** - -|Name|Type|Description| -|----|----|-----------| -|`txOutputVector`|`bytes`|Bitcoin transaction output vector. This function assumes vector's structure is valid so it must be validated using e.g. `BTCUtils.validateVout` function before it is passed here.| -|`scriptPubKeyHash`|`bytes32`|Expected Bitcoin scriptPubKey keccak256 hash.| -|`processInfo`|`TxOutputsProcessingInfo`|TxOutputsProcessingInfo identifying output starting index and the number of outputs.| - -### extractEvmAddressFromOutput +### extractHashFromOutput ```solidity -function extractEvmAddressFromOutput(bytes memory _output, uint256 _at) internal pure returns (address evmAddress); +function extractHashFromOutput(bytes memory _output, uint256 _at) internal pure returns (bytes32 outputHash); ``` ### reverseEndianness @@ -185,6 +165,7 @@ outputs processing. struct TxOutputsInfo { uint64 value; address evmAddress; + bytes32 hash; } ``` diff --git a/src/utils/BitcoinTx.sol b/src/utils/BitcoinTx.sol index 796374de..f6cd5e7b 100644 --- a/src/utils/BitcoinTx.sol +++ b/src/utils/BitcoinTx.sol @@ -220,23 +220,26 @@ library BitcoinTx { uint64 value; // Optional EVM address specified in OP_RETURN. address evmAddress; + // Optional hash specified in OP_RETURN. + bytes32 hash; } - /// @notice Processes the Bitcoin transaction output vector. - /// @param txOutputVector Bitcoin transaction output vector. - /// This function assumes vector's structure is valid so it - /// must be validated using e.g. `BTCUtils.validateVout` function - /// before it is passed here. + /// @notice Processes all outputs from the transaction. + /// @param txOutputVector Bitcoin transaction output vector. This function + /// assumes vector's structure is valid so it must be validated using + /// e.g. `BTCUtils.validateVout` function before it is passed here. /// @param scriptPubKeyHash Expected Bitcoin scriptPubKey keccak256 hash. - /// @return resultInfo Outcomes of the processing. function processTxOutputs(bytes memory txOutputVector, bytes32 scriptPubKeyHash) internal pure returns (TxOutputsInfo memory resultInfo) { + // needed to avoid stack too deep errors + TxOutputsProcessingInfo memory processInfo; + // 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(); + (processInfo.outputStartingIndex, processInfo.outputsCount) = txOutputVector.parseVarInt(); // To determine the first output starting index, we must jump over // the compactSize uint which prepends the output vector. One byte @@ -253,25 +256,8 @@ library BitcoinTx { // // Please refer `BTCUtils` library and compactSize uint // docs in `BitcoinTx` library for more details. - uint256 outputStartingIndex = 1 + outputsCompactSizeUintLength; + processInfo.outputStartingIndex++; - return processTxOutputs( - txOutputVector, scriptPubKeyHash, TxOutputsProcessingInfo(outputStartingIndex, outputsCount) - ); - } - - /// @notice Processes all outputs from the transaction. - /// @param txOutputVector Bitcoin transaction output vector. This function - /// assumes vector's structure is valid so it must be validated using - /// e.g. `BTCUtils.validateVout` function before it is passed here. - /// @param scriptPubKeyHash Expected Bitcoin scriptPubKey keccak256 hash. - /// @param processInfo TxOutputsProcessingInfo identifying output - /// starting index and the number of outputs. - function processTxOutputs( - bytes memory txOutputVector, - bytes32 scriptPubKeyHash, - TxOutputsProcessingInfo memory processInfo - ) internal pure returns (TxOutputsInfo memory resultInfo) { // Helper flag indicating whether there was at least one // output present bool outputPresent = false; @@ -311,6 +297,12 @@ library BitcoinTx { // NOTE: this will overwrite if there are multiple OP_RETURN outputs resultInfo.evmAddress = outputEvmAddress; } + + bytes32 outputHash = extractHashFromOutput(txOutputVector, processInfo.outputStartingIndex); + if (outputHash != 0) { + // NOTE: this will overwrite if there are multiple OP_RETURN outputs + resultInfo.hash = outputHash; + } } // Make the `outputStartingIndex` pointing to the next output by @@ -339,6 +331,20 @@ library BitcoinTx { } } + function extractHashFromOutput(bytes memory _output, uint256 _at) internal pure returns (bytes32 outputHash) { + // OP_RETURN + if (_output[_at + 9] != hex"6a") { + return 0; + } + bytes1 _dataLen = _output[_at + 10]; + if (uint256(uint8(_dataLen)) == 32) { + uint256 opReturnStart = _at + 11; + assembly { + outputHash := mload(add(_output, add(opReturnStart, 32))) + } + } + } + function reverseEndianness(bytes32 b) internal pure returns (bytes32 txHash) { bytes memory newValue = new bytes(b.length); for (uint256 i = 0; i < b.length; i++) { diff --git a/test/BitcoinTx.t.sol b/test/BitcoinTx.t.sol index f39be877..1321b4f5 100644 --- a/test/BitcoinTx.t.sol +++ b/test/BitcoinTx.t.sol @@ -156,7 +156,7 @@ contract BitcoinTxTest is Test { assertFalse(success); } - function test_ProcessTxOutputsWithOpReturn() public { + function test_ProcessTxOutputsWithOpReturnAddress() public { BitcoinTx.TxOutputsInfo memory resultInfo = BitcoinTx.processTxOutputs( hex"02983a000000000000146142b39c0073672dc382b89a42b29e06368bcabd0000000000000000166a14675ca18a04027fd50c88ccd03939e0e5c97b795f", keccak256(hex"146142b39c0073672dc382b89a42b29e06368bcabd") @@ -164,4 +164,13 @@ contract BitcoinTxTest is Test { assertEq(resultInfo.value, 15000); assertEq(resultInfo.evmAddress, 0x675Ca18A04027fd50C88CcD03939E0e5C97b795f); } + + function test_ProcessTxOutputsWithOpReturnBytes32() public { + BitcoinTx.TxOutputsInfo memory resultInfo = BitcoinTx.processTxOutputs( + hex"02983a000000000000146142b39c0073672dc382b89a42b29e06368bcabd0000000000000000166a2000112233445566778899001122334455667788990011", + keccak256(hex"146142b39c0073672dc382b89a42b29e06368bcabd") + ); + assertEq(resultInfo.value, 15000); + assertEq(resultInfo.hash, hex"00112233445566778899001122334455667788990011"); + } }