From 8eb1a8bbec1fa3baee83f8bf1cc7bc1f9878463c Mon Sep 17 00:00:00 2001 From: Horobchenko Andrii Date: Wed, 22 May 2024 15:03:48 +0300 Subject: [PATCH 1/2] Changed the tests run to run on push --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9282e82..9ef660d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,6 @@ name: test -on: workflow_dispatch +on: push env: FOUNDRY_PROFILE: ci From 29be480092a20e574a6da562a73be2bf01ac8927 Mon Sep 17 00:00:00 2001 From: Viacheslav Date: Wed, 22 May 2024 14:04:24 +0300 Subject: [PATCH 2/2] refactor BTCDepositAddressDeriver --- script/SetSeed.s.sol | 7 ++- src/BTCDepositAddressDeriver.sol | 80 +++++++++++++++++++++-------- test/BTCDepositAddressDeriver.t.sol | 12 ++--- 3 files changed, 68 insertions(+), 31 deletions(-) diff --git a/script/SetSeed.s.sol b/script/SetSeed.s.sol index c00a87a..177cdb0 100644 --- a/script/SetSeed.s.sol +++ b/script/SetSeed.s.sol @@ -9,23 +9,28 @@ import {BTCDepositAddressDeriver} from "../src/BTCDepositAddressDeriver.sol"; contract SetSeed is Script { function run() external { + // get contract address address contractAddress = DevOpsTools.get_most_recent_deployment( "BTCDepositAddressDeriver", block.chainid ); console.log("contractAddress", contractAddress); + // get first validators' joint pubkey string memory btcAddr1 = vm.envString("BTC_ADDR1"); console.log("BTC_ADDR1:", btcAddr1); + // get second validators' joint pubkey string memory btcAddr2 = vm.envString("BTC_ADDR2"); console.log("BTC_ADDR2:", btcAddr2); BTCDepositAddressDeriver deriver = BTCDepositAddressDeriver( contractAddress ); + + // set validators' pubkeys and network prefix vm.startBroadcast(); - deriver.setSeed(btcAddr1, btcAddr2, "bcrt"); + deriver.setSeed(btcAddr1, btcAddr2, BTCDepositAddressDeriver.BitcoinNetwork.REGTEST); vm.stopBroadcast(); } } diff --git a/src/BTCDepositAddressDeriver.sol b/src/BTCDepositAddressDeriver.sol index 75d6fac..38823ec 100644 --- a/src/BTCDepositAddressDeriver.sol +++ b/src/BTCDepositAddressDeriver.sol @@ -6,22 +6,35 @@ import {Deriver} from "./Deriver.sol"; import {Bech32m} from "./Bech32m.sol"; error SeedWasNotSetYet(); - +error UnsupportedBtcAddress(string btcAddress); error CannotParseBtcAddress( string btcAddress, string hrp, Bech32m.DecodeError err ); -error UnsupportedBtcAddress(string btcAddress); + +// Types of Bitcoin Network contract BTCDepositAddressDeriver { + + enum BitcoinNetwork { + TESTNET, + MAINNET, + REGTEST + } + event SeedChanged(string btcAddr1, string btcAddr2, string hrp); bool public wasSeedSet; + + // Validators' joint pubkeys in format of taproot addresses string public btcAddr1; string public btcAddr2; + + // Bitcoin address's network prefix string public networkHrp; + // Validators' pubkey both coordinates deconstructed from taproot addresses uint256 public p1x; uint256 public p1y; uint256 public p2x; @@ -31,21 +44,55 @@ contract BTCDepositAddressDeriver { wasSeedSet = false; } + // Set Validators' joint pubkeys and network prefix, must be called after contract deployment + function setSeed( + string calldata _btcAddr1, + string calldata _btcAddr2, + BitcoinNetwork _network + ) public virtual { + + string memory _hrp; + + if (_network == BitcoinNetwork.TESTNET) { + _hrp = 'tb'; + } else if (_network == BitcoinNetwork.MAINNET) { + _hrp = 'bc'; + } else if (_network == BitcoinNetwork.REGTEST) { + _hrp = 'brct'; + } + + networkHrp = _hrp; + + (p1x, p1y) = parseBTCTaprootAddress(_hrp, _btcAddr1); + (p2x, p2y) = parseBTCTaprootAddress(_hrp, _btcAddr2); + + btcAddr1 = _btcAddr1; + btcAddr2 = _btcAddr2; + + wasSeedSet = true; + emit SeedChanged(_btcAddr1, _btcAddr2, _hrp); + } + + // Derive pubkey's (x,y) coordinates from taproot address function parseBTCTaprootAddress( - string calldata hrp, - string calldata addr + string memory _hrp, + string calldata _bitcoinAddress ) public pure returns (uint256, uint256) { + (uint8 witVer, bytes memory witProg, Bech32m.DecodeError err) = Bech32m - .decodeSegwitAddress(bytes(hrp), bytes(addr)); + .decodeSegwitAddress(bytes(_hrp), bytes(_bitcoinAddress)); + if (err != Bech32m.DecodeError.NoError) { - revert CannotParseBtcAddress(addr, hrp, err); + revert CannotParseBtcAddress(_bitcoinAddress, _hrp, err); } if (witVer != 1 || witProg.length != 32) { - revert UnsupportedBtcAddress(addr); + revert UnsupportedBtcAddress(_bitcoinAddress); } + uint256 x = uint256(bytes32(witProg)); + if (x == 0 || x >= Deriver.PP) { - revert UnsupportedBtcAddress(addr); + revert UnsupportedBtcAddress(_bitcoinAddress); } uint256 y = Deriver.liftX(x); @@ -53,26 +100,15 @@ contract BTCDepositAddressDeriver { return (x, y); } - function setSeed( - string calldata _btcAddr1, - string calldata _btcAddr2, - string calldata _hrp - ) public virtual { - networkHrp = _hrp; - (p1x, p1y) = parseBTCTaprootAddress(_hrp, _btcAddr1); - (p2x, p2y) = parseBTCTaprootAddress(_hrp, _btcAddr2); - btcAddr1 = _btcAddr1; - btcAddr2 = _btcAddr2; - wasSeedSet = true; - emit SeedChanged(_btcAddr1, _btcAddr2, _hrp); - } - + // Get users' Bitcoin deposit address from user's Ethereum address function getBTCDepositAddress( address ethAddr ) public view returns (string memory) { + if (!wasSeedSet) { revert SeedWasNotSetYet(); } + return Deriver.getBtcAddressFromEth( p1x, diff --git a/test/BTCDepositAddressDeriver.t.sol b/test/BTCDepositAddressDeriver.t.sol index 608b4b4..a4ca5a5 100644 --- a/test/BTCDepositAddressDeriver.t.sol +++ b/test/BTCDepositAddressDeriver.t.sol @@ -6,12 +6,8 @@ import {Test} from "forge-std/Test.sol"; import {BTCDepositAddressDeriver} from "../src/BTCDepositAddressDeriver.sol"; contract BTCDepositAddressDeriverTest is Test { - BTCDepositAddressDeriver deriver; - // Due to a bug/feature of Solidity, it is impossible to import events from other contract.abi - // https://github.com/ethereum/solidity/issues/13928 - // It is copy-pasted here from src/BTCDepositAddressDeriver.sol - event SeedChanged(string btcAddr1, string btcAddr2, string hrp); + BTCDepositAddressDeriver deriver; function setUp() public { deriver = new BTCDepositAddressDeriver(); @@ -58,7 +54,7 @@ contract BTCDepositAddressDeriverTest is Test { assertEq(deriver.p2y(), 0); vm.expectEmit(address(deriver)); - emit SeedChanged( + emit BTCDepositAddressDeriver.SeedChanged( "tb1p7g532zgvuzv8fz3hs02wvn2almqh8qyvz4xdr564nannkxh28kdq62ewy3", "tb1psfpmk6v8cvd8kr4rdda0l8gwyn42v5yfjlqkhnureprgs5tuumkqvdkewz", "tb" @@ -66,7 +62,7 @@ contract BTCDepositAddressDeriverTest is Test { deriver.setSeed( "tb1p7g532zgvuzv8fz3hs02wvn2almqh8qyvz4xdr564nannkxh28kdq62ewy3", "tb1psfpmk6v8cvd8kr4rdda0l8gwyn42v5yfjlqkhnureprgs5tuumkqvdkewz", - "tb" + BTCDepositAddressDeriver.BitcoinNetwork.TESTNET ); assertEq(deriver.wasSeedSet(), true); @@ -101,7 +97,7 @@ contract BTCDepositAddressDeriverTest is Test { deriver.setSeed( "tb1p7g532zgvuzv8fz3hs02wvn2almqh8qyvz4xdr564nannkxh28kdq62ewy3", "tb1psfpmk6v8cvd8kr4rdda0l8gwyn42v5yfjlqkhnureprgs5tuumkqvdkewz", - "tb" + BTCDepositAddressDeriver.BitcoinNetwork.TESTNET ); string memory btcAddress = deriver.getBTCDepositAddress(