Skip to content

Commit

Permalink
move bitcoin utils to this repo (#28)
Browse files Browse the repository at this point in the history
* move bitcoin utils to this repo

* cleanup

---------

Co-authored-by: Viacheslav Zhygulin <[email protected]>
  • Loading branch information
szhygulin and Viacheslav Zhygulin authored Jun 27, 2024
1 parent d5ec222 commit 4c27632
Show file tree
Hide file tree
Showing 9 changed files with 763 additions and 27 deletions.
5 changes: 3 additions & 2 deletions script/SetSeed.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {Tools} from "../src/Tools.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {AddressReaderWriter} from "./AddressReaderWriter.s.sol";
import {console} from "forge-std/console.sol";
import {BitcoinNetworkEncoder} from "../src/BitcoinNetworkEncoder.sol";

contract SetSeed is Script, AddressReaderWriter {
function run() external {
Expand Down Expand Up @@ -40,9 +41,9 @@ contract SetSeed is Script, AddressReaderWriter {

// get network
uint _network = vm.envUint("BTC_NETWORK");
uint8 network = uint8(_network);
BitcoinNetworkEncoder.Network network = BitcoinNetworkEncoder.Network(_network);

console.log("BTC_NETWORK:", network);
console.log("BTC_NETWORK:", _network);

BTCDepositAddressDeriver deriver = BTCDepositAddressDeriver(
contractAddress
Expand Down
25 changes: 3 additions & 22 deletions src/BTCDepositAddressDeriver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.8.24;

import {Deriver} from "./Deriver.sol";
import {Bech32m} from "./Bech32m.sol";
import {BitcoinNetworkEncoder} from "./BitcoinNetworkEncoder.sol";
import {console} from "forge-std/console.sol";

error SeedWasNotSetYet();
Expand Down Expand Up @@ -43,9 +44,9 @@ contract BTCDepositAddressDeriver {
function setSeed(
string calldata _btcAddr1,
string calldata _btcAddr2,
uint8 _network
BitcoinNetworkEncoder.Network _network
) public virtual {
string memory _hrp = getNetworkPrefix(_network);
string memory _hrp = BitcoinNetworkEncoder.getNetworkPrefix(_network);

networkHrp = _hrp;

Expand All @@ -59,26 +60,6 @@ contract BTCDepositAddressDeriver {
emit SeedChanged(_btcAddr1, _btcAddr2, _hrp);
}

// get address prefix from network type
function getNetworkPrefix(
uint8 _network
) public pure returns (string memory) {

string memory _hrp;

if (_network == 0) {
_hrp = 'tb';
} else if (_network == 1) {
_hrp = 'bc';
} else if (_network == 2) {
_hrp = 'brct';
} else {
_hrp = 'unknown';
}

return _hrp;
}

// Derive pubkey's (x,y) coordinates from taproot address
function parseBTCTaprootAddress(
string memory _hrp,
Expand Down
158 changes: 158 additions & 0 deletions src/Base58.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.24;

bytes constant ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

/**
* @notice encode is used to encode the given bytes in base58 standard.
* @param data_ raw data, passed in as bytes.
* @return base58 encoded data_, returned as bytes.
*/
function encode(bytes memory data_) pure returns (bytes memory) {
unchecked {
uint256 size = data_.length;
uint256 zeroCount;
while (zeroCount < size && data_[zeroCount] == 0) {
zeroCount++;
}
size = zeroCount + ((size - zeroCount) * 8351) / 6115 + 1;
bytes memory slot = new bytes(size);
uint32 carry;
int256 m;
int256 high = int256(size) - 1;
for (uint256 i = 0; i < data_.length; i++) {
m = int256(size - 1);
for (carry = uint8(data_[i]); m > high || carry != 0; m--) {
carry = carry + 256 * uint8(slot[uint256(m)]);
slot[uint256(m)] = bytes1(uint8(carry % 58));
carry /= 58;
}
high = m;
}
uint256 n;
for (n = zeroCount; n < size && slot[n] == 0; n++) {}
size = slot.length - (n - zeroCount);
bytes memory out = new bytes(size);
for (uint256 i = 0; i < size; i++) {
uint256 j = i + n - zeroCount;
out[i] = ALPHABET[uint8(slot[j])];
}
return out;
}
}

/**
* @notice decode is used to decode the given string in base58 standard.
* @param data_ data encoded with base58, passed in as bytes.
* @return raw data, returned as bytes.
*/
function decode(bytes memory data_) pure returns (bytes memory) {
unchecked {
uint256 zero = 49;
uint256 b58sz = data_.length;
uint256 zcount = 0;
for (uint256 i = 0; i < b58sz && uint8(data_[i]) == zero; i++) {
zcount++;
}
uint256 t;
uint256 c;
bool f;
bytes memory binu = new bytes(2 * (((b58sz * 8351) / 6115) + 1));
uint32[] memory outi = new uint32[]((b58sz + 3) / 4);
for (uint256 i = 0; i < data_.length; i++) {
bytes1 r = data_[i];
(c, f) = indexOf(ALPHABET, r);
require(f, "invalid base58 digit");
for (int256 k = int256(outi.length) - 1; k >= 0; k--) {
t = uint64(outi[uint256(k)]) * 58 + c;
c = t >> 32;
outi[uint256(k)] = uint32(t & 0xffffffff);
}
}
uint64 mask = uint64(b58sz % 4) * 8;
if (mask == 0) {
mask = 32;
}
mask -= 8;
uint256 outLen = 0;
for (uint256 j = 0; j < outi.length; j++) {
while (mask < 32) {
binu[outLen] = bytes1(uint8(outi[j] >> mask));
outLen++;
if (mask < 8) {
break;
}
mask -= 8;
}
mask = 24;
}
for (uint256 msb = zcount; msb < binu.length; msb++) {
if (binu[msb] > 0) {
return slice(binu, msb - zcount, outLen);
}
}
return slice(binu, 0, outLen);
}
}

/**
* @notice encodeToString is used to encode the given byte in base58 standard.
* @param data_ raw data, passed in as bytes.
* @return base58 encoded data_, returned as a string.
*/
function encodeToString(bytes memory data_) pure returns (string memory) {
return string(encode(data_));
}

/**
* @notice encodeFromString is used to encode the given string in base58 standard.
* @param data_ raw data, passed in as a string.
* @return base58 encoded data_, returned as bytes.
*/
function encodeFromString(string memory data_) pure returns (bytes memory) {
return encode(bytes(data_));
}

/**
* @notice decode is used to decode the given string in base58 standard.
* @param data_ data encoded with base58, passed in as string.
* @return raw data, returned as bytes.
*/
function decodeFromString(string memory data_) pure returns (bytes memory) {
return decode(bytes(data_));
}

/**
* @notice slice is used to slice the given byte, returns the bytes in the range of [start_, end_)
* @param data_ raw data, passed in as bytes.
* @param start_ start index.
* @param end_ end index.
* @return slice data
*/
function slice(bytes memory data_, uint256 start_, uint256 end_) pure returns (bytes memory) {
unchecked {
bytes memory ret = new bytes(end_ - start_);
for (uint256 i = 0; i < end_ - start_; i++) {
ret[i] = data_[i + start_];
}
return ret;
}
}

/**
* @notice indexOf is used to find where char_ appears in data_.
* @param data_ raw data, passed in as bytes.
* @param char_ target byte.
* @return index, and whether the search was successful.
*/
function indexOf(bytes memory data_, bytes1 char_) pure returns (uint256, bool) {
unchecked {
for (uint256 i = 0; i < data_.length; i++) {
if (data_[i] == char_) {
return (i, true);
}
}
return (0, false);
}
}
47 changes: 47 additions & 0 deletions src/BitcoinNetworkEncoder.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.24;

library BitcoinNetworkEncoder {

bytes constant BTC_BECH32_MAINNET_BYTES = hex"626331"; // prefix = bc1
bytes constant BTC_BECH32_TESTNET_BYTES = hex"746231"; // prefix = tb1
bytes constant BTC_BECH32_REGTEST_BYTES = hex"6263727431"; // prefix = bcrt1

string constant BTC_BECH32_MAINNET = 'bc';
string constant BTC_BECH32_TESTNET = 'tb';
string constant BTC_BECH32_REGTEST = 'brct';

//NB: don't forget to update `lnbtc_ext.go` when changing this enum!
enum Network {
Mainnet,
Testnet,
Regtest
}

function getBtcBech32Prefix(Network _network) public pure returns (bytes memory) {
if (_network == Network.Mainnet) {
return BTC_BECH32_MAINNET_BYTES;
} else if (_network == Network.Regtest) {
return BTC_BECH32_REGTEST_BYTES;
} else if (_network == Network.Testnet) {
return BTC_BECH32_TESTNET_BYTES;
} else {
revert("Unknown network type");
}
}

function getNetworkPrefix(Network _network) public pure returns (string memory) {
if (_network == Network.Mainnet) {
return BTC_BECH32_MAINNET;
} else if (_network == Network.Testnet) {
return BTC_BECH32_TESTNET;
} else if (_network == Network.Regtest) {
return BTC_BECH32_REGTEST;
} else {
revert("Unknown network type");
}
}


}
Loading

0 comments on commit 4c27632

Please sign in to comment.