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

feat: allow extracting hash from output #298

Merged
merged 3 commits into from
Jul 31, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function evaluateProofDifficulty(IRelay relay, uint256 txProofDifficultyFactor,

### processTxOutputs

Processes the Bitcoin transaction output vector.
Processes all outputs from the transaction.


```solidity
Expand All @@ -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
Expand Down Expand Up @@ -185,6 +165,7 @@ outputs processing.
struct TxOutputsInfo {
uint64 value;
address evmAddress;
bytes32 hash;
}
```

56 changes: 31 additions & 25 deletions src/utils/BitcoinTx.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
gregdhill marked this conversation as resolved.
Show resolved Hide resolved
}

/// @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
Expand All @@ -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++;
gregdhill marked this conversation as resolved.
Show resolved Hide resolved

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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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++) {
Expand Down
11 changes: 10 additions & 1 deletion test/BitcoinTx.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,21 @@ 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")
);
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");
}
}