diff --git a/python_ref/gen_ref_data_hmac_sha512.py b/python_ref/gen_ref_data_hmac_sha512.py
new file mode 100644
index 0000000..fc0b483
--- /dev/null
+++ b/python_ref/gen_ref_data_hmac_sha512.py
@@ -0,0 +1,30 @@
+import hashlib
+import hmac
+
+tests = [
+ [bytes.fromhex("010203"), bytes.fromhex("040506")],
+ [bytes.fromhex("01"*128), bytes.fromhex("04")],
+ [bytes.fromhex("01"*150), bytes.fromhex("04")],
+]
+
+
+if __name__ == "__main__":
+ for itest, test in enumerate(tests):
+ key, message = test
+
+ key_hex = key.hex()
+ message_hex = message.hex()
+
+ hmac_result = hmac.new(key, message, hashlib.sha512).digest()
+ hmac1_hex = hmac_result[:32].hex()
+ hmac2_hex = hmac_result[32:].hex()
+
+ print(f"bytes memory key{itest} = hex\"{key_hex}\";")
+ print(f"bytes memory message{itest} = hex\"{message_hex}\";")
+ print(f"bytes32 hmac{itest}_1_expected = hex\"{hmac1_hex}\";")
+ print(f"bytes32 hmac{itest}_2_expected = hex\"{hmac2_hex}\";")
+ print(f"(bytes32 hmac{itest}_1, bytes32 hmac{itest}_2) = Hmac.hmacSha512(key{itest}, message{itest});")
+ print(f"assertEq(hmac{itest}_1, hmac{itest}_1_expected);")
+ print(f"assertEq(hmac{itest}_2, hmac{itest}_2_expected);")
+ print()
+
diff --git a/python_ref/gen_ref_data_sha512.py b/python_ref/gen_ref_data_sha512.py
new file mode 100644
index 0000000..259da0d
--- /dev/null
+++ b/python_ref/gen_ref_data_sha512.py
@@ -0,0 +1,27 @@
+import hashlib
+
+tests = [
+ "",
+ "010203",
+ "ab"*1000,
+]
+
+
+if __name__ == "__main__":
+ for itest, data_hex in enumerate(tests):
+ data = bytes.fromhex(data_hex)
+
+ # Calculate SHA-512
+ sha512_hash = hashlib.sha512(data)
+ hash1_hex = sha512_hash.digest()[:32].hex()
+ hash2_hex = sha512_hash.digest()[32:].hex()
+
+ print(f"bytes memory data{itest} = hex\"{data_hex}\";")
+ print(f"bytes32 hash{itest}_1_expected = hex\"{hash1_hex}\";")
+ print(f"bytes32 hash{itest}_2_expected = hex\"{hash2_hex}\";")
+
+ print(f"(bytes32 hash{itest}_1, bytes32 hash{itest}_2) = Sha2Ext.sha512(data{itest});")
+ print(f"assertEq(hash{itest}_1, hash{itest}_1_expected);")
+ print(f"assertEq(hash{itest}_2, hash{itest}_2_expected);")
+ print()
+
diff --git a/src/Deriver.sol b/src/Deriver.sol
index a4c3658..535fc91 100644
--- a/src/Deriver.sol
+++ b/src/Deriver.sol
@@ -4,6 +4,7 @@ pragma solidity 0.8.27;
import {EllipticCurve} from "../lib/elliptic-curve-solidity/contracts/EllipticCurve.sol";
+import {Hmac} from "./Hmac.sol";
import {Bech32m} from "./Bech32m.sol";
library Deriver {
@@ -16,8 +17,16 @@ library Deriver {
uint256 public constant BB = 7;
uint256 public constant PP =
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F;
+ uint256 public constant NN = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
// END SECP256k1 CONSTANTS
+ // HardenedKeyStart is the index at which a hardened key starts. Each
+ // extended key has 2^31 normal child keys and 2^31 hardened child keys.
+ // Thus the range for normal child keys is [0, 2^31 - 1] and the range
+ // for hardened child keys is [2^31, 2^32 - 1].
+ // Pubkey derivation is only supported for normal(not hardened) child keys.
+ uint256 public constant HARDENED_KEY_START = 0x80000000; // 2^31
+
// sha256("TapTweak")
bytes32 public constant SHA256_TAP_TWEAK =
hex"e80fe1639c9ca050e3af1b39c143c63e429cbceb15d940fbb5c5a1f4af57c5e9";
@@ -152,4 +161,67 @@ library Deriver {
return getBtcTaprootAddrFromPubkey(xTweaked, hrp);
}
+
+ // Public key derivation works only for normal(not hardened) child keys.
+ // index < HARDENED_KEY_START
+ function deriveChildPubkeyBip32(
+ uint256 px,
+ uint256 py,
+ bytes32 chainCode,
+ uint256 index
+ ) internal pure returns (uint256, uint256) {
+ require(index < HARDENED_KEY_START, "Index must be less than HARDENED_KEY_START");
+
+ bytes1 prefix = 0x02;
+ if (py % 2 == 1) {
+ prefix = 0x03;
+ }
+
+ bytes memory data = abi.encodePacked(
+ prefix,
+ bytes32(px),
+ uint32(index)
+ );
+
+ (bytes32 ilBytes32, bytes32 irBytes32) = Hmac.hmacSha512(abi.encodePacked(chainCode), data);
+ uint256 il = uint256(ilBytes32);
+
+ require(il < NN, "il must be less than NN");
+
+ (uint256 ilx, uint256 ily) = mulPubkey(GX, GY, il);
+ (uint256 x1, uint256 y1) = addPubkeys(px, py, ilx, ily);
+
+ require(x1 != 0 || y1 != 0, "child pubkey is point at infinity");
+
+ return (x1, y1);
+ }
+
+ function deriveReceivingAddressFromIndex(
+ uint256 parentX,
+ uint256 parentY,
+ uint256 index,
+ bytes memory hrp
+ ) internal pure returns (string memory){
+ bytes1 prefix = 0x02;
+ if (parentY % 2 == 1) {
+ prefix = 0x03;
+ }
+
+ bytes memory parentSerialized = abi.encodePacked(
+ prefix,
+ bytes32(parentX)
+ );
+
+ bytes32 chainCode = sha256(parentSerialized);
+
+ (uint256 childX, uint256 childY) = deriveChildPubkeyBip32(parentX, parentY, chainCode, index);
+
+ if (childY % 2 == 1) {
+ childY = PP - childY;
+ }
+
+ (uint256 childXTweaked,) = computeTaprootKeyNoScript(childX, childY);
+
+ return getBtcTaprootAddrFromPubkey(childXTweaked, hrp);
+ }
}
diff --git a/src/Hmac.sol b/src/Hmac.sol
new file mode 100644
index 0000000..72746b6
--- /dev/null
+++ b/src/Hmac.sol
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.27;
+
+import {Sha2Ext} from "./sha2/Sha2Ext.sol";
+
+library Hmac {
+ error KeyCannotBeEmpty();
+ error MessageCannotBeEmpty();
+
+ // SHA-512 block size in bytes
+ uint256 constant BLOCK_SIZE = 128;
+
+ function sha512AsBytes(
+ bytes memory message
+ ) internal pure returns (bytes memory) {
+ (bytes32 hash1, bytes32 hash2) = Sha2Ext.sha512(message);
+ return abi.encodePacked(hash1, hash2);
+ }
+
+ function hmacSha512(
+ bytes memory key,
+ bytes memory message
+ ) internal pure returns (bytes32, bytes32) {
+ if(key.length == 0) {
+ revert KeyCannotBeEmpty();
+ }
+ if(message.length == 0) {
+ revert MessageCannotBeEmpty();
+ }
+
+ bytes memory paddedKey = key;
+
+ // If key is longer than block size, hash it
+ if (key.length > BLOCK_SIZE) {
+ paddedKey = sha512AsBytes(key);
+ }
+
+ // If key is shorter than block size, pad with zeros
+ if (paddedKey.length < BLOCK_SIZE) {
+ bytes memory temp = new bytes(BLOCK_SIZE);
+ for (uint i = 0; i < paddedKey.length; i++) {
+ temp[i] = paddedKey[i];
+ }
+ paddedKey = temp;
+ }
+
+ // Create inner and outer padded keys
+ bytes memory innerKey = new bytes(BLOCK_SIZE);
+ bytes memory outerKey = new bytes(BLOCK_SIZE);
+
+ for (uint i = 0; i < BLOCK_SIZE; i++) {
+ innerKey[i] = bytes1(uint8(paddedKey[i]) ^ 0x36);
+ outerKey[i] = bytes1(uint8(paddedKey[i]) ^ 0x5c);
+ }
+
+ // HMAC = H(outerKey || H(innerKey || message))
+ bytes memory innerHash = sha512AsBytes(abi.encodePacked(innerKey, message));
+ (bytes32 rez1, bytes32 rez2) = Sha2Ext.sha512(abi.encodePacked(outerKey, innerHash));
+
+ return (rez1, rez2);
+ }
+}
diff --git a/src/sha2/LibBytes.sol b/src/sha2/LibBytes.sol
new file mode 100644
index 0000000..7c8212b
--- /dev/null
+++ b/src/sha2/LibBytes.sol
@@ -0,0 +1,467 @@
+// SPDX-License-Identifier: MIT
+
+// Copied from https://github.com/yangfh2004/SolSha2Ext/blob/main/contracts/lib/LibBytes.sol
+// Direct import is not possible because it has hardcoded solidity version there
+
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+// solhint-disable max-line-length
+// Original library copied from
+// https://github.com/0xProject/exchange-v3/blob/aae46bef841bfd1cc31028f41793db4fe7197084/contracts/utils/contracts/src/LibBytes.sol
+// solhint-enable max-line-length
+
+pragma solidity 0.8.27;
+
+library LibBytes {
+ using LibBytes for bytes;
+
+ /// @dev Gets the memory address for a byte array.
+ /// @param input Byte array to lookup.
+ /// @return memoryAddress Memory address of byte array. This
+ /// points to the header of the byte array which contains
+ /// the length.
+ function rawAddress(bytes memory input) internal pure returns (uint256 memoryAddress) {
+ assembly {
+ memoryAddress := input
+ }
+ return memoryAddress;
+ }
+
+ /// @dev Gets the memory address for the contents of a byte array.
+ /// @param input Byte array to lookup.
+ /// @return memoryAddress Memory address of the contents of the byte array.
+ function contentAddress(bytes memory input) internal pure returns (uint256 memoryAddress) {
+ assembly {
+ memoryAddress := add(input, 32)
+ }
+ return memoryAddress;
+ }
+
+ /// @dev Copies `length` bytes from memory location `source` to `dest`.
+ /// @param dest memory address to copy bytes to.
+ /// @param source memory address to copy bytes from.
+ /// @param length number of bytes to copy.
+ function memCopy(uint256 dest, uint256 source, uint256 length) internal pure {
+ if (length < 32) {
+ // Handle a partial word by reading destination and masking
+ // off the bits we are interested in.
+ // This correctly handles overlap, zero lengths and source == dest
+ assembly {
+ let mask := sub(exp(256, sub(32, length)), 1)
+ let s := and(mload(source), not(mask))
+ let d := and(mload(dest), mask)
+ mstore(dest, or(s, d))
+ }
+ } else {
+ // Skip the O(length) loop when source == dest.
+ if (source == dest) {
+ return;
+ }
+
+ // For large copies we copy whole words at a time. The final
+ // word is aligned to the end of the range (instead of after the
+ // previous) to handle partial words. So a copy will look like this:
+ //
+ // ####
+ // ####
+ // ####
+ // ####
+ //
+ // We handle overlap in the source and destination range by
+ // changing the copying direction. This prevents us from
+ // overwriting parts of source that we still need to copy.
+ //
+ // This correctly handles source == dest
+ //
+ if (source > dest) {
+ assembly {
+ // We subtract 32 from `sEnd` and `dEnd` because it
+ // is easier to compare with in the loop, and these
+ // are also the addresses we need for copying the
+ // last bytes.
+ length := sub(length, 32)
+ let sEnd := add(source, length)
+ let dEnd := add(dest, length)
+
+ // Remember the last 32 bytes of source
+ // This needs to be done here and not after the loop
+ // because we may have overwritten the last bytes in
+ // source already due to overlap.
+ let last := mload(sEnd)
+
+ // Copy whole words front to back
+ // Note: the first check is always true,
+ // this could have been a do-while loop.
+ // solhint-disable-next-line no-empty-blocks
+ for {
+
+ } lt(source, sEnd) {
+
+ } {
+ mstore(dest, mload(source))
+ source := add(source, 32)
+ dest := add(dest, 32)
+ }
+
+ // Write the last 32 bytes
+ mstore(dEnd, last)
+ }
+ } else {
+ assembly {
+ // We subtract 32 from `sEnd` and `dEnd` because those
+ // are the starting points when copying a word at the end.
+ length := sub(length, 32)
+ let sEnd := add(source, length)
+ let dEnd := add(dest, length)
+
+ // Remember the first 32 bytes of source
+ // This needs to be done here and not after the loop
+ // because we may have overwritten the first bytes in
+ // source already due to overlap.
+ let first := mload(source)
+
+ // Copy whole words back to front
+ // We use a signed comparisson here to allow dEnd to become
+ // negative (happens when source and dest < 32). Valid
+ // addresses in local memory will never be larger than
+ // 2**255, so they can be safely re-interpreted as signed.
+ // Note: the first check is always true,
+ // this could have been a do-while loop.
+ // solhint-disable-next-line no-empty-blocks
+ for {
+
+ } slt(dest, dEnd) {
+
+ } {
+ mstore(dEnd, mload(sEnd))
+ sEnd := sub(sEnd, 32)
+ dEnd := sub(dEnd, 32)
+ }
+
+ // Write the first 32 bytes
+ mstore(dest, first)
+ }
+ }
+ }
+ }
+
+ /// @dev Returns a slices from a byte array.
+ /// @param b The byte array to take a slice from.
+ /// @param from The starting index for the slice (inclusive).
+ /// @param to The final index for the slice (exclusive).
+ /// @return result The slice containing bytes at indices [from, to)
+ function slice(bytes memory b, uint256 from, uint256 to) internal pure returns (bytes memory result) {
+ require(from <= to, "FROM_LESS_THAN_TO_REQUIRED");
+ require(to <= b.length, "TO_LESS_THAN_LENGTH_REQUIRED");
+
+ // Create a new bytes structure and copy contents
+ result = new bytes(to - from);
+ memCopy(result.contentAddress(), b.contentAddress() + from, result.length);
+ return result;
+ }
+
+ /// @dev Returns a slice from a byte array without preserving the input.
+ /// @param b The byte array to take a slice from. Will be destroyed in the process.
+ /// @param from The starting index for the slice (inclusive).
+ /// @param to The final index for the slice (exclusive).
+ /// @return result The slice containing bytes at indices [from, to)
+ /// @dev When `from == 0`, the original array will match the slice. In other cases its state will be corrupted.
+ function sliceDestructive(bytes memory b, uint256 from, uint256 to) internal pure returns (bytes memory result) {
+ require(from <= to, "FROM_LESS_THAN_TO_REQUIRED");
+ require(to <= b.length, "TO_LESS_THAN_LENGTH_REQUIRED");
+
+ // Create a new bytes structure around [from, to) in-place.
+ assembly {
+ result := add(b, from)
+ mstore(result, sub(to, from))
+ }
+ return result;
+ }
+
+ /// @dev Pops the last byte off of a byte array by modifying its length.
+ /// @param b Byte array that will be modified.
+ /// @return result The byte that was popped off.
+ function popLastByte(bytes memory b) internal pure returns (bytes1 result) {
+ require(b.length > 0, "GREATER_THAN_ZERO_LENGTH_REQUIRED");
+
+ // Store last byte.
+ result = b[b.length - 1];
+
+ assembly {
+ // Decrement length of byte array.
+ let newLen := sub(mload(b), 1)
+ mstore(b, newLen)
+ }
+ return result;
+ }
+
+ /// @dev Pops the last 20 bytes off of a byte array by modifying its length.
+ /// @param b Byte array that will be modified.
+ /// @return result The 20 byte address that was popped off.
+ function popLast20Bytes(bytes memory b) internal pure returns (address result) {
+ require(b.length >= 20, "GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED");
+
+ // Store last 20 bytes.
+ result = readAddress(b, b.length - 20);
+
+ assembly {
+ // Subtract 20 from byte array length.
+ let newLen := sub(mload(b), 20)
+ mstore(b, newLen)
+ }
+ return result;
+ }
+
+ /// @dev Tests equality of two byte arrays.
+ /// @param lhs First byte array to compare.
+ /// @param rhs Second byte array to compare.
+ /// @return equal True if arrays are the same. False otherwise.
+ function equals(bytes memory lhs, bytes memory rhs) internal pure returns (bool equal) {
+ // Keccak gas cost is 30 + numWords * 6. This is a cheap way to compare.
+ // We early exit on unequal lengths, but keccak would also correctly
+ // handle this.
+ return lhs.length == rhs.length && keccak256(lhs) == keccak256(rhs);
+ }
+
+ /// @dev Reads an address from a position in a byte array.
+ /// @param b Byte array containing an address.
+ /// @param index Index in byte array of address.
+ /// @return result address from byte array.
+ function readAddress(bytes memory b, uint256 index) internal pure returns (address result) {
+ require(
+ b.length >= index + 20, // 20 is length of address
+ "GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED"
+ );
+
+ // Add offset to index:
+ // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
+ // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index)
+ index += 20;
+
+ // Read address from array memory
+ assembly {
+ // 1. Add index to address of bytes array
+ // 2. Load 32-byte word from memory
+ // 3. Apply 20-byte mask to obtain address
+ result := and(mload(add(b, index)), 0xffffffffffffffffffffffffffffffffffffffff)
+ }
+ return result;
+ }
+
+ /// @dev Writes an address into a specific position in a byte array.
+ /// @param b Byte array to insert address into.
+ /// @param index Index in byte array of address.
+ /// @param input Address to put into byte array.
+ function writeAddress(bytes memory b, uint256 index, address input) internal pure {
+ require(
+ b.length >= index + 20, // 20 is length of address
+ "GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED"
+ );
+
+ // Add offset to index:
+ // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
+ // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index)
+ index += 20;
+
+ // Store address into array memory
+ assembly {
+ // The address occupies 20 bytes and mstore stores 32 bytes.
+ // First fetch the 32-byte word where we'll be storing the address, then
+ // apply a mask so we have only the bytes in the word that the address will not occupy.
+ // Then combine these bytes with the address and store the 32 bytes back to memory with mstore.
+
+ // 1. Add index to address of bytes array
+ // 2. Load 32-byte word from memory
+ // 3. Apply 12-byte mask to obtain extra bytes occupying word of memory where we'll store the address
+ let neighbors := and(
+ mload(add(b, index)),
+ 0xffffffffffffffffffffffff0000000000000000000000000000000000000000
+ )
+
+ // Make sure input address is clean.
+ // (Solidity does not guarantee this)
+ input := and(input, 0xffffffffffffffffffffffffffffffffffffffff)
+
+ // Store the neighbors and address into memory
+ mstore(add(b, index), xor(input, neighbors))
+ }
+ }
+
+ /// @dev Reads a bytes32 value from a position in a byte array.
+ /// @param b Byte array containing a bytes32 value.
+ /// @param index Index in byte array of bytes32 value.
+ /// @return result bytes32 value from byte array.
+ function readBytes32(bytes memory b, uint256 index) internal pure returns (bytes32 result) {
+ require(b.length >= index + 32, "GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED");
+
+ // Arrays are prefixed by a 256 bit length parameter
+ index += 32;
+
+ // Read the bytes32 from array memory
+ assembly {
+ result := mload(add(b, index))
+ }
+ return result;
+ }
+
+ /// @dev Writes a bytes32 into a specific position in a byte array.
+ /// @param b Byte array to insert into.
+ /// @param index Index in byte array of .
+ /// @param input bytes32 to put into byte array.
+ function writeBytes32(bytes memory b, uint256 index, bytes32 input) internal pure {
+ require(b.length >= index + 32, "GREATER_OR_EQUAL_TO_32_LENGTH_REQUIRED");
+
+ // Arrays are prefixed by a 256 bit length parameter
+ index += 32;
+
+ // Read the bytes32 from array memory
+ assembly {
+ mstore(add(b, index), input)
+ }
+ }
+
+ /// @dev Reads a uint256 value from a position in a byte array.
+ /// @param b Byte array containing a uint256 value.
+ /// @param index Index in byte array of uint256 value.
+ /// @return result uint256 value from byte array.
+ function readUint256(bytes memory b, uint256 index) internal pure returns (uint256 result) {
+ result = uint256(readBytes32(b, index));
+ return result;
+ }
+
+ /// @dev Writes a uint256 into a specific position in a byte array.
+ /// @param b Byte array to insert into.
+ /// @param index Index in byte array of .
+ /// @param input uint256 to put into byte array.
+ function writeUint256(bytes memory b, uint256 index, uint256 input) internal pure {
+ writeBytes32(b, index, bytes32(input));
+ }
+
+ /// @dev Reads an unpadded bytes4 value from a position in a byte array.
+ /// @param b Byte array containing a bytes4 value.
+ /// @param index Index in byte array of bytes4 value.
+ /// @return result bytes4 value from byte array.
+ function readBytes4(bytes memory b, uint256 index) internal pure returns (bytes4 result) {
+ require(b.length >= index + 4, "GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED");
+
+ // Arrays are prefixed by a 32 byte length field
+ index += 32;
+
+ // Read the bytes4 from array memory
+ assembly {
+ result := mload(add(b, index))
+ // Solidity does not require us to clean the trailing bytes.
+ // We do it anyway
+ result := and(result, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000)
+ }
+ return result;
+ }
+
+ /// @dev Reads an unpadded bytes8 value from a position in a byte array.
+ /// @param b Byte array containing a bytes8 value.
+ /// @param index Index in byte array of bytes4 value.
+ /// @return result bytes8 value from byte array.
+ function readBytes8(bytes memory b, uint256 index) internal pure returns (bytes8 result) {
+ require(b.length >= index + 8, "GREATER_OR_EQUAL_TO_8_LENGTH_REQUIRED");
+
+ // Arrays are prefixed by a 32 byte length field
+ index += 32;
+
+ // Read the bytes8 from array memory
+ assembly {
+ result := mload(add(b, index))
+ // Solidity does not require us to clean the trailing bytes.
+ // We do it anyway
+ result := and(result, 0xFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000)
+ }
+ return result;
+ }
+
+ /// @dev Reads an unpadded bytes2 value from a position in a byte array.
+ /// @param b Byte array containing a bytes2 value.
+ /// @param index Index in byte array of bytes2 value.
+ /// @return result bytes2 value from byte array.
+ function readBytes2(bytes memory b, uint256 index) internal pure returns (bytes2 result) {
+ require(b.length >= index + 2, "GREATER_OR_EQUAL_TO_2_LENGTH_REQUIRED");
+
+ // Arrays are prefixed by a 32 byte length field
+ index += 32;
+
+ // Read the bytes2 from array memory
+ assembly {
+ result := mload(add(b, index))
+ // Solidity does not require us to clean the trailing bytes.
+ // We do it anyway
+ result := and(result, 0xFFFF000000000000000000000000000000000000000000000000000000000000)
+ }
+ return result;
+ }
+
+ /// @dev Reads nested bytes from a specific position.
+ /// @dev NOTE: the returned value overlaps with the input value.
+ /// Both should be treated as immutable.
+ /// @param b Byte array containing nested bytes.
+ /// @param index Index of nested bytes.
+ /// @return result Nested bytes.
+ function readBytesWithLength(bytes memory b, uint256 index) internal pure returns (bytes memory result) {
+ // Read length of nested bytes
+ uint256 nestedBytesLength = readUint256(b, index);
+ index += 32;
+
+ // Assert length of is valid, given
+ // length of nested bytes
+ require(b.length >= index + nestedBytesLength, "GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED");
+
+ // Return a pointer to the byte array as it exists inside `b`
+ assembly {
+ result := add(b, index)
+ }
+ return result;
+ }
+
+ /// @dev Inserts bytes at a specific position in a byte array.
+ /// @param b Byte array to insert into.
+ /// @param index Index in byte array of .
+ /// @param input bytes to insert.
+ function writeBytesWithLength(bytes memory b, uint256 index, bytes memory input) internal pure {
+ // Assert length of is valid, given
+ // length of input
+ require(
+ b.length >= index + 32 + input.length, // 32 bytes to store length
+ "GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED"
+ );
+
+ // Copy into
+ memCopy(
+ b.contentAddress() + index,
+ input.rawAddress(), // includes length of
+ input.length + 32 // +32 bytes to store length
+ );
+ }
+
+ /// @dev Performs a deep copy of a byte array onto another byte array of greater than or equal length.
+ /// @param dest Byte array that will be overwritten with source bytes.
+ /// @param source Byte array to copy onto dest bytes.
+ function deepCopyBytes(bytes memory dest, bytes memory source) internal pure {
+ uint256 sourceLen = source.length;
+ // Dest length must be >= source length, or some bytes would not be copied.
+ require(dest.length >= sourceLen, "GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED");
+ memCopy(dest.contentAddress(), source.contentAddress(), sourceLen);
+ }
+}
\ No newline at end of file
diff --git a/src/sha2/Sha2Ext.sol b/src/sha2/Sha2Ext.sol
new file mode 100644
index 0000000..9ff535b
--- /dev/null
+++ b/src/sha2/Sha2Ext.sol
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: MIT
+
+// Copied from https://github.com/yangfh2004/SolSha2Ext/blob/main/contracts/lib/Sha2Ext.sol
+// Direct import is not possible because it has hardcoded solidity version there
+
+// MIT License
+
+// Copyright (c) 2023 Paul Razvan Berg
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
+// documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
+// Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+pragma solidity 0.8.27;
+
+import { LibBytes } from "./LibBytes.sol";
+
+library Sha2Ext {
+ function sha2(bytes memory message, uint64[8] memory h) internal pure {
+ uint64[80] memory k = [
+ 0x428a2f98d728ae22,
+ 0x7137449123ef65cd,
+ 0xb5c0fbcfec4d3b2f,
+ 0xe9b5dba58189dbbc,
+ 0x3956c25bf348b538,
+ 0x59f111f1b605d019,
+ 0x923f82a4af194f9b,
+ 0xab1c5ed5da6d8118,
+ 0xd807aa98a3030242,
+ 0x12835b0145706fbe,
+ 0x243185be4ee4b28c,
+ 0x550c7dc3d5ffb4e2,
+ 0x72be5d74f27b896f,
+ 0x80deb1fe3b1696b1,
+ 0x9bdc06a725c71235,
+ 0xc19bf174cf692694,
+ 0xe49b69c19ef14ad2,
+ 0xefbe4786384f25e3,
+ 0x0fc19dc68b8cd5b5,
+ 0x240ca1cc77ac9c65,
+ 0x2de92c6f592b0275,
+ 0x4a7484aa6ea6e483,
+ 0x5cb0a9dcbd41fbd4,
+ 0x76f988da831153b5,
+ 0x983e5152ee66dfab,
+ 0xa831c66d2db43210,
+ 0xb00327c898fb213f,
+ 0xbf597fc7beef0ee4,
+ 0xc6e00bf33da88fc2,
+ 0xd5a79147930aa725,
+ 0x06ca6351e003826f,
+ 0x142929670a0e6e70,
+ 0x27b70a8546d22ffc,
+ 0x2e1b21385c26c926,
+ 0x4d2c6dfc5ac42aed,
+ 0x53380d139d95b3df,
+ 0x650a73548baf63de,
+ 0x766a0abb3c77b2a8,
+ 0x81c2c92e47edaee6,
+ 0x92722c851482353b,
+ 0xa2bfe8a14cf10364,
+ 0xa81a664bbc423001,
+ 0xc24b8b70d0f89791,
+ 0xc76c51a30654be30,
+ 0xd192e819d6ef5218,
+ 0xd69906245565a910,
+ 0xf40e35855771202a,
+ 0x106aa07032bbd1b8,
+ 0x19a4c116b8d2d0c8,
+ 0x1e376c085141ab53,
+ 0x2748774cdf8eeb99,
+ 0x34b0bcb5e19b48a8,
+ 0x391c0cb3c5c95a63,
+ 0x4ed8aa4ae3418acb,
+ 0x5b9cca4f7763e373,
+ 0x682e6ff3d6b2b8a3,
+ 0x748f82ee5defb2fc,
+ 0x78a5636f43172f60,
+ 0x84c87814a1f0ab72,
+ 0x8cc702081a6439ec,
+ 0x90befffa23631e28,
+ 0xa4506cebde82bde9,
+ 0xbef9a3f7b2c67915,
+ 0xc67178f2e372532b,
+ 0xca273eceea26619c,
+ 0xd186b8c721c0c207,
+ 0xeada7dd6cde0eb1e,
+ 0xf57d4f7fee6ed178,
+ 0x06f067aa72176fba,
+ 0x0a637dc5a2c898a6,
+ 0x113f9804bef90dae,
+ 0x1b710b35131c471b,
+ 0x28db77f523047d84,
+ 0x32caab7b40c72493,
+ 0x3c9ebe0a15c9bebc,
+ 0x431d67c49c100d4c,
+ 0x4cc5d4becb3e42b6,
+ 0x597f299cfc657e2a,
+ 0x5fcb6fab3ad6faec,
+ 0x6c44198c4a475817
+ ];
+
+ bytes memory padding = padMessage(message);
+ require(padding.length % 128 == 0, "PADDING_ERROR");
+ uint64[80] memory w;
+ uint64[8] memory temp;
+ uint64[16] memory blocks;
+ uint256 messageLength = (message.length / 128) * 128;
+ unchecked {
+ for (uint256 i = 0; i < (messageLength + padding.length); i += 128) {
+ if (i < messageLength) {
+ getBlock(message, blocks, i);
+ } else {
+ getBlock(padding, blocks, i - messageLength);
+ }
+ for (uint256 j = 0; j < 16; ++j) {
+ w[j] = blocks[j];
+ }
+ for (uint256 j = 16; j < 80; ++j) {
+ w[j] = gamma1(w[j - 2]) + w[j - 7] + gamma0(w[j - 15]) + w[j - 16];
+ }
+ for (uint256 j = 0; j < 8; ++j) {
+ temp[j] = h[j];
+ }
+ for (uint256 j = 0; j < 80; ++j) {
+ uint64 t1 = temp[7] + sigma1(temp[4]) + ch(temp[4], temp[5], temp[6]) + k[j] + w[j];
+ uint64 t2 = sigma0(temp[0]) + maj(temp[0], temp[1], temp[2]);
+ temp[7] = temp[6];
+ temp[6] = temp[5];
+ temp[5] = temp[4];
+ temp[4] = temp[3] + t1;
+ temp[3] = temp[2];
+ temp[2] = temp[1];
+ temp[1] = temp[0];
+ temp[0] = t1 + t2;
+ }
+ for (uint256 j = 0; j < 8; ++j) {
+ h[j] += temp[j];
+ }
+ }
+ }
+ }
+
+ function sha384(bytes memory message) internal pure returns (bytes32, bytes16) {
+ uint64[8] memory h = [
+ 0xcbbb9d5dc1059ed8,
+ 0x629a292a367cd507,
+ 0x9159015a3070dd17,
+ 0x152fecd8f70e5939,
+ 0x67332667ffc00b31,
+ 0x8eb44a8768581511,
+ 0xdb0c2e0d64f98fa7,
+ 0x47b5481dbefa4fa4
+ ];
+ sha2(message, h);
+ return (
+ bytes32(abi.encodePacked(bytes8(h[0]), bytes8(h[1]), bytes8(h[2]), bytes8(h[3]))),
+ bytes16(abi.encodePacked(bytes8(h[4]), bytes8(h[5])))
+ );
+ }
+
+ function sha512(bytes memory message) internal pure returns (bytes32, bytes32) {
+ uint64[8] memory h = [
+ 0x6a09e667f3bcc908,
+ 0xbb67ae8584caa73b,
+ 0x3c6ef372fe94f82b,
+ 0xa54ff53a5f1d36f1,
+ 0x510e527fade682d1,
+ 0x9b05688c2b3e6c1f,
+ 0x1f83d9abfb41bd6b,
+ 0x5be0cd19137e2179
+ ];
+ sha2(message, h);
+ return (
+ bytes32(abi.encodePacked(bytes8(h[0]), bytes8(h[1]), bytes8(h[2]), bytes8(h[3]))),
+ bytes32(abi.encodePacked(bytes8(h[4]), bytes8(h[5]), bytes8(h[6]), bytes8(h[7])))
+ );
+ }
+
+ function padMessage(bytes memory message) internal pure returns (bytes memory) {
+ uint256 messageLength = message.length;
+ bytes8 bitLength = bytes8(uint64(messageLength * 8));
+ uint256 mdi = messageLength % 128;
+ uint256 paddingLength;
+ if (mdi < 112) {
+ paddingLength = 119 - mdi;
+ } else {
+ paddingLength = 247 - mdi;
+ }
+ bytes memory padding = new bytes(paddingLength);
+ bytes memory tail = LibBytes.slice(message, messageLength - mdi, messageLength);
+ return abi.encodePacked(tail, bytes1(0x80), padding, bitLength);
+ }
+
+ function getBlock(bytes memory message, uint64[16] memory blocks, uint256 index) internal pure {
+ for (uint256 i = 0; i < 16; ++i) {
+ blocks[i] = uint64(LibBytes.readBytes8(message, index + i * 8));
+ }
+ }
+
+ function ch(uint64 x, uint64 y, uint64 z) internal pure returns (uint64) {
+ return (x & y) ^ (~x & z);
+ }
+
+ function maj(uint64 x, uint64 y, uint64 z) internal pure returns (uint64) {
+ return (x & y) ^ (x & z) ^ (y & z);
+ }
+
+ function sigma0(uint64 x) internal pure returns (uint64) {
+ return (rotateRight(x, 28) ^ rotateRight(x, 34) ^ rotateRight(x, 39));
+ }
+
+ function sigma1(uint64 x) internal pure returns (uint64) {
+ return (rotateRight(x, 14) ^ rotateRight(x, 18) ^ rotateRight(x, 41));
+ }
+
+ function gamma0(uint64 x) internal pure returns (uint64) {
+ return (rotateRight(x, 1) ^ rotateRight(x, 8) ^ (x >> 7));
+ }
+
+ function gamma1(uint64 x) internal pure returns (uint64) {
+ return (rotateRight(x, 19) ^ rotateRight(x, 61) ^ (x >> 6));
+ }
+
+ function rotateRight(uint64 x, uint64 n) internal pure returns (uint64) {
+ return (x << (64 - n)) | (x >> n);
+ }
+}
\ No newline at end of file
diff --git a/test/Deriver.t.sol b/test/Deriver.t.sol
index 70388c5..de7013d 100644
--- a/test/Deriver.t.sol
+++ b/test/Deriver.t.sol
@@ -310,4 +310,173 @@ contract DeriverTest is Test {
);
assertEq(expectedBtcAddr_3, btcAddr_3);
}
+
+ function testDeriveChildPubkeyBip32() public pure {
+ uint256 px0 = 57074945586406715334625111669072956770253198967468104181021882430082100612963;
+ uint256 py0 = 23142750304142828437953232435448444948300648252183245674679985556457305450420;
+ bytes32 chainCode0 = hex"93071d00a68b251e2556974c5a9cba5fd3ceebfdf3d6b083978cb3f3072bdc6b";
+ uint256 index0 = 2;
+ (uint256 cx0, uint256 cy0) = Deriver.deriveChildPubkeyBip32(
+ px0,
+ py0,
+ chainCode0,
+ index0
+ );
+ assertEq(
+ cx0,
+ 74070375407870803772383716147851670902687590661570579425410879994591649090379
+ );
+ assertEq(
+ cy0,
+ 22360565704915606957816728902241275983499976804656018213898267138481100992100
+ );
+
+ uint256 px1 = 30742219803864166600272570996883887958649406957278654474409962972519929889405;
+ uint256 py1 = 70513211465368901219354942416364416752777296449079386521912399107647951595716;
+ bytes32 chainCode1 = hex"190e31c385f819a048cabffdb0b3d0dbd3789d788a49cffc68261ef71b8263c8";
+ uint256 index1 = 17;
+ (uint256 cx1, uint256 cy1) = Deriver.deriveChildPubkeyBip32(
+ px1,
+ py1,
+ chainCode1,
+ index1
+ );
+ assertEq(
+ cx1,
+ 5087411184501414391835649840157068842008489572913385638823261961588855293850
+ );
+ assertEq(
+ cy1,
+ 110550600879399219428880348010790347930829296535905640329050505014535179606341
+ );
+
+ uint256 px2 = 107140830029448792289342525244896270065376582144659706402692854250688314563220;
+ uint256 py2 = 79935496324080726152605555942072989487007435347931064453233281410555623580053;
+ bytes32 chainCode2 = hex"bc9a6e462c0f8143a5f66a31cd445d849a7773a54ad7b04fef65497f7961c01a";
+ uint256 index2 = 130;
+ (uint256 cx2, uint256 cy2) = Deriver.deriveChildPubkeyBip32(
+ px2,
+ py2,
+ chainCode2,
+ index2
+ );
+ assertEq(
+ cx2,
+ 29618804800753246453676863992713689288577981054331407572395640099596072070334
+ );
+ assertEq(
+ cy2,
+ 19012186344758431981077100555529013400608836327149189423685420650092980251608
+ );
+
+ uint256 px3 = 50447842649702692613222804199309429964729864494053468570763353341059104687484;
+ uint256 py3 = 88549452749358207275533961140370602316997088319769331567603926522170617984074;
+ bytes32 chainCode3 = hex"502cc99c8c3090ebb776054f2016b18c04fb5ee9a281f7d0a4ed14761d9c0894";
+ uint256 index3 = 1027;
+ (uint256 cx3, uint256 cy3) = Deriver.deriveChildPubkeyBip32(
+ px3,
+ py3,
+ chainCode3,
+ index3
+ );
+ assertEq(
+ cx3,
+ 39410688582448126829209298776445037307408821414810593880153166428268607576812
+ );
+ assertEq(
+ cy3,
+ 27955881002545452218257578656005771940987221892190183476469781542040442979146
+ );
+
+ uint256 px4 = 83492360506258972363229007168259843798874789271615741208051464981258120268822;
+ uint256 py4 = 76191252864540787586774708913716597080653872773324753541711814103970596290506;
+ bytes32 chainCode4 = hex"dd5c8089a398c500c1a23245aba1179ac6d798facf695d7a600301891c8e8760";
+ uint256 index4 = 8196;
+ (uint256 cx4, uint256 cy4) = Deriver.deriveChildPubkeyBip32(
+ px4,
+ py4,
+ chainCode4,
+ index4
+ );
+ assertEq(
+ cx4,
+ 70050336265972352500022718553585380558030667178344386277189596126374957151588
+ );
+ assertEq(
+ cy4,
+ 42291296715940497887745101979021449602442772175864971502864329278021423114042
+ );
+ }
+
+ function testDeriveReceivingAddressFromIndex() public pure {
+ uint256 px0 = 92827281731274008954803586629051298518909571261423619289955380343486580347456;
+ uint256 py0 = 73102114537722655223140831762751126960901222634499698479313524413007101409342;
+ uint256 index0 = 2;
+ string memory addr0 = Deriver.deriveReceivingAddressFromIndex(
+ px0,
+ py0,
+ index0,
+ bytes("bcrt")
+ );
+ assertEq(
+ addr0,
+ "bcrt1pnxsxys8vd0zyznm8hrzukyh9dkvhvalxg9fk6mgd4e4awur2v7uqgrq4sc"
+ );
+
+ uint256 px1 = 42043242194725732014842968116051319138334003633183842263306037194578700909017;
+ uint256 py1 = 105821727192811762117189587761380553359906792616517208870399232874956654784394;
+ uint256 index1 = 33;
+ string memory addr1 = Deriver.deriveReceivingAddressFromIndex(
+ px1,
+ py1,
+ index1,
+ bytes("bcrt")
+ );
+ assertEq(
+ addr1,
+ "bcrt1pqmenx5decj6z7w0flwdlapuyqran78w0jq44sk7wkf7s37x3pp7s0yv6qp"
+ );
+
+ uint256 px2 = 64704383364590153413480446292465727728387599614170842002472827633593419784476;
+ uint256 py2 = 100573695907478917642263950404364417572064838031945548826282911915821399827971;
+ uint256 index2 = 514;
+ string memory addr2 = Deriver.deriveReceivingAddressFromIndex(
+ px2,
+ py2,
+ index2,
+ bytes("bcrt")
+ );
+ assertEq(
+ addr2,
+ "bcrt1pgpqxw0dagcsxlyae2hj34a0538jnmtvarpwtqm5txwnusnmygpqqucvsvt"
+ );
+
+ uint256 px3 = 52672097719492178455192603747575185348778895943012542362274508865272077582372;
+ uint256 py3 = 22715561287017813787869506730496377822788860365548411913263474592148236937818;
+ uint256 index3 = 8195;
+ string memory addr3 = Deriver.deriveReceivingAddressFromIndex(
+ px3,
+ py3,
+ index3,
+ bytes("bcrt")
+ );
+ assertEq(
+ addr3,
+ "bcrt1p97yvrl0dc67l3l42q3894k5x8nx7vt0z7jcjfwxzp066vqm5uk5ssszat7"
+ );
+
+ uint256 px4 = 7762222604264485911983061564766040050645982976758130076669678366079793347684;
+ uint256 py4 = 77842275810050337275361784865384886754506198694389874207460975687961431123894;
+ uint256 index4 = 131076;
+ string memory addr4 = Deriver.deriveReceivingAddressFromIndex(
+ px4,
+ py4,
+ index4,
+ bytes("bcrt")
+ );
+ assertEq(
+ addr4,
+ "bcrt1ph246t99e89ruellljuqnekwuf2p523lsmugw2kqu4u4uk32dmsasz086s8"
+ );
+ }
}
diff --git a/test/HmacSha512.t.sol b/test/HmacSha512.t.sol
new file mode 100644
index 0000000..a50caa5
--- /dev/null
+++ b/test/HmacSha512.t.sol
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: MIT
+
+pragma solidity 0.8.27;
+
+import {Test, console} from "forge-std/Test.sol";
+import {Hmac} from "../src/Hmac.sol";
+
+contract HmacSha512Test is Test {
+ function testHmacSha512() public pure {
+ // This test was generated automatically by gen_ref_data_hmac_sha512.py
+
+ bytes memory key0 = hex"010203";
+ bytes memory message0 = hex"040506";
+ bytes32 hmac0_1_expected = hex"d397d5a94c4f9b1ad5bd483f38d382c965c89e8129c2998d7202c530bf492c60";
+ bytes32 hmac0_2_expected = hex"a14ee3202d4bbab087dc9af4aacaede0f4b32dfdfc707756a12a7a750b79b5eb";
+ (bytes32 hmac0_1, bytes32 hmac0_2) = Hmac.hmacSha512(key0, message0);
+ assertEq(hmac0_1, hmac0_1_expected);
+ assertEq(hmac0_2, hmac0_2_expected);
+
+ bytes
+ memory key1 = hex"0101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101";
+ bytes memory message1 = hex"04";
+ bytes32 hmac1_1_expected = hex"c68f8756a0235a67f4f3a1c563025bc6c84389ffb80e4fe3a77344b14104c6d9";
+ bytes32 hmac1_2_expected = hex"7816a5522ce5c018d8ef7e8133eaa98579690f49d7e38038fbf13ce51898300e";
+ (bytes32 hmac1_1, bytes32 hmac1_2) = Hmac.hmacSha512(key1, message1);
+ assertEq(hmac1_1, hmac1_1_expected);
+ assertEq(hmac1_2, hmac1_2_expected);
+
+ bytes
+ memory key2 = hex"010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101";
+ bytes memory message2 = hex"04";
+ bytes32 hmac2_1_expected = hex"4440366771063083654ba2bc822768a9e16210b9443910c77a6fb5ef4978c0a9";
+ bytes32 hmac2_2_expected = hex"4c4cbf7bf68e3cd18acb16e3c98bdf6be1efdcb2519ee9c4d66eb8f92eac5838";
+ (bytes32 hmac2_1, bytes32 hmac2_2) = Hmac.hmacSha512(key2, message2);
+ assertEq(hmac2_1, hmac2_1_expected);
+ assertEq(hmac2_2, hmac2_2_expected);
+ }
+}
diff --git a/test/Sha512.t.sol b/test/Sha512.t.sol
new file mode 100644
index 0000000..59328cb
--- /dev/null
+++ b/test/Sha512.t.sol
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: MIT
+
+pragma solidity 0.8.27;
+
+import {Test, console} from "forge-std/Test.sol";
+import {Sha2Ext} from "../src/sha2/Sha2Ext.sol";
+
+contract Sha512Test is Test {
+ function testSha512() public pure {
+ // This test was generated automatically by gen_ref_data_sha512.py
+
+ bytes memory data0 = hex"";
+ bytes32 hash0_1_expected = hex"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce";
+ bytes32 hash0_2_expected = hex"47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e";
+ (bytes32 hash0_1, bytes32 hash0_2) = Sha2Ext.sha512(data0);
+ assertEq(hash0_1, hash0_1_expected);
+ assertEq(hash0_2, hash0_2_expected);
+
+ bytes memory data1 = hex"010203";
+ bytes32 hash1_1_expected = hex"27864cc5219a951a7a6e52b8c8dddf6981d098da1658d96258c870b2c88dfbcb";
+ bytes32 hash1_2_expected = hex"51841aea172a28bafa6a79731165584677066045c959ed0f9929688d04defc29";
+ (bytes32 hash1_1, bytes32 hash1_2) = Sha2Ext.sha512(data1);
+ assertEq(hash1_1, hash1_1_expected);
+ assertEq(hash1_2, hash1_2_expected);
+
+ bytes
+ memory data2 = hex"abababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab";
+ bytes32 hash2_1_expected = hex"fd805b4f843ccb741c22186ed4f6f0f12dafc2e6a330458fe84f9ecf1ad1a17c";
+ bytes32 hash2_2_expected = hex"a24489d6ae63662bc29f507bd00882573473ccc660ff05f818fe6a720632abfc";
+ (bytes32 hash2_1, bytes32 hash2_2) = Sha2Ext.sha512(data2);
+ assertEq(hash2_1, hash2_1_expected);
+ assertEq(hash2_2, hash2_2_expected);
+ }
+}