Skip to content

Commit

Permalink
Merge pull request #43 from rsksmart/QA-Test
Browse files Browse the repository at this point in the history
Qa test -> Stable-Test
  • Loading branch information
Luisfc68 authored Nov 13, 2024
2 parents 5789dcf + 3d27d24 commit 6585183
Show file tree
Hide file tree
Showing 12 changed files with 570 additions and 139 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1

- name: Use Node.js 20.12.0
uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
- name: 'Dependency Review'
uses: actions/dependency-review-action@72eb03d02c7872a771aacd928f3123ac62ad6d3a # v4.3.3
8 changes: 4 additions & 4 deletions .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,25 @@ jobs:

steps:
- name: "Checkout code"
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
with:
persist-credentials: false

- name: "Run analysis"
uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
with:
results_file: results.sarif
results_format: sarif
publish_results: true

- name: "Upload artifact"
uses: actions/upload-artifact@97a0fba1372883ab732affbe8f94b823f91727db # v3.pre.node20
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
with:
name: SARIF file
path: results.sarif
retention-days: 5

- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9
uses: github/codeql-action/upload-sarif@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9
with:
sarif_file: results.sarif
4 changes: 2 additions & 2 deletions .github/workflows/slither.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
permissions:
security-events: write
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1

- name: Run Slither
uses: crytic/slither-action@6ef3a33e56de4e8f59488cf60858b5c1bf4967c0 # v0.3.0
Expand All @@ -21,6 +21,6 @@ jobs:
target: contracts/

- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@85b07cf1e13dd512be7c27c37a33c5864c252fcc # v2
uses: github/codeql-action/upload-sarif@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9
with:
sarif_file: ${{ steps.slither.outputs.sarif }}
149 changes: 138 additions & 11 deletions contracts/BtcUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,26 @@ library BtcUtils {
uint8 private constant MAX_COMPACT_SIZE_LENGTH = 252;
uint8 private constant MAX_BYTES_USED_FOR_COMPACT_SIZE = 8;

uint private constant HASH160_SIZE = 20;
uint private constant SHA256_SIZE = 32;
uint private constant TAPROOT_PUBKEY_SIZE = 32;

uint8 private constant OUTPOINT_SIZE = 36;
uint8 private constant OUTPUT_VALUE_SIZE = 8;

uint8 private constant PUBKEY_HASH_SIZE = 20;
uint8 private constant PUBKEY_HASH_START = 3;
bytes1 private constant PUBKEY_HASH_MAINNET_BYTE = 0x00;
bytes1 private constant PUBKEY_HASH_TESTNET_BYTE = 0x6f;

uint8 private constant SCRIPT_HASH_SIZE = 20;
uint8 private constant SCRIPT_HASH_START = 2;
bytes1 private constant SCRIPT_HASH_MAINNET_BYTE = 0x05;
bytes1 private constant SCRIPT_HASH_TESTNET_BYTE = 0xc4;

uint private constant BECH32_WORD_SIZE = 5;
uint private constant BYTE_SIZE = 8;

bytes1 private constant WITNESS_VERSION_0 = 0x00;
bytes1 private constant WITNESS_VERSION_1 = 0x01;


/**
Expand All @@ -39,6 +46,10 @@ library BtcUtils {
uint256 totalSize;
}

function version() external pure returns (string memory) {
return "0.2.1";
}

/// @notice Parse a raw transaction to get an array of its outputs in a structured representation
/// @param rawTx the raw transaction
/// @return An array of `TxRawOutput` with the outputs of the transaction
Expand Down Expand Up @@ -103,18 +114,26 @@ library BtcUtils {
if (isP2SHOutput(outputScript)) {
return parsePayToScriptHash(outputScript, mainnet);
}
// TODO add here P2WPKH, P2WSH and P2TR
if (isP2WPKHOutput(outputScript)) {
return parsePayToWitnessPubKeyHash(outputScript);
}
if (isP2WSHOutput(outputScript)) {
return parsePayToWitnessScriptHash(outputScript);
}
if (isP2TROutput(outputScript)) {
return parsePayToTaproot(outputScript);
}
revert("Unsupported script type");
}

/// @notice Check if a raw output script is a pay-to-public-key-hash output
/// @param pkScript the fragment of the raw transaction containing the raw output script
/// @return Whether the script has a pay-to-public-key-hash output structure or not
function isP2PKHOutput(bytes memory pkScript) public pure returns (bool) {
return pkScript.length == 25 &&
return pkScript.length == 5 + HASH160_SIZE &&
pkScript[0] == OpCodes.OP_DUP &&
pkScript[1] == OpCodes.OP_HASH160 &&
uint8(pkScript[2]) == PUBKEY_HASH_SIZE &&
uint8(pkScript[2]) == HASH160_SIZE &&
pkScript[23] == OpCodes.OP_EQUALVERIFY &&
pkScript[24] == OpCodes.OP_CHECKSIG;
}
Expand All @@ -123,12 +142,40 @@ library BtcUtils {
/// @param pkScript the fragment of the raw transaction containing the raw output script
/// @return Whether the script has a pay-to-script-hash output structure or not
function isP2SHOutput(bytes memory pkScript) public pure returns (bool) {
return pkScript.length == 23 &&
return pkScript.length == 3 + HASH160_SIZE &&
pkScript[0] == OpCodes.OP_HASH160 &&
uint8(pkScript[1]) == SCRIPT_HASH_SIZE &&
uint8(pkScript[1]) == HASH160_SIZE &&
pkScript[22] == OpCodes.OP_EQUAL;
}

/// @notice Check if a raw output script is a pay-to-witness-pubkey-hash output
/// @param pkScript the fragment of the raw transaction containing the raw output script
/// @return Whether the script has a pay-to-witness-pubkey-hash output structure or not
function isP2WPKHOutput(bytes memory pkScript) public pure returns (bool) {
return pkScript.length == 2 + HASH160_SIZE &&
pkScript[0] == OpCodes.OP_0 &&
uint8(pkScript[1]) == HASH160_SIZE;
}

/// @notice Check if a raw output script is a pay-to-witness-script-hash output
/// @param pkScript the fragment of the raw transaction containing the raw output script
/// @return Whether the script has a pay-to-witness-script-hash output structure or not
function isP2WSHOutput(bytes memory pkScript) public pure returns (bool) {
return pkScript.length == 2 + SHA256_SIZE &&
pkScript[0] == OpCodes.OP_0 &&
uint8(pkScript[1]) == SHA256_SIZE;
}

/// @notice Check if a raw output script is a pay-to-taproot output
/// @notice Reference for implementation: https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki
/// @param pkScript the fragment of the raw transaction containing the raw output script
/// @return Whether the script has a pay-to-taproot output structure or not
function isP2TROutput(bytes memory pkScript) public pure returns (bool) {
return pkScript.length == 2 + TAPROOT_PUBKEY_SIZE &&
pkScript[0] == OpCodes.OP_1 &&
uint8(pkScript[1]) == TAPROOT_PUBKEY_SIZE;
}

/// @notice Parse a raw pay-to-public-key-hash output script to get the corresponding address,
/// the resulting byte array doesn't include the checksum bytes of the base58check encoding at
/// the end
Expand All @@ -138,8 +185,8 @@ library BtcUtils {
function parsePayToPubKeyHash(bytes calldata outputScript, bool mainnet) public pure returns (bytes memory) {
require(isP2PKHOutput(outputScript), "Script hasn't the required structure");

bytes memory destinationAddress = new bytes(PUBKEY_HASH_SIZE + 1);
for(uint8 i = PUBKEY_HASH_START; i < PUBKEY_HASH_SIZE + PUBKEY_HASH_START; i++) {
bytes memory destinationAddress = new bytes(HASH160_SIZE + 1);
for(uint8 i = PUBKEY_HASH_START; i < HASH160_SIZE + PUBKEY_HASH_START; i++) {
destinationAddress[i - PUBKEY_HASH_START + 1] = outputScript[i];
}

Expand All @@ -156,15 +203,63 @@ library BtcUtils {
function parsePayToScriptHash(bytes calldata outputScript, bool mainnet) public pure returns (bytes memory) {
require(isP2SHOutput(outputScript), "Script hasn't the required structure");

bytes memory destinationAddress = new bytes(SCRIPT_HASH_SIZE + 1);
for(uint8 i = SCRIPT_HASH_START; i < SCRIPT_HASH_SIZE + SCRIPT_HASH_START; i++) {
bytes memory destinationAddress = new bytes(HASH160_SIZE + 1);
for(uint8 i = SCRIPT_HASH_START; i < HASH160_SIZE + SCRIPT_HASH_START; i++) {
destinationAddress[i - SCRIPT_HASH_START + 1] = outputScript[i];
}

destinationAddress[0] = mainnet? SCRIPT_HASH_MAINNET_BYTE : SCRIPT_HASH_TESTNET_BYTE;
return destinationAddress;
}

/// @notice Parse a raw pay-to-witness-pubkey-hash output script to get the corresponding address words,
/// the resulting words are only the data part of the bech32 encoding and doesn't include the HRP
/// @param outputScript the fragment of the raw transaction containing the raw output script
/// @return The address bech32 words generated using the pubkey hash
function parsePayToWitnessPubKeyHash(bytes calldata outputScript) public pure returns (bytes memory) {
require(isP2WPKHOutput(outputScript), "Script hasn't the required structure");
uint length = 1 + total5BitWords(HASH160_SIZE);
bytes memory result = new bytes(length);
result[0] = WITNESS_VERSION_0;
bytes memory words = to5BitWords(outputScript[2:]);
for (uint i = 1; i < length; i++) {
result[i] = words[i - 1];
}
return result;
}

/// @notice Parse a raw pay-to-witness-script-hash output script to get the corresponding address words,
/// the resulting words are only the data part of the bech32 encoding and doesn't include the HRP
/// @param outputScript the fragment of the raw transaction containing the raw output script
/// @return The address bech32 words generated using the script hash
function parsePayToWitnessScriptHash(bytes calldata outputScript) public pure returns (bytes memory) {
require(isP2WSHOutput(outputScript), "Script hasn't the required structure");
uint length = 1 + total5BitWords(SHA256_SIZE);
bytes memory result = new bytes(length);
result[0] = WITNESS_VERSION_0;
bytes memory words = to5BitWords(outputScript[2:]);
for (uint i = 1; i < length; i++) {
result[i] = words[i - 1];
}
return result;
}

/// @notice Parse a raw pay-to-taproot output script to get the corresponding address words,
/// the resulting words are only the data part of the bech32m encoding and doesn't include the HRP
/// @param outputScript the fragment of the raw transaction containing the raw output script
/// @return The address bech32m words generated using the taproot pubkey hash
function parsePayToTaproot(bytes calldata outputScript) public pure returns (bytes memory) {
require(isP2TROutput(outputScript), "Script hasn't the required structure");
uint length = 1 + total5BitWords(TAPROOT_PUBKEY_SIZE);
bytes memory result = new bytes(length);
result[0] = WITNESS_VERSION_1;
bytes memory words = to5BitWords(outputScript[2:]);
for (uint i = 1; i < length; i++) {
result[i] = words[i - 1];
}
return result;
}

/// @notice Parse a raw null-data output script to get its content
/// @param outputScript the fragment of the raw transaction containing the raw output script
/// @return The content embedded inside the script
Expand Down Expand Up @@ -271,4 +366,36 @@ library BtcUtils {
}
return result;
}

/// @notice Referece for implementation: https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
function to5BitWords(bytes memory byteArray) private pure returns(bytes memory) {
uint8 MAX_VALUE = 31;

uint currentValue = 0;
uint bitCount = 0;
uint8 resultIndex = 0;
bytes memory result = new bytes(total5BitWords(byteArray.length));

for (uint i = 0; i < byteArray.length; ++i) {
currentValue = (currentValue << BYTE_SIZE) | uint8(byteArray[i]);
bitCount += BYTE_SIZE;
while (bitCount >= BECH32_WORD_SIZE) {
bitCount -= BECH32_WORD_SIZE;
// this mask ensures that the result will always have 5 bits
result[resultIndex] = bytes1(uint8((currentValue >> bitCount) & MAX_VALUE));
resultIndex++;
}
}

if (bitCount > 0) {
result[resultIndex] = bytes1(uint8((currentValue << (BECH32_WORD_SIZE - bitCount)) & MAX_VALUE));
}
return result;
}

function total5BitWords(uint numberOfBytes) private pure returns(uint) {
uint total = (numberOfBytes * BYTE_SIZE) / BECH32_WORD_SIZE;
bool extraWord = (numberOfBytes * BYTE_SIZE) % BECH32_WORD_SIZE == 0;
return total + (extraWord? 0 : 1);
}
}
3 changes: 3 additions & 0 deletions contracts/OpCodes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ library OpCodes {
bytes1 public constant OP_CHECKSIG = 0xac;
bytes1 public constant OP_RETURN = 0x6a;
bytes1 public constant OP_EQUAL = 0x87;

bytes1 public constant OP_0 = 0x00;
bytes1 public constant OP_1 = 0x51;
}
Loading

0 comments on commit 6585183

Please sign in to comment.