-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
move bitcoin utils to this repo (#28)
* move bitcoin utils to this repo * cleanup --------- Co-authored-by: Viacheslav Zhygulin <[email protected]>
- Loading branch information
Showing
9 changed files
with
763 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | ||
} | ||
} | ||
|
||
|
||
} |
Oops, something went wrong.