Skip to content

Commit

Permalink
Merge pull request #298 from bob-collective/endre/hashExtract2
Browse files Browse the repository at this point in the history
feat: allow extracting hash from output
  • Loading branch information
gregdhill authored Jul 31, 2024
2 parents e85ce51 + fc56c76 commit 6f459d4
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 51 deletions.
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;
}

/// @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++;

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");
}
}

0 comments on commit 6f459d4

Please sign in to comment.