From d893b341cc62caf0e1d17aeb305b4f90f3ab6810 Mon Sep 17 00:00:00 2001 From: Mohsen Date: Thu, 6 Feb 2025 11:19:54 +0330 Subject: [PATCH] V4.1.0 - Added support for Sui addresses using Secp256k1, Secp256r1, Ed25519, and MultiSig. - Implemented support for Sui BIP44 coin derivation. --- CHANGELOG.md | 8 +- lib/bip/address/addr_dec_utils.dart | 8 +- lib/bip/address/aptos_addr.dart | 325 ++++++++-- lib/bip/address/decoders.dart | 4 + lib/bip/address/encoders.dart | 16 +- lib/bip/address/substrate_addr.dart | 2 +- lib/bip/address/sui.dart | 240 ++++++++ lib/bip/address/ton_addr.dart | 8 +- lib/bip/address/xlm_addr.dart | 2 +- lib/bip/address/xmr_addr.dart | 6 +- lib/bip/bip/bip32/base/bip32_base.dart | 23 +- lib/bip/bip/bip32/bip32.dart | 2 + lib/bip/bip/bip32/bip32_keys.dart | 27 +- .../bip32_slip10_mst_key_generator.dart | 7 +- .../slip10/bip32_slip10_nist256p1_hybrid.dart | 150 +++++ .../bip32/slip10/bip32_slip10_secp256k1.dart | 1 - lib/bip/bip/bip44/base/bip44_base.dart | 23 +- lib/bip/bip/bip44/bip44_base.dart | 3 +- lib/bip/bip/bip49/bip49_base.dart | 3 +- lib/bip/bip/bip84/bip84_base.dart | 3 +- lib/bip/bip/bip86/bip86_base.dart | 3 +- lib/bip/bip/conf/bip44/bip44_coins.dart | 21 + lib/bip/bip/conf/bip44/bip44_conf.dart | 67 +++ .../conf/config/bip_bitcoin_cash_conf.dart | 6 +- lib/bip/bip/conf/config/bip_coin_conf.dart | 8 +- .../bip/conf/config/bip_litecoin_conf.dart | 33 +- lib/bip/coin_conf/constant/coins_conf.dart | 8 +- lib/bip/ecc/bip_ecc.dart | 1 + lib/bip/ecc/curve/elliptic_curve_getter.dart | 1 + lib/bip/ecc/curve/elliptic_curve_types.dart | 48 +- lib/bip/ecc/keys/i_keys.dart | 9 + lib/bip/ecc/keys/nist256p1_keys_hybrid.dart | 202 +++++++ lib/bip/slip/slip44/slip44.dart | 3 + lib/cbor/extention/extenton.dart | 2 +- lib/cbor/utils/cbor_utils.dart | 2 +- lib/crypto/crypto/schnorrkel/keys/keys.dart | 2 +- lib/helper/extensions/extensions.dart | 45 +- lib/layout/constant/constant.dart | 80 +++ lib/layout/core/core.dart | 3 +- lib/layout/core/types/lazy_union.dart | 27 +- lib/layout/core/types/leb128.dart | 103 ++++ lib/layout/core/types/leb128_offset.dart | 70 +++ lib/layout/core/types/numeric.dart | 3 +- .../web3_storage_defination.dart | 16 +- lib/service/models/params.dart | 36 +- lib/service/utils/utils.dart | 55 +- lib/signer/{ => bitcoin}/bitcoin_signer.dart | 2 +- lib/signer/const/constants.dart | 7 + lib/signer/cosmos/cosmos.dart | 4 - .../cosmos/signers/cosmos_ed25519_signer.dart | 4 - .../signers/cosmos_eth_sec256k1_signer.dart | 71 --- .../ed25519.dart} | 46 +- lib/signer/eth/evm_signer.dart | 2 +- .../secp256k1_signer.dart} | 46 +- .../secp256r1_signer.dart} | 46 +- lib/signer/signer.dart | 11 +- .../{ => signing_key}/ecdsa_signing_key.dart | 0 .../substrate/signers/substrate_ecdsa.dart | 4 +- .../substrate/signers/substrate_eddsa.dart | 14 +- .../substrate/signers/substrate_sr25519.dart | 4 +- lib/signer/tron/tron_signer.dart | 2 +- lib/signer/{ => xrp}/xrp_signer.dart | 2 +- lib/utils/binary/binary_operation.dart | 3 + lib/utils/binary/utils.dart | 4 +- lib/utils/numbers/utils/bigint_utils.dart | 19 +- lib/utils/numbers/utils/int_utils.dart | 24 +- lib/utils/string/string.dart | 2 +- pubspec.yaml | 2 +- test/address/aptos/aptos_test.dart | 128 +++- test/address/aptos/test_vector.dart | 8 +- test/address/sui/sui_test.dart | 76 +++ test/address/sui/test_vector.dart | 553 ++++++++++++++++++ test/bip/bip44/test_vector.dart | 2 +- test/signer/bitcoin_test.dart | 2 +- 74 files changed, 2386 insertions(+), 417 deletions(-) create mode 100644 lib/bip/address/sui.dart create mode 100644 lib/bip/bip/bip32/slip10/bip32_slip10_nist256p1_hybrid.dart create mode 100644 lib/bip/ecc/keys/nist256p1_keys_hybrid.dart create mode 100644 lib/layout/core/types/leb128.dart create mode 100644 lib/layout/core/types/leb128_offset.dart rename lib/signer/{ => bitcoin}/bitcoin_signer.dart (99%) create mode 100644 lib/signer/const/constants.dart delete mode 100644 lib/signer/cosmos/cosmos.dart delete mode 100644 lib/signer/cosmos/signers/cosmos_ed25519_signer.dart delete mode 100644 lib/signer/cosmos/signers/cosmos_eth_sec256k1_signer.dart rename lib/signer/{solana/solana_signer.dart => ed25519/ed25519.dart} (70%) rename lib/signer/{cosmos/signers/cosmos_secp256k1_signer.dart => secp256k1/secp256k1_signer.dart} (68%) rename lib/signer/{cosmos/signers/cosmos_nist256r1_signer.dart => secp256r1/secp256r1_signer.dart} (71%) rename lib/signer/{ => signing_key}/ecdsa_signing_key.dart (100%) rename lib/signer/{ => xrp}/xrp_signer.dart (99%) create mode 100644 test/address/sui/sui_test.dart create mode 100644 test/address/sui/test_vector.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 3611054..32e49c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ +## 4.1.0 + +- Added support for Sui addresses using Secp256k1, Secp256r1, Ed25519, and MultiSig. +- Implemented support for Sui BIP44 coin derivation. + + ## 4.0.1 - - Fix schnorr vrf verification. +- Fix schnorr vrf verification. ## 4.0.0 diff --git a/lib/bip/address/addr_dec_utils.dart b/lib/bip/address/addr_dec_utils.dart index c9d7469..f21b025 100644 --- a/lib/bip/address/addr_dec_utils.dart +++ b/lib/bip/address/addr_dec_utils.dart @@ -22,16 +22,12 @@ class AddrDecUtils { } /// Validate and remove prefix from an address. - static String validateAndRemovePrefix( - String addr, - String prefix, - ) { + static String validateAndRemovePrefix(String addr, String prefix) { final prefixGot = addr.substring(0, prefix.length); if (prefix != prefixGot) { throw AddressConverterException( - 'Invalid prefix (expected $prefix, got $prefixGot)', - ); + 'Invalid prefix (expected $prefix, got $prefixGot)'); } return addr.substring(prefix.length); diff --git a/lib/bip/address/aptos_addr.dart b/lib/bip/address/aptos_addr.dart index 59231a9..2233a6e 100644 --- a/lib/bip/address/aptos_addr.dart +++ b/lib/bip/address/aptos_addr.dart @@ -1,89 +1,294 @@ -import 'package:blockchain_utils/bip/address/addr_dec_utils.dart'; import 'package:blockchain_utils/bip/address/decoder.dart'; import 'package:blockchain_utils/bip/address/encoder.dart'; -import 'package:blockchain_utils/bip/coin_conf/constant/coins_conf.dart'; +import 'package:blockchain_utils/bip/address/exception/exception.dart'; +import 'package:blockchain_utils/bip/bip.dart'; import 'package:blockchain_utils/crypto/quick_crypto.dart'; +import 'package:blockchain_utils/layout/layout.dart'; import 'package:blockchain_utils/utils/utils.dart'; import 'addr_key_validator.dart'; -/// Constants related to Aptos blockchain addresses. class AptosAddrConst { - /// The suffix byte used for single signature addresses. - static final singleSigSuffixByte = List.from([0x00]); + static const int specialAddressLastBytesMax = 16; + + /// aptos address bytes length + static const int addressBytesLength = 32; + + /// The Ed25519 signing scheme flag + static const int ed25519AddressFlag = 0; + + /// The multi-signature Ed25519 scheme flag + static const int multiEd25519AddressFlag = 1; + + /// A single key signing scheme flag (likely used for single-key accounts) + static const int signleKeyAddressFlag = 2; + + /// A multi-key signing scheme flag where multiple different types of keys are involved + static const int multikeyAddressFlag = 3; + + /// Max number of keys in the multi-signature account + static const int maximumPublicKeys = 32; + + /// Minimum number of keys required + static const int minPublicKeys = 2; + + /// Minimum threshold of required signatures + static const int minthreshold = 1; + + static const int shortAddressLength = 63; + + /// Bcs layout for encdoing multikey address + static final multiKeyAddressLayout = LayoutConst.struct([ + LayoutConst.bcsVector( + LayoutConst.bcsLazyEnum([ + LazyVariantModel( + layout: LayoutConst.bcsBytes, + property: EllipticCurveTypes.ed25519.name, + index: 0), + LazyVariantModel( + layout: LayoutConst.bcsBytes, + property: EllipticCurveTypes.secp256k1.name, + index: 1) + ], property: "pubKey"), + property: 'publicKeys'), + LayoutConst.u8(property: "requiredSignature") + ]); + + /// Bcs layout for encoding single key address + static final singleKeyAddressLayout = LayoutConst.bcsLazyEnum([ + LazyVariantModel( + layout: LayoutConst.bcsBytes, + property: EllipticCurveTypes.ed25519.name, + index: 0), + LazyVariantModel( + layout: LayoutConst.bcsBytes, + property: EllipticCurveTypes.secp256k1.name, + index: 1) + ]); +} + +class AptosAddressUtils { + /// check address bytes and convert special address to 32bytes. + static List praseAddressBytes(List bytes) { + final length = bytes.length; + if (length != AptosAddrConst.addressBytesLength) { + throw AddressConverterException("Invalid aptos address bytes length.", + details: { + "expected": AptosAddrConst.addressBytesLength, + "length": bytes.length + }); + } + + return bytes; + } + + /// convert address string to bytes with padding zero for special addresses. + static List addressToBytes(String address) { + address = StringUtils.strip0x(address); + List? bytes = BytesUtils.tryFromHexString(address, + paddingZero: address.length == 1 || + address.length == AptosAddrConst.shortAddressLength); + if (bytes == null || + (bytes.length != AptosAddrConst.addressBytesLength && + bytes.length != 1)) { + throw AddressConverterException("Invalid aptos address.", + details: {"address": address}); + } + if (bytes.length == 1) { + final byte = bytes[0]; + if (byte >= AptosAddrConst.specialAddressLastBytesMax) { + throw AddressConverterException("Invalid special address.", + details: {"address": BytesUtils.toHexString(bytes)}); + } + bytes = List.filled(AptosAddrConst.addressBytesLength, 0); + bytes.last = byte; + } + return praseAddressBytes(bytes); + } + + /// convert bytes (ED25519, Secp256k1 or multisig key data) to address with specify scheme + static List hashKeyBytes( + {required List bytes, required int scheme}) { + bytes = [...bytes, scheme]; + bytes = QuickCrypto.sha3256Hash(bytes); + return bytes; + } + + /// encode ED25519 public key to address + static List encodeEd25519Key(List bytes) { + try { + final key = AddrKeyValidator.validateAndGetEd25519Key(bytes) + .compressed + .sublist(1); + return hashKeyBytes( + bytes: key, scheme: AptosAddrConst.ed25519AddressFlag); + } catch (e) { + throw AddressConverterException( + "Failed to generate Aptos address: Invalid Ed25519 public key provided."); + } + } + + /// encode public key to SignleKey address + static List encodeSingleKey(IPublicKey publicKey) { + try { + final pubkeyBytes = switch (publicKey.curve) { + EllipticCurveTypes.secp256k1 => publicKey.uncompressed, + EllipticCurveTypes.ed25519 => publicKey.compressed.sublist(1), + _ => throw AddressConverterException( + "Unsupported public key: Aptos SingleKey can only be generated from secp256k1 or ed25519 public keys.") + }; + final structLayout = {publicKey.curve.name: pubkeyBytes}; + final encode = + AptosAddrConst.singleKeyAddressLayout.serialize(structLayout); + return hashKeyBytes( + bytes: encode, scheme: AptosAddrConst.signleKeyAddressFlag); + } on AddressConverterException { + rethrow; + } catch (e) { + throw AddressConverterException("Invalid aptos MultiKey address bytes.", + details: {"error": e.toString()}); + } + } + + /// encode Multi ED25519 public keys to MultiEd25519 address + static List encodeMultiEd25519Key( + List publicKeys, int threshold) { + try { + if (publicKeys.length < AptosAddrConst.minPublicKeys || + publicKeys.length > AptosAddrConst.maximumPublicKeys) { + throw AddressConverterException( + "The number of public keys provided is invalid. It must be between ${AptosAddrConst.minPublicKeys} and ${AptosAddrConst.maximumPublicKeys}."); + } + if (threshold < AptosAddrConst.minthreshold || + threshold > publicKeys.length) { + throw AddressConverterException( + "Invalid threshold. The threshold must be between ${AptosAddrConst.minthreshold} and the number of provided public keys (${publicKeys.length})."); + } + final keyBytes = [ + ...publicKeys.map((e) => e.compressed.sublist(1)).expand((e) => e), + threshold + ]; + return hashKeyBytes( + bytes: keyBytes, scheme: AptosAddrConst.multiEd25519AddressFlag); + } on AddressConverterException { + rethrow; + } catch (e) { + throw AddressConverterException( + "Invalid aptos MultiEd25519 address bytes.", + details: {"error": e.toString()}); + } + } + + /// encode Multi Public keys to MultiKey address + static List encodeMultiKey( + List publicKeys, int requiredSignature) { + try { + final pubkeyLayoutStruct = publicKeys.map((e) { + return switch (e.curve) { + EllipticCurveTypes.secp256k1 => {e.curve.name: e.uncompressed}, + EllipticCurveTypes.ed25519 => {e.curve.name: e.compressed.sublist(1)}, + _ => throw AddressConverterException( + "Unsupported public key: Aptos Multikey address can only be generated from secp256k1 or ed25519 public keys.") + }; + }).toList(); + if (publicKeys.length < AptosAddrConst.minPublicKeys || + publicKeys.length > AptosAddrConst.maximumPublicKeys) { + throw AddressConverterException( + "The number of public keys provided is invalid. It must be between ${AptosAddrConst.minPublicKeys} and ${AptosAddrConst.maximumPublicKeys}."); + } + if (requiredSignature < AptosAddrConst.minthreshold || + requiredSignature > publicKeys.length) { + throw AddressConverterException( + "Invalid threshold. The threshold must be between ${AptosAddrConst.minthreshold} and the number of provided public keys (${publicKeys.length})."); + } + final layoutStruct = { + "requiredSignature": requiredSignature, + "publicKeys": pubkeyLayoutStruct + }; + final encode = + AptosAddrConst.multiKeyAddressLayout.serialize(layoutStruct); + return hashKeyBytes( + bytes: encode, scheme: AptosAddrConst.multikeyAddressFlag); + } on AddressConverterException { + rethrow; + } catch (e) { + throw AddressConverterException("Invalid aptos MultiKey address bytes.", + details: {"error": e.toString()}); + } + } } /// Implementation of the [BlockchainAddressDecoder] for Aptos address. class AptosAddrDecoder implements BlockchainAddressDecoder { - /// Decode an Aptos blockchain address from its string representation. - /// - /// This method decodes the provided `addr` string by removing the prefix, - /// ensuring the address length is valid, and parsing the hexadecimal string - /// to obtain the address bytes. - /// - /// Parameters: - /// - `addr`: The Aptos blockchain address in string format. - /// - `kwargs` (optional): Additional arguments, though none are used in this method. - /// - /// Returns: - /// - A List containing the decoded address bytes. - /// - /// Throws: - /// - ArgumentException if the provided string is not a valid hex encoding. - /// /// This method is used to convert an Aptos blockchain address from its string /// representation to its binary format for further processing. @override List decodeAddr(String addr, [Map kwargs = const {}]) { - String addrNoPrefix = AddrDecUtils.validateAndRemovePrefix( - addr, - CoinsConf.aptos.params.addrPrefix!, - ); - addrNoPrefix = addrNoPrefix.padLeft(QuickCrypto.sha3256DigestSize * 2, "0"); - AddrDecUtils.validateLength( - addrNoPrefix, QuickCrypto.sha3256DigestSize * 2); - - return BytesUtils.fromHexString(addrNoPrefix); + return AptosAddressUtils.addressToBytes(addr); + } +} + +class AptosSingleKeyEd25519AddrEncoder implements BlockchainAddressEncoder { + /// This method is used to create an Aptos `SingleKey` address from `ED25519` public key. + @override + String encodeKey(List pubKey, [Map kwargs = const {}]) { + final publicKey = AddrKeyValidator.validateAndGetEd25519Key(pubKey); + final addressBytes = AptosAddressUtils.encodeSingleKey(publicKey); + + /// Concatenate the address prefix and the hash bytes, removing leading zeros + return BytesUtils.toHexString(addressBytes, + prefix: CoinsConf.aptos.params.addrPrefix); + } +} + +class AptosSingleKeySecp256k1AddrEncoder implements BlockchainAddressEncoder { + /// This method is used to create an Aptos `SingleKey` address from `Sec256k1` public key. + @override + String encodeKey(List pubKey, [Map kwargs = const {}]) { + final publicKey = AddrKeyValidator.validateAndGetSecp256k1Key(pubKey); + final addressBytes = AptosAddressUtils.encodeSingleKey(publicKey); + + /// Concatenate the address prefix and the hash bytes, removing leading zeros + return BytesUtils.toHexString(addressBytes, + prefix: CoinsConf.aptos.params.addrPrefix); } } /// Implementation of the [BlockchainAddressEncoder] for Aptos address. class AptosAddrEncoder implements BlockchainAddressEncoder { - /// Encode an Aptos blockchain address from a public key. - /// - /// This method encodes an Aptos blockchain address from the provided `pubKey` - /// by performing the following steps: - /// 1. Validate the public key and extract its raw compressed bytes. - /// 2. Prepare the payload by appending a single-sig suffix byte. - /// 3. Compute the SHA-3-256 hash of the payload. - /// 4. Concatenate the address prefix and the hash bytes. - /// 5. Remove leading zeros from the resulting hex-encoded address. - /// - /// Parameters: - /// - `pubKey`: The public key for which to generate the address. - /// - `kwargs` (optional): Additional arguments, though none are used in this method. - /// - /// Returns: - /// - A hex-encoded string representing the generated Aptos blockchain address. - /// - /// This method is used to create an Aptos blockchain address from a public key. - /// The resulting address is a hexadecimal string without leading zeros. + /// This method is used to create an Aptos `ED25519` address from public key. @override String encodeKey(List pubKey, [Map kwargs = const {}]) { - final pubKeyBytes = pubKey; - final pubKeyObj = AddrKeyValidator.validateAndGetEd25519Key(pubKeyBytes); + final addressBytes = AptosAddressUtils.encodeEd25519Key(pubKey); - /// Prepare the payload by appending a single-sig suffix byte - final payloadBytes = List.from([ - ...List.from(pubKeyObj.compressed.sublist(1)), - ...AptosAddrConst.singleSigSuffixByte - ]); + return BytesUtils.toHexString(addressBytes, + prefix: CoinsConf.aptos.params.addrPrefix); + } - /// Compute the SHA-3-256 hash of the payload - final keyHashBytes = QuickCrypto.sha3256Hash(payloadBytes); + /// This method is used to create an Aptos `SingleKey` address from (ED25519, Sec256k1) public key. + String encodeSingleKey(IPublicKey pubKey) { + final addressBytes = AptosAddressUtils.encodeSingleKey(pubKey); /// Concatenate the address prefix and the hash bytes, removing leading zeros - return CoinsConf.aptos.params.addrPrefix! + - BytesUtils.toHexString(keyHashBytes).replaceFirst(RegExp('^0+'), ''); + return BytesUtils.toHexString(addressBytes, + prefix: CoinsConf.aptos.params.addrPrefix); + } + + /// This method is used to create an Aptos `MultiEd25519` address from ED25519 public keys. + String encodeMultiEd25519Key( + {required List publicKeys, required int threshold}) { + final addressBytes = + AptosAddressUtils.encodeMultiEd25519Key(publicKeys, threshold); + return BytesUtils.toHexString(addressBytes, + prefix: CoinsConf.aptos.params.addrPrefix); + } + + /// This method is used to create an Aptos `MultiKey` address from (ED25519 or Secp256k1) public keys. + String encodeMultiKey( + {required List publicKeys, required int requiredSignature}) { + final addressBytes = + AptosAddressUtils.encodeMultiKey(publicKeys, requiredSignature); + return BytesUtils.toHexString(addressBytes, + prefix: CoinsConf.aptos.params.addrPrefix); } } diff --git a/lib/bip/address/decoders.dart b/lib/bip/address/decoders.dart index ec8b18c..74813e6 100644 --- a/lib/bip/address/decoders.dart +++ b/lib/bip/address/decoders.dart @@ -4,6 +4,10 @@ library; /// Export statement for Ada Byron address decoder. export 'ada/ada_byron_addr.dart' show AdaByronAddrDecoder; +/// Export statement for Sui address decoder. +export 'sui.dart' + show SuiAddrEncoder, SuiAddressUtils, SuiPublicKeyAndWeight, SuiAddrConst; + /// Export statements for Ada Shelley address decoders. export 'ada/ada.dart' show diff --git a/lib/bip/address/encoders.dart b/lib/bip/address/encoders.dart index 3606b1b..8478d98 100644 --- a/lib/bip/address/encoders.dart +++ b/lib/bip/address/encoders.dart @@ -5,6 +5,16 @@ library; export 'ada/ada_byron_addr.dart' show AdaByronIcarusAddrEncoder, AdaByronLegacyAddrEncoder; +/// Export statements for Sui address encoders. +export 'sui.dart' + show + SuiAddrEncoder, + SuiSecp256k1AddrEncoder, + SuiSecp256r1AddrEncoder, + SuiAddressUtils, + SuiPublicKeyAndWeight, + SuiAddrConst; + /// Export statements for Ada Shelley address encoders. export 'ada/ada.dart' show @@ -24,7 +34,11 @@ export 'ada/network.dart'; export 'algo_addr.dart' show AlgoAddrEncoder; /// Export statement for Aptos address encoder. -export 'aptos_addr.dart' show AptosAddrEncoder; +export 'aptos_addr.dart' + show + AptosAddrEncoder, + AptosSingleKeySecp256k1AddrEncoder, + AptosSingleKeyEd25519AddrEncoder; /// Export statement for Atom address encoder. export 'atom_addr.dart' diff --git a/lib/bip/address/substrate_addr.dart b/lib/bip/address/substrate_addr.dart index 14907e2..077aebb 100644 --- a/lib/bip/address/substrate_addr.dart +++ b/lib/bip/address/substrate_addr.dart @@ -31,7 +31,7 @@ class _SubstrateAddrUtils { static String encode(List pubKeyBytes, int ss58Format) { if (pubKeyBytes.length != encodeBytesLength) { throw AddressConverterException( - "Invalid pubkey length (excepted $encodeBytesLength, got ${pubKeyBytes.length}) "); + "Invalid pubkey length (expected $encodeBytesLength, got ${pubKeyBytes.length}) "); } return SS58Encoder.encode(pubKeyBytes, ss58Format); } diff --git a/lib/bip/address/sui.dart b/lib/bip/address/sui.dart new file mode 100644 index 0000000..0e281a4 --- /dev/null +++ b/lib/bip/address/sui.dart @@ -0,0 +1,240 @@ +import 'package:blockchain_utils/bip/address/decoder.dart'; +import 'package:blockchain_utils/bip/address/encoder.dart'; +import 'package:blockchain_utils/bip/address/exception/exception.dart'; +import 'package:blockchain_utils/bip/bip.dart'; +import 'package:blockchain_utils/crypto/quick_crypto.dart'; +import 'package:blockchain_utils/layout/layout.dart'; +import 'package:blockchain_utils/utils/utils.dart'; + +import 'addr_key_validator.dart'; + +class SuiPublicKeyAndWeight { + final IPublicKey publicKey; + final int weight; + const SuiPublicKeyAndWeight._( + {required this.publicKey, required this.weight}); + factory SuiPublicKeyAndWeight( + {required IPublicKey publicKey, required int weight}) { + if (weight < 1 || weight >= mask8) { + throw AddressConverterException( + "Invalid signer wieght. weight must be between 1 and $mask8 ."); + } + switch (publicKey.curve) { + case EllipticCurveTypes.ed25519: + case EllipticCurveTypes.secp256k1: + case EllipticCurveTypes.nist256p1: + break; + default: + throw AddressConverterException( + "Unsupported public key: sui Multikey address can only be generated from secp256k1, ed25519 or nist256p1 public keys."); + } + return SuiPublicKeyAndWeight._(publicKey: publicKey, weight: weight); + } + + List toBytes() { + final int flag = switch (publicKey.curve) { + EllipticCurveTypes.ed25519 => SuiAddrConst.ed25519AddressFlag, + EllipticCurveTypes.secp256k1 => SuiAddrConst.secp256k1AddressFlag, + _ => SuiAddrConst.secp256r1AddressFlag, + }; + List publicKeyBytes = publicKey.compressed; + if (publicKey.curve == EllipticCurveTypes.ed25519) { + publicKeyBytes = publicKeyBytes.sublist(1); + } + return [flag, ...publicKeyBytes, weight]; + } +} + +class SuiAddrConst { + static const int specialAddressLastBytesMax = 16; + + /// sui address bytes length + static const int addressBytesLength = 32; + + /// The Ed25519 signing scheme flag + static const int ed25519AddressFlag = 0; + + /// The secp256k1 scheme flag + static const int secp256k1AddressFlag = 1; + + /// The secp256r1 scheme flag + static const int secp256r1AddressFlag = 2; + + /// A multi-key signing scheme flag where multiple different types of keys are involved + static const int multisigAddressFlag = 3; +} + +class SuiAddressUtils { + /// check address bytes and convert special address to 32bytes. + static List praseAddressBytes(List bytes) { + if (bytes.length != SuiAddrConst.addressBytesLength) { + throw AddressConverterException("Invalid sui address bytes length.", + details: { + "expected": SuiAddrConst.addressBytesLength, + "length": bytes.length + }); + } + return bytes; + } + + /// convert address string to bytes without padding special addresses. + static List addressToBytes(String address) { + address = StringUtils.strip0x(address); + List? bytes = + BytesUtils.tryFromHexString(address, paddingZero: address.length < 2); + if (bytes?.length != SuiAddrConst.addressBytesLength) { + throw AddressConverterException("Invalid sui address.", + details: {"address": address}); + } + return bytes!; + } + + /// convert bytes (ED25519, Secp256k1 or multisig key data) to address with specify scheme + static List hashKeyBytes( + {required List bytes, required int scheme}) { + return QuickCrypto.blake2b256Hash([scheme, ...bytes]); + } + + /// encode ED25519 public key to address + static List encodeEd25519Key(List bytes) { + try { + final key = AddrKeyValidator.validateAndGetEd25519Key(bytes) + .compressed + .sublist(1); + return hashKeyBytes(bytes: key, scheme: SuiAddrConst.ed25519AddressFlag); + } catch (e) { + throw AddressConverterException( + "Failed to generate sui address: Invalid Ed25519 public key provided."); + } + } + + /// encode secp256k1 public key to address + static List encodeSecp256k1(List bytes) { + try { + final key = AddrKeyValidator.validateAndGetSecp256k1Key(bytes).compressed; + return hashKeyBytes( + bytes: key, scheme: SuiAddrConst.secp256k1AddressFlag); + } catch (e) { + throw AddressConverterException( + "Failed to generate sui address: Invalid secp256k1 public key provided."); + } + } + + /// encode secp256r1 public key to address + static List encodeSecp256r1(List bytes) { + try { + final key = AddrKeyValidator.validateAndGetNist256p1Key(bytes).compressed; + return hashKeyBytes( + bytes: key, scheme: SuiAddrConst.secp256r1AddressFlag); + } catch (e) { + throw AddressConverterException( + "Failed to generate sui address: Invalid secp256r1 public key provided."); + } + } + + /// encode Multi Public keys to MultiKey address + static List encodeMultiKey( + List publicKeys, int threshold) { + try { + if (publicKeys.isEmpty) { + throw AddressConverterException( + "at least one publickey required for multisig address."); + } + final keys = publicKeys.map((e) => e.publicKey).toSet(); + if (keys.length != publicKeys.length) { + throw AddressConverterException("Duplicate public key detected."); + } + + if (threshold < 1 || threshold >= mask16) { + throw AddressConverterException( + "Invalid threshold. threshold must be between 1 and $mask16 ."); + } + final sumWeight = publicKeys.fold(0, (p, c) => p + c.weight); + if (sumWeight < threshold) { + throw AddressConverterException( + "Sum of publickey weights must reach the threshold."); + } + final encode = publicKeys.map((e) => e.toBytes()).expand((e) => e); + return hashKeyBytes(bytes: [ + ...LayoutConst.u16().serialize(threshold), + ...encode, + ], scheme: SuiAddrConst.multisigAddressFlag); + } on AddressConverterException { + rethrow; + } catch (e) { + throw AddressConverterException("Invalid sui Multisig address bytes.", + details: {"error": e.toString()}); + } + } +} + +/// Implementation of the [BlockchainAddressDecoder] for sui address. +class SuiAddrDecoder implements BlockchainAddressDecoder { + /// This method is used to convert an sui blockchain address from its string + /// representation to its binary format for further processing. + @override + List decodeAddr(String addr, [Map kwargs = const {}]) { + final addressBytes = SuiAddressUtils.addressToBytes(addr); + return addressBytes; + } +} + +/// Implementation of the [BlockchainAddressEncoder] for sui address. +class SuiSecp256k1AddrEncoder implements BlockchainAddressEncoder { + /// encode secp256k1 public key to address + @override + String encodeKey(List pubKey, [Map kwargs = const {}]) { + final addressBytes = SuiAddressUtils.encodeSecp256k1(pubKey); + + return BytesUtils.toHexString(addressBytes, + prefix: CoinsConf.sui.params.addrPrefix); + } +} + +/// Implementation of the [BlockchainAddressEncoder] for sui address. +class SuiSecp256r1AddrEncoder implements BlockchainAddressEncoder { + /// encode secp256r1 public key to address + @override + String encodeKey(List pubKey, [Map kwargs = const {}]) { + final addressBytes = SuiAddressUtils.encodeSecp256r1(pubKey); + return BytesUtils.toHexString(addressBytes, + prefix: CoinsConf.sui.params.addrPrefix); + } +} + +/// Implementation of the [BlockchainAddressEncoder] for sui address. +class SuiAddrEncoder implements BlockchainAddressEncoder { + /// This method is used to create an sui `ED25519` address from public key. + @override + String encodeKey(List pubKey, [Map kwargs = const {}]) { + final addressBytes = SuiAddressUtils.encodeEd25519Key(pubKey); + + return BytesUtils.toHexString(addressBytes, + prefix: CoinsConf.sui.params.addrPrefix); + } + + /// encode secp256k1 public key to address + String encodeSecp256k1Key(List pubKey, + [Map kwargs = const {}]) { + final addressBytes = SuiAddressUtils.encodeSecp256k1(pubKey); + + return BytesUtils.toHexString(addressBytes, + prefix: CoinsConf.sui.params.addrPrefix); + } + + /// encode secp256r1 public key to address + String encodeSecp256r1Key(List pubKey, + [Map kwargs = const {}]) { + final addressBytes = SuiAddressUtils.encodeSecp256r1(pubKey); + return BytesUtils.toHexString(addressBytes, + prefix: CoinsConf.sui.params.addrPrefix); + } + + /// encode public keys to multisig address + String encodeMultisigKey( + {required List pubKey, required int threshold}) { + final addressBytes = SuiAddressUtils.encodeMultiKey(pubKey, threshold); + return BytesUtils.toHexString(addressBytes, + prefix: CoinsConf.sui.params.addrPrefix); + } +} diff --git a/lib/bip/address/ton_addr.dart b/lib/bip/address/ton_addr.dart index 9b14f04..9ba9b3a 100644 --- a/lib/bip/address/ton_addr.dart +++ b/lib/bip/address/ton_addr.dart @@ -99,7 +99,7 @@ class TonAddressUtils { final calcedCrc = Crc16.quickIntDigest(addr); if (!BytesUtils.bytesEqual(crc, calcedCrc)) { throw AddressConverterException("Invalid checksum", - details: {"excepted": calcedCrc, "checksum": crc}); + details: {"expected": calcedCrc, "checksum": crc}); } final List flags = []; // Parse tag @@ -180,7 +180,7 @@ class TonAddressUtils { static List validateAddressHash(List bytes) { if (bytes.length != _TonAddressConst.addressHashLength) { throw AddressConverterException("Invalid address hash length.", details: { - "excepted": _TonAddressConst.addressHashLength, + "expected": _TonAddressConst.addressHashLength, "length": bytes.length }); } @@ -196,7 +196,7 @@ class TonAddrDecoder implements BlockchainAddressDecoder { final decode = TonAddressUtils.decodeAddress(addr); if (workChain != null && workChain != decode.workchain) { throw AddressConverterException("Invalid address workchain.", - details: {"excepted": workChain, "workchain": decode.workchain}); + details: {"expected": workChain, "workchain": decode.workchain}); } return decode.hash; } @@ -208,7 +208,7 @@ class TonAddrDecoder implements BlockchainAddressDecoder { final decode = TonAddressUtils.decodeAddress(addr); if (workChain != null && workChain != decode.workchain) { throw AddressConverterException("Invalid address workchain.", - details: {"excepted": workChain, "workchain": decode.workchain}); + details: {"expected": workChain, "workchain": decode.workchain}); } return decode; } diff --git a/lib/bip/address/xlm_addr.dart b/lib/bip/address/xlm_addr.dart index a3b99cc..20bf5d2 100644 --- a/lib/bip/address/xlm_addr.dart +++ b/lib/bip/address/xlm_addr.dart @@ -54,7 +54,7 @@ class XlmAddrTypes { orElse: () => throw AddressConverterException( "Invalid or unsuported xlm address type.", details: { - "excepted": values.map((e) => e.value).join(", "), + "expected": values.map((e) => e.value).join(", "), "got": tag })); } diff --git a/lib/bip/address/xmr_addr.dart b/lib/bip/address/xmr_addr.dart index a451dce..9a25fbe 100644 --- a/lib/bip/address/xmr_addr.dart +++ b/lib/bip/address/xmr_addr.dart @@ -154,7 +154,7 @@ class _XmrAddrUtils { if (netVerBytes.length != XmrAddrConst.prefixLength || netVerBytes[0] != netVersion) { throw AddressConverterException("Invalid address prefix.", - details: {"excepted": netVersion, "network_version": netVersion}); + details: {"expected": netVersion, "network_version": netVersion}); } } @@ -184,7 +184,7 @@ class _XmrAddrUtils { if (paymentIdBytes != null && !BytesUtils.bytesEqual(paymentIdBytes, paymentBytes)) { throw AddressConverterException('Invalid payment ID.', details: { - "excepted": BytesUtils.toHexString(paymentIdBytes), + "expected": BytesUtils.toHexString(paymentIdBytes), "payment_id": BytesUtils.toHexString(paymentBytes) }); } @@ -194,7 +194,7 @@ class _XmrAddrUtils { payloadBytesWithoutPrefix, Ed25519KeysConst.pubKeyByteLen * 2); if (paymentIdBytes != null) { throw AddressConverterException('Invalid address type.', details: { - "excepted": XmrAddressType.integrated.toString(), + "expected": XmrAddressType.integrated.toString(), "type": addrType.toString() }); } diff --git a/lib/bip/bip/bip32/base/bip32_base.dart b/lib/bip/bip/bip32/base/bip32_base.dart index 3d5888c..1f582b4 100644 --- a/lib/bip/bip/bip32/base/bip32_base.dart +++ b/lib/bip/bip/bip32/base/bip32_base.dart @@ -197,13 +197,7 @@ abstract class Bip32Base { Bip32KeyNetVersions keyNetVer, EllipticCurveTypes curve) { if (privKey != null) { - final prv = Bip32PrivateKey.fromBytes( - privKey, - keyData, - keyNetVer, - curve, - ); - return prv; + return Bip32PrivateKey.fromBytes(privKey, keyData, keyNetVer, curve); } return null; } @@ -216,20 +210,11 @@ abstract class Bip32Base { Bip32KeyNetVersions keyNetVer, EllipticCurveTypes curve) { if (privKey != null) { - final bip32PrivateKey = Bip32PrivateKey.fromBytes( - privKey, - keyData, - keyNetVer, - curve, - ); + final bip32PrivateKey = + Bip32PrivateKey.fromBytes(privKey, keyData, keyNetVer, curve); return bip32PrivateKey.publicKey; } else { - return Bip32PublicKey.fromBytes( - pubKey!, - keyData, - keyNetVer, - curve, - ); + return Bip32PublicKey.fromBytes(pubKey!, keyData, keyNetVer, curve); } } diff --git a/lib/bip/bip/bip32/bip32.dart b/lib/bip/bip/bip32/bip32.dart index 3a1021f..dc5cbf4 100644 --- a/lib/bip/bip/bip32/bip32.dart +++ b/lib/bip/bip/bip32/bip32.dart @@ -39,6 +39,8 @@ export 'slip10/bip32_slip10_nist256p1.dart'; /// Export the Slip-10 BIP-32 implementation for SECG P-256k1. export 'slip10/bip32_slip10_secp256k1.dart'; +export 'slip10/bip32_slip10_nist256p1_hybrid.dart'; + /// Export statements for general BIP-32 components: /// Export BIP-32 path utilities for working with paths. export 'bip32_path.dart'; diff --git a/lib/bip/bip/bip32/bip32_keys.dart b/lib/bip/bip/bip32/bip32_keys.dart index c572468..2b00d09 100644 --- a/lib/bip/bip/bip32/bip32_keys.dart +++ b/lib/bip/bip/bip32/bip32_keys.dart @@ -77,17 +77,10 @@ class Bip32PublicKey extends Bip32KeyBase { } /// Creates a [Bip32PublicKey] from a byte representation, key data, key network versions, and curve type. - static Bip32PublicKey fromBytes( - List keyBytes, - Bip32KeyData keyData, - Bip32KeyNetVersions keyNetVer, - EllipticCurveTypes curveType, - ) { + static Bip32PublicKey fromBytes(List keyBytes, Bip32KeyData keyData, + Bip32KeyNetVersions keyNetVer, EllipticCurveTypes curveType) { return Bip32PublicKey( - IPublicKey.fromBytes(keyBytes, curveType), - keyData, - keyNetVer, - ); + IPublicKey.fromBytes(keyBytes, curveType), keyData, keyNetVer); } @override @@ -122,11 +115,7 @@ class Bip32PrivateKey extends Bip32KeyBase { /// Gets the corresponding public key derived from this private key. Bip32PublicKey get publicKey { - return Bip32PublicKey( - privKey.publicKey, - keyData, - keyNetVer, - ); + return Bip32PublicKey(privKey.publicKey, keyData, keyNetVer); } /// Gets the extended key of private key. @@ -136,12 +125,8 @@ class Bip32PrivateKey extends Bip32KeyBase { } /// Creates a Bip32PrivateKey from a byte representation, key data, key network versions, and curve type. - static Bip32PrivateKey fromBytes( - List keyBytes, - Bip32KeyData keyData, - Bip32KeyNetVersions keyNetVer, - EllipticCurveTypes curveType, - ) { + static Bip32PrivateKey fromBytes(List keyBytes, Bip32KeyData keyData, + Bip32KeyNetVersions keyNetVer, EllipticCurveTypes curveType) { return Bip32PrivateKey( IPrivateKey.fromBytes(keyBytes, curveType), keyData, keyNetVer); } diff --git a/lib/bip/bip/bip32/slip10/bip32_slip10_mst_key_generator.dart b/lib/bip/bip/bip32/slip10/bip32_slip10_mst_key_generator.dart index 10420dd..2d8c899 100644 --- a/lib/bip/bip/bip32/slip10/bip32_slip10_mst_key_generator.dart +++ b/lib/bip/bip/bip32/slip10/bip32_slip10_mst_key_generator.dart @@ -166,9 +166,8 @@ class Bip32Slip10Secp256k1MstKeyGenerator extends IBip32MstKeyGenerator { @override Tuple, List> generateFromSeed(List seedBytes) { return _Bip32Slip10MstKeyGenerator.generateFromSeed( - seedBytes, - List.from(Bip32Slip10MstKeyGeneratorConst.hmacKeySecp256k1Bytes), - EllipticCurveTypes.secp256k1, - ); + seedBytes, + List.from(Bip32Slip10MstKeyGeneratorConst.hmacKeySecp256k1Bytes), + EllipticCurveTypes.secp256k1); } } diff --git a/lib/bip/bip/bip32/slip10/bip32_slip10_nist256p1_hybrid.dart b/lib/bip/bip/bip32/slip10/bip32_slip10_nist256p1_hybrid.dart new file mode 100644 index 0000000..8824b2b --- /dev/null +++ b/lib/bip/bip/bip32/slip10/bip32_slip10_nist256p1_hybrid.dart @@ -0,0 +1,150 @@ +import 'package:blockchain_utils/bip/bip/bip32/base/bip32_base.dart'; +import 'package:blockchain_utils/bip/bip/bip32/base/ibip32_key_derivator.dart'; +import 'package:blockchain_utils/bip/bip/bip32/base/ibip32_mst_key_generator.dart'; +import 'package:blockchain_utils/bip/bip/bip32/bip32_const.dart'; +import 'package:blockchain_utils/bip/bip/bip32/bip32_ex.dart'; +import 'package:blockchain_utils/bip/bip/bip32/bip32_key_data.dart'; +import 'package:blockchain_utils/bip/bip/bip32/bip32_key_net_ver.dart'; +import 'package:blockchain_utils/bip/bip/bip32/bip32_keys.dart'; +import 'package:blockchain_utils/bip/bip/bip32/slip10/bip32_slip10_mst_key_generator.dart'; +import 'package:blockchain_utils/bip/ecc/curve/elliptic_curve_types.dart'; +import 'package:blockchain_utils/bip/ecc/keys/i_keys.dart'; + +import 'bip32_slip10_key_derivator.dart'; + +/// Represents a Bip32Slip10Nist256p1Hybrid hierarchical deterministic key for NIST256P1 curve. +/// +/// This class extends the [Bip32Base] class and provides functionality for +/// working with Bip32Slip10Nist256p1Hybrid keys. It is used for key derivation and +/// management within the NIST256P1 curve. +/// +/// Constructors: +/// - [Bip32Slip10Nist256p1Hybrid] constructor for creating an instance with provided key data. +/// - [Bip32Slip10Nist256p1Hybrid.fromSeed] constructor for creating a key from a seed. +/// - [Bip32Slip10Nist256p1Hybrid.fromPrivateKey] constructor for creating a key from a private key. +/// - [Bip32Slip10Nist256p1Hybrid.fromExtendedKey] constructor for creating a key from an extended key string. +/// +/// This class is specific to the NIST256P1 curve and provides necessary methods for +/// working with this curve, such as key derivation and management. +class Bip32Slip10Nist256p1Hybrid extends Bip32Base { + Bip32Slip10Nist256p1Hybrid._( + {required super.keyData, + required super.keyNetVer, + required super.privKey, + required super.pubKey}); + + /// constructor for creating a key from a seed. + Bip32Slip10Nist256p1Hybrid.fromSeed(super.seedBytes, [super.keyNetVer]) + : super.fromSeed(); + + /// constructor for creating a key from an extended key string. + Bip32Slip10Nist256p1Hybrid.fromExtendedKey(super.exKeyStr, [super.keyNetVer]) + : super.fromExtendedKey(); + + /// constructor for creating a key from a private key. + Bip32Slip10Nist256p1Hybrid.fromPrivateKey(List privKey, + {Bip32KeyData? keyData, Bip32KeyNetVersions? keyNetVer}) + : super.fromPrivateKey(privKey, keyData, keyNetVer); + + /// Derives a child key from the current key, based on the provided index. + /// + /// This method derives a child key from the current key, either privately + /// or publicly, depending on the input and key type. If the current key is + /// private, it can derive both private and public child keys. If the current + /// key is public, it can only derive public child keys. + /// + /// - [index]: The index used to derive the child key. + /// + /// If the current key is private: + /// - For non-hardened derivation, this method can derive both private and + /// public child keys. + /// - For hardened derivation, it can only derive private child keys. + /// + /// Returns a new key instance representing the derived child key. If public + /// derivation is not supported or if there's an issue with the derivation + /// process, an error is thrown. + @override + Bip32Slip10Nist256p1Hybrid childKey(Bip32KeyIndex index) { + final isPublic = isPublicOnly; + if (isPublicOnly) { + throw const Bip32KeyError( + 'Nist256p1 Hyprid Derivation without private key is not supported'); + } + final secpPublicKey = Bip32PublicKey( + IPrivateKey.fromBytes(privateKey.raw, EllipticCurveTypes.secp256k1) + .publicKey, + publicKey.keyData, + publicKey.keyNetVer); + // final secPP + if (!isPublic) { + if (!index.isHardened && !isPublicDerivationSupported) { + throw const Bip32KeyError( + 'Private child derivation with not-hardened index is not supported'); + } + final result = keyDerivator.ckdPriv( + privateKey, secpPublicKey, index, EllipticCurveTypes.secp256k1); + + return Bip32Slip10Nist256p1Hybrid._( + keyData: Bip32KeyData( + chainCode: Bip32ChainCode(result.item2), + depth: depth.increase(), + index: index, + parentFingerPrint: fingerPrint), + keyNetVer: keyNetVersions, + privKey: result.item1, + pubKey: null); + } + if (!isPublicDerivationSupported) { + throw const Bip32KeyError('Public child derivation is not supported'); + } + + if (index.isHardened) { + throw const Bip32KeyError( + "Public child derivation cannot be used to create an hardened child key"); + } + final result = + keyDerivator.ckdPub(secpPublicKey, index, EllipticCurveTypes.secp256k1); + return Bip32Slip10Nist256p1Hybrid._( + keyData: Bip32KeyData( + chainCode: Bip32ChainCode(result.item2), + depth: depth.increase(), + index: index, + parentFingerPrint: fingerPrint), + keyNetVer: keyNetVersions, + privKey: null, + pubKey: result.item1); + } + + /// Returns the curve type, NIST256P1. + @override + EllipticCurveTypes get curveType { + return EllipticCurveTypes.nist256p1Hybrid; + } + + /// Returns the default key network versions network. + /// + /// This method returns the default key network versions, + /// which are used when creating keys. + @override + Bip32KeyNetVersions get defaultKeyNetVersion { + return Bip32Const.mainNetKeyNetVersions; + } + + /// Returns the key derivator for NIST256P1. + /// + /// This method returns an instance of the [Bip32Slip10EcdsaDerivator] + /// class, which is used for key derivation within the Ed25519 curve. + @override + IBip32KeyDerivator get keyDerivator { + return Bip32Slip10EcdsaDerivator(); + } + + /// Returns the master key generator for NIST256P1. + /// + /// This method returns an instance of the [Bip32Slip10Secp256k1MstKeyGenerator] + /// class, which is used for generating the master key within the Ed25519 curve. + @override + IBip32MstKeyGenerator get masterKeyGenerator { + return Bip32Slip10Secp256k1MstKeyGenerator(); + } +} diff --git a/lib/bip/bip/bip32/slip10/bip32_slip10_secp256k1.dart b/lib/bip/bip/bip32/slip10/bip32_slip10_secp256k1.dart index 686f858..399fb76 100644 --- a/lib/bip/bip/bip32/slip10/bip32_slip10_secp256k1.dart +++ b/lib/bip/bip/bip32/slip10/bip32_slip10_secp256k1.dart @@ -112,7 +112,6 @@ class Bip32Slip10Secp256k1 extends Bip32Base { assert(!isPublicOnly); final result = keyDerivator.ckdPriv(privateKey, publicKey, index, curveType); - return Bip32Slip10Secp256k1._( keyData: Bip32KeyData( chainCode: Bip32ChainCode(result.item2), diff --git a/lib/bip/bip/bip44/base/bip44_base.dart b/lib/bip/bip/bip44/base/bip44_base.dart index 54a5b8a..07a2970 100644 --- a/lib/bip/bip/bip44/base/bip44_base.dart +++ b/lib/bip/bip/bip44/base/bip44_base.dart @@ -5,6 +5,7 @@ import 'package:blockchain_utils/bip/bip/bip32/khalow/bip32_kholaw_ed25519.dart' import 'package:blockchain_utils/bip/bip/bip32/slip10/bip32_slip10_ed25519.dart'; import 'package:blockchain_utils/bip/bip/bip32/slip10/bip32_slip10_ed25519_blake2b.dart'; import 'package:blockchain_utils/bip/bip/bip32/slip10/bip32_slip10_nist256p1.dart'; +import 'package:blockchain_utils/bip/bip/bip32/slip10/bip32_slip10_nist256p1_hybrid.dart'; import 'package:blockchain_utils/bip/bip/bip32/slip10/bip32_slip10_secp256k1.dart'; import 'package:blockchain_utils/bip/bip/bip44/base/bip44_base_ex.dart'; import 'package:blockchain_utils/bip/bip/conf/config/bip_coin_conf.dart'; @@ -111,6 +112,9 @@ abstract class Bip44Base { case EllipticCurveTypes.nist256p1: bip = Bip32Slip10Nist256p1.fromSeed(seedBytes, coin.keyNetVer); break; + case EllipticCurveTypes.nist256p1Hybrid: + bip = Bip32Slip10Nist256p1Hybrid.fromSeed(seedBytes, coin.keyNetVer); + break; default: throw ArgumentException("Bip44 does not supported ${coin.type}"); } @@ -145,8 +149,15 @@ abstract class Bip44Base { case EllipticCurveTypes.nist256p1: bip = Bip32Slip10Nist256p1.fromExtendedKey(extendedKey, coin.keyNetVer); break; + case EllipticCurveTypes.nist256p1Hybrid: + bip = Bip32Slip10Nist256p1Hybrid.fromExtendedKey( + extendedKey, coin.keyNetVer); + break; default: - throw const ArgumentException("invaid type"); + throw ArgumentException("Unsuported curve type.", details: { + "coin": coinConf.coinNames.name, + "curve": coinConf.type.toString() + }); } final validate = _validate(bip, coin); bip32 = validate.item1; @@ -184,7 +195,10 @@ abstract class Bip44Base { keyData: keyData, keyNetVer: coin.keyNetVer); break; default: - throw const ArgumentException("invaid type"); + throw ArgumentException("Unsuported curve type.", details: { + "coin": coinConf.coinNames.name, + "curve": coinConf.type.toString() + }); } final validate = _validate(bip, coin); bip32 = validate.item1; @@ -222,7 +236,10 @@ abstract class Bip44Base { keyData: keyData, keyNetVer: coin.keyNetVer); break; default: - throw const ArgumentException("invaid type"); + throw ArgumentException("Unsuported curve type.", details: { + "coin": coinConf.coinNames.name, + "curve": coinConf.type.toString() + }); } final validate = _validate(bip, coin); bip32 = validate.item1; diff --git a/lib/bip/bip/bip44/bip44_base.dart b/lib/bip/bip/bip44/bip44_base.dart index 3db88ed..ef728a3 100644 --- a/lib/bip/bip/bip44/bip44_base.dart +++ b/lib/bip/bip/bip44/bip44_base.dart @@ -44,7 +44,8 @@ class Bip44 extends Bip44Base { throw Bip44DepthError( "Current depth (${bip32.depth.toInt()}) is not suitable for deriving purpose"); } - return Bip44._(bip32.childKey(Bip44Const.purpose), coinConf); + return Bip44._( + bip32.childKey(coinConf.purpose ?? Bip44Const.purpose), coinConf); } /// derive default path diff --git a/lib/bip/bip/bip49/bip49_base.dart b/lib/bip/bip/bip49/bip49_base.dart index 79940ad..45d2b8d 100644 --- a/lib/bip/bip/bip49/bip49_base.dart +++ b/lib/bip/bip/bip49/bip49_base.dart @@ -98,7 +98,8 @@ class Bip49 extends Bip44Base { throw Bip44DepthError( "Current depth (${bip32.depth.toInt()}) is not suitable for deriving purpose"); } - return Bip49._(bip32.childKey(Bip49Const.purpose), coinConf); + return Bip49._( + bip32.childKey(coinConf.purpose ?? Bip49Const.purpose), coinConf); } /// derive coin diff --git a/lib/bip/bip/bip84/bip84_base.dart b/lib/bip/bip/bip84/bip84_base.dart index c5bfe1b..f83aa09 100644 --- a/lib/bip/bip/bip84/bip84_base.dart +++ b/lib/bip/bip/bip84/bip84_base.dart @@ -44,7 +44,8 @@ class Bip84 extends Bip44Base { throw Bip44DepthError( "Current depth (${bip32.depth.toInt()}) is not suitable for deriving purpose"); } - return Bip84._(bip32.childKey(Bip84Const.purpose), coinConf); + return Bip84._( + bip32.childKey(coinConf.purpose ?? Bip84Const.purpose), coinConf); } /// derive coin diff --git a/lib/bip/bip/bip86/bip86_base.dart b/lib/bip/bip/bip86/bip86_base.dart index 0b6713b..779b674 100644 --- a/lib/bip/bip/bip86/bip86_base.dart +++ b/lib/bip/bip/bip86/bip86_base.dart @@ -44,7 +44,8 @@ class Bip86 extends Bip44Base { throw Bip44DepthError( "Current depth (${bip32.depth.toInt()}) is not suitable for deriving purpose"); } - return Bip86._(bip32.childKey(Bip86Const.purpose), coinConf); + return Bip86._( + bip32.childKey(coinConf.purpose ?? Bip86Const.purpose), coinConf); } /// derive coin diff --git a/lib/bip/bip/conf/bip44/bip44_coins.dart b/lib/bip/bip/conf/bip44/bip44_coins.dart index 737129b..d4c2c36 100644 --- a/lib/bip/bip/conf/bip44/bip44_coins.dart +++ b/lib/bip/bip/conf/bip44/bip44_coins.dart @@ -73,6 +73,22 @@ class Bip44Coins extends BipCoins { /// Aptos static const aptos = Bip44Coins._('aptos'); + /// Aptos Ed25519 SingleKey + static const aptosEd25519SingleKey = Bip44Coins._('aptosEd25519SingleKey'); + + /// Aptos Secp256k1 SingleKey + static const aptosSecp256k1SingleKey = + Bip44Coins._('aptosSecp256k1SingleKey'); + + /// sui + static const sui = Bip44Coins._('sui'); + + /// sui Secp256k1 SingleKey + static const suiSecp256k1 = Bip44Coins._('suiSecp256k1'); + + /// sui Secp256R1 SingleKey + static const suiSecp256r1 = Bip44Coins._('suiSecp256r1'); + /// Avalanche C-Chain static const avaxCChain = Bip44Coins._('avaxCChain'); @@ -405,6 +421,11 @@ class Bip44Coins extends BipCoins { Bip44Coins.akashNetwork: Bip44Conf.akashNetwork, Bip44Coins.algorand: Bip44Conf.algorand, Bip44Coins.aptos: Bip44Conf.aptos, + Bip44Coins.aptosEd25519SingleKey: Bip44Conf.aptosSingleKeyEd25519, + Bip44Coins.aptosSecp256k1SingleKey: Bip44Conf.aptosSingleKeySecp256k1, + Bip44Coins.sui: Bip44Conf.suiEd25519, + Bip44Coins.suiSecp256k1: Bip44Conf.suiSecp256k1, + Bip44Coins.suiSecp256r1: Bip44Conf.suiSecp256r1, Bip44Coins.avaxCChain: Bip44Conf.avaxCChain, Bip44Coins.avaxPChain: Bip44Conf.avaxPChain, Bip44Coins.avaxXChain: Bip44Conf.avaxXChain, diff --git a/lib/bip/bip/conf/bip44/bip44_conf.dart b/lib/bip/bip/conf/bip44/bip44_conf.dart index 4028e04..65dc5a6 100644 --- a/lib/bip/bip/conf/bip44/bip44_conf.dart +++ b/lib/bip/bip/conf/bip44/bip44_conf.dart @@ -1,6 +1,7 @@ import 'package:blockchain_utils/bip/address/atom_addr.dart'; import 'package:blockchain_utils/bip/address/encoders.dart'; import 'package:blockchain_utils/bip/bip/bip32/bip32_const.dart'; +import 'package:blockchain_utils/bip/bip/bip32/bip32_key_data.dart'; import 'package:blockchain_utils/bip/bip/bip32/bip32_key_net_ver.dart'; import 'package:blockchain_utils/bip/bip/conf/config/bip_bitcoin_cash_conf.dart'; import 'package:blockchain_utils/bip/bip/conf/config/bip_coin_conf.dart'; @@ -63,6 +64,31 @@ class Bip44Conf { addrParams: {}, ); + /// Configuration for Aptos (Secp256k1) SingleKey Address + static final BipCoinConfig aptosSingleKeySecp256k1 = BipCoinConfig( + coinNames: CoinsConf.aptos.coinName, + coinIdx: Slip44.aptos, + chainType: ChainType.mainnet, + defPath: derPathNonHardenedFull, + keyNetVer: bip44BtcKeyNetVerMain, + wifNetVer: null, + type: EllipticCurveTypes.secp256k1, + addressEncoder: ([dynamic kwargs]) => AptosSingleKeySecp256k1AddrEncoder(), + addrParams: {}, + ); + + /// Configuration for Aptos (Ed25519) SingleKey Address + static final BipCoinConfig aptosSingleKeyEd25519 = BipCoinConfig( + coinNames: CoinsConf.aptos.coinName, + coinIdx: Slip44.aptos, + chainType: ChainType.mainnet, + defPath: derPathHardenedFull, + keyNetVer: bip44BtcKeyNetVerMain, + wifNetVer: null, + type: EllipticCurveTypes.ed25519, + addressEncoder: ([dynamic kwargs]) => AptosSingleKeyEd25519AddrEncoder(), + addrParams: {}); + /// Configuration for Avax C-Chain static final BipCoinConfig avaxCChain = BipCoinConfig( coinNames: CoinsConf.avaxCChain.coinName, @@ -1512,4 +1538,45 @@ class Bip44Conf { "net_ver": CoinsConf.electraProtocolTestNet.params.p2pkhNetVer! }, ); + + /// Configuration for Sui mainnet (Secp256k1) + static final BipCoinConfig suiSecp256k1 = BipCoinConfig( + coinNames: CoinsConf.sui.coinName, + coinIdx: Slip44.sui, + chainType: ChainType.mainnet, + defPath: derPathNonHardenedFull, + keyNetVer: bip44BtcKeyNetVerMain, + wifNetVer: null, + purpose: Bip32KeyIndex.hardenIndex(54), + type: EllipticCurveTypes.secp256k1, + addressEncoder: ([dynamic kwargs]) => SuiSecp256k1AddrEncoder(), + addrParams: {}, + ); + + /// Configuration for Sui mainnet (Secp256r1) + static final BipCoinConfig suiSecp256r1 = BipCoinConfig( + coinNames: CoinsConf.sui.coinName, + coinIdx: Slip44.sui, + chainType: ChainType.mainnet, + defPath: derPathNonHardenedFull, + purpose: Bip32KeyIndex.hardenIndex(74), + keyNetVer: bip44BtcKeyNetVerMain, + wifNetVer: null, + type: EllipticCurveTypes.nist256p1Hybrid, + addressEncoder: ([dynamic kwargs]) => SuiSecp256r1AddrEncoder(), + addrParams: {}, + ); + + /// Configuration for Sui mainnet (Ed25519) + static final BipCoinConfig suiEd25519 = BipCoinConfig( + coinNames: CoinsConf.sui.coinName, + coinIdx: Slip44.sui, + chainType: ChainType.mainnet, + defPath: derPathHardenedFull, + keyNetVer: bip44BtcKeyNetVerMain, + wifNetVer: null, + type: EllipticCurveTypes.ed25519, + addressEncoder: ([dynamic kwargs]) => SuiAddrEncoder(), + addrParams: {}, + ); } diff --git a/lib/bip/bip/conf/config/bip_bitcoin_cash_conf.dart b/lib/bip/bip/conf/config/bip_bitcoin_cash_conf.dart index f5061ba..6712398 100644 --- a/lib/bip/bip/conf/config/bip_bitcoin_cash_conf.dart +++ b/lib/bip/bip/conf/config/bip_bitcoin_cash_conf.dart @@ -1,4 +1,5 @@ import 'package:blockchain_utils/bip/address/encoder.dart'; +import 'package:blockchain_utils/bip/bip/bip32/bip32_key_data.dart'; import 'package:blockchain_utils/bip/bip/bip32/bip32_key_net_ver.dart'; import 'package:blockchain_utils/bip/bip/conf/config/bip_coin_conf.dart'; import 'package:blockchain_utils/bip/bip/conf/core/coin_conf.dart'; @@ -28,6 +29,7 @@ class BipBitcoinCashConf extends BipCoinConfig { required super.type, required super.addressEncoder, required super.addrParams, + super.purpose, this.useLagacyAdder = false, }); @@ -55,6 +57,7 @@ class BipBitcoinCashConf extends BipCoinConfig { EllipticCurveTypes? type, AddrEncoder? addressEncoder, bool? useLagacyAdder, + Bip32KeyIndex? purpose, }) { return BipBitcoinCashConf( coinNames: coinNames ?? this.coinNames, @@ -66,6 +69,7 @@ class BipBitcoinCashConf extends BipCoinConfig { addrParams: addrParams ?? this.addrParams, type: type ?? this.type, addressEncoder: addressEncoder ?? this.addressEncoder, - useLagacyAdder: useLagacyAdder ?? this.useLagacyAdder); + useLagacyAdder: useLagacyAdder ?? this.useLagacyAdder, + purpose: purpose ?? this.purpose); } } diff --git a/lib/bip/bip/conf/config/bip_coin_conf.dart b/lib/bip/bip/conf/config/bip_coin_conf.dart index 38ea2ee..26bca5b 100644 --- a/lib/bip/bip/conf/config/bip_coin_conf.dart +++ b/lib/bip/bip/conf/config/bip_coin_conf.dart @@ -53,6 +53,7 @@ */ import 'package:blockchain_utils/bip/address/encoder.dart'; +import 'package:blockchain_utils/bip/bip/bip32/bip32_key_data.dart'; import 'package:blockchain_utils/bip/bip/bip32/bip32_key_net_ver.dart'; import 'package:blockchain_utils/bip/bip/bip32/bip32_keys.dart'; import 'package:blockchain_utils/bip/bip/conf/core/coin_conf.dart'; @@ -84,6 +85,8 @@ class BipCoinConfig implements CoinConfig { @override final EllipticCurveTypes type; + final Bip32KeyIndex? purpose; + /// Creates a copy of the BipCoinConfig object with optional properties updated. BipCoinConfig copy({ CoinNames? coinNames, @@ -95,6 +98,7 @@ class BipCoinConfig implements CoinConfig { Map? addrParams, EllipticCurveTypes? type, AddrEncoder? addressEncoder, + Bip32KeyIndex? purpose, }) { return BipCoinConfig( coinNames: coinNames ?? this.coinNames, @@ -105,7 +109,8 @@ class BipCoinConfig implements CoinConfig { wifNetVer: wifNetVer ?? this.wifNetVer, addrParams: addrParams ?? this.addrParams, type: type ?? this.type, - addressEncoder: addressEncoder ?? this.addressEncoder); + addressEncoder: addressEncoder ?? this.addressEncoder, + purpose: purpose ?? this.purpose); } /// Constructor for BipCoinConfig. @@ -119,6 +124,7 @@ class BipCoinConfig implements CoinConfig { required this.addrParams, required this.type, required this.addressEncoder, + this.purpose, }); /// Get address parameters with optional chain code inclusion. diff --git a/lib/bip/bip/conf/config/bip_litecoin_conf.dart b/lib/bip/bip/conf/config/bip_litecoin_conf.dart index 25088f8..47fe402 100644 --- a/lib/bip/bip/conf/config/bip_litecoin_conf.dart +++ b/lib/bip/bip/conf/config/bip_litecoin_conf.dart @@ -1,3 +1,4 @@ +import 'package:blockchain_utils/bip/bip/bip32/bip32_key_data.dart'; import 'package:blockchain_utils/bip/bip/bip32/bip32_key_net_ver.dart'; import 'package:blockchain_utils/bip/bip/conf/config/bip_coin_conf.dart'; import 'package:blockchain_utils/bip/bip/conf/core/coin_conf.dart'; @@ -12,20 +13,20 @@ class BipLitecoinConf extends BipCoinConfig { final bool useAltKeyNetVer; /// Constructor for BipLitecoinConf. - const BipLitecoinConf({ - required super.coinNames, - required super.coinIdx, - required super.chainType, - required super.defPath, - required super.keyNetVer, - required super.wifNetVer, - required super.type, - required super.addressEncoder, - required super.addrParams, - required this.altKeyNetVer, - this.useAltKeyNetVer = false, - this.useDeprAddress = false, - }); + const BipLitecoinConf( + {required super.coinNames, + required super.coinIdx, + required super.chainType, + required super.defPath, + required super.keyNetVer, + required super.wifNetVer, + required super.type, + required super.addressEncoder, + required super.addrParams, + required this.altKeyNetVer, + this.useAltKeyNetVer = false, + this.useDeprAddress = false, + super.purpose}); /// Overrides the 'addrParams' getter to return the appropriate address parameters /// based on the 'useDeprAddress' flag. @@ -59,6 +60,7 @@ class BipLitecoinConf extends BipCoinConfig { AddrEncoder? addressEncoder, bool? useAltKeyNetVer, bool? useDeprAddress, + Bip32KeyIndex? purpose, }) { return BipLitecoinConf( coinNames: coinNames ?? this.coinNames, @@ -72,6 +74,7 @@ class BipLitecoinConf extends BipCoinConfig { addressEncoder: addressEncoder ?? this.addressEncoder, altKeyNetVer: altKeyNetVer ?? this.altKeyNetVer, useAltKeyNetVer: useAltKeyNetVer ?? this.useAltKeyNetVer, - useDeprAddress: useDeprAddress ?? this.useDeprAddress); + useDeprAddress: useDeprAddress ?? this.useDeprAddress, + purpose: purpose ?? this.purpose); } } diff --git a/lib/bip/coin_conf/constant/coins_conf.dart b/lib/bip/coin_conf/constant/coins_conf.dart index 15f5c6b..39e0a90 100644 --- a/lib/bip/coin_conf/constant/coins_conf.dart +++ b/lib/bip/coin_conf/constant/coins_conf.dart @@ -44,9 +44,11 @@ class CoinsConf { /// Configuration for Aptos static const CoinConf aptos = CoinConf( coinName: CoinNames("Aptos", "APTOS"), - params: CoinParams( - addrPrefix: "0x", - )); + params: CoinParams(addrPrefix: "0x")); + + /// Configuration for SUI + static const CoinConf sui = CoinConf( + coinName: CoinNames("Sui", "SUI"), params: CoinParams(addrPrefix: "0x")); /// Configuration for Avax C-Chain static const CoinConf avaxCChain = CoinConf( diff --git a/lib/bip/ecc/bip_ecc.dart b/lib/bip/ecc/bip_ecc.dart index 6c48365..ebea16e 100644 --- a/lib/bip/ecc/bip_ecc.dart +++ b/lib/bip/ecc/bip_ecc.dart @@ -9,3 +9,4 @@ export 'keys/i_keys.dart'; export 'keys/nist256p1_keys.dart'; export 'keys/secp256k1_keys_ecdsa.dart'; export 'keys/sr25519_keys.dart'; +export 'keys/nist256p1_keys_hybrid.dart'; diff --git a/lib/bip/ecc/curve/elliptic_curve_getter.dart b/lib/bip/ecc/curve/elliptic_curve_getter.dart index ff0ffec..5cfc3cf 100644 --- a/lib/bip/ecc/curve/elliptic_curve_getter.dart +++ b/lib/bip/ecc/curve/elliptic_curve_getter.dart @@ -15,6 +15,7 @@ class EllipticCurveGetter { case EllipticCurveTypes.secp256k1: return Curves.generatorSecp256k1; case EllipticCurveTypes.nist256p1: + case EllipticCurveTypes.nist256p1Hybrid: return Curves.generator256; case EllipticCurveTypes.ed25519: case EllipticCurveTypes.ed25519Kholaw: diff --git a/lib/bip/ecc/curve/elliptic_curve_types.dart b/lib/bip/ecc/curve/elliptic_curve_types.dart index a36dfdc..1563636 100644 --- a/lib/bip/ecc/curve/elliptic_curve_types.dart +++ b/lib/bip/ecc/curve/elliptic_curve_types.dart @@ -1,52 +1,34 @@ -import 'package:blockchain_utils/blockchain_utils.dart'; - /// An enumeration of common elliptic curve types used in cryptographic operations. -class EllipticCurveTypes { +enum EllipticCurveTypes { /// Edwards-curve Digital Signature Algorithm (EdDSA) using ed25519 curve - static const EllipticCurveTypes ed25519 = EllipticCurveTypes._('ed25519'); + ed25519, /// EdDSA with Blake2b hash - static const EllipticCurveTypes ed25519Blake2b = - EllipticCurveTypes._('ed25519Blake2b'); + ed25519Blake2b, /// EdDSA with Kholaw's 25519 curve - static const EllipticCurveTypes ed25519Kholaw = - EllipticCurveTypes._('ed25519Kholaw'); + ed25519Kholaw, /// EdDSA curve used in Monero - static const EllipticCurveTypes ed25519Monero = - EllipticCurveTypes._('ed25519Monero'); + ed25519Monero, /// NIST P-256 elliptic curve - static const EllipticCurveTypes nist256p1 = EllipticCurveTypes._('nist256p1'); + nist256p1, + + /// Hybrid derivation using NIST P-256 elliptic curve + nist256p1Hybrid, /// SECG secp256k1 elliptic curve - static const EllipticCurveTypes secp256k1 = EllipticCurveTypes._('secp256k1'); + secp256k1, /// Schnorr over Ristretto255 curve - static const EllipticCurveTypes sr25519 = EllipticCurveTypes._('sr25519'); - - final String name; - - const EllipticCurveTypes._(this.name); - static const List values = [ - ed25519, - ed25519Blake2b, - ed25519Kholaw, - ed25519Monero, - nist256p1, - secp256k1, - sr25519, - ]; + sr25519; + /// Retrieves the [EllipticCurveTypes] from its string [name] static EllipticCurveTypes fromName(String name) { return EllipticCurveTypes.values.firstWhere( - (element) => element.name == name, - orElse: () => throw MessageException("Invalid curve type name. $name")); - } - - @override - String toString() { - return "EllipticCurveTypes.$name"; + (element) => element.name == name, + orElse: () => throw ArgumentError('Invalid curve type name: $name'), + ); } } diff --git a/lib/bip/ecc/keys/i_keys.dart b/lib/bip/ecc/keys/i_keys.dart index 77d9bc6..f841786 100644 --- a/lib/bip/ecc/keys/i_keys.dart +++ b/lib/bip/ecc/keys/i_keys.dart @@ -3,6 +3,7 @@ import 'package:blockchain_utils/bip/ecc/keys/ed25519_blake2b_keys.dart'; import 'package:blockchain_utils/bip/ecc/keys/ed25519_kholaw_keys.dart'; import 'package:blockchain_utils/bip/ecc/keys/ed25519_monero_keys.dart'; import 'package:blockchain_utils/bip/ecc/keys/nist256p1_keys.dart'; +import 'package:blockchain_utils/bip/ecc/keys/nist256p1_keys_hybrid.dart'; import 'package:blockchain_utils/bip/ecc/keys/secp256k1_keys_ecdsa.dart'; import 'package:blockchain_utils/bip/ecc/keys/sr25519_keys.dart'; import 'package:blockchain_utils/bip/ecc/curve/elliptic_curve_types.dart'; @@ -16,6 +17,8 @@ abstract class IPublicKey { switch (type) { case EllipticCurveTypes.nist256p1: return Nist256p1PublicKey.fromBytes(keybytes); + case EllipticCurveTypes.nist256p1Hybrid: + return Nist256p1HybridPublicKey.fromBytes(keybytes); case EllipticCurveTypes.sr25519: return Sr25519PublicKey.fromBytes(keybytes); case EllipticCurveTypes.ed25519: @@ -39,6 +42,8 @@ abstract class IPublicKey { switch (type) { case EllipticCurveTypes.nist256p1: return Nist256p1PublicKey.isValidBytes(keyBytes); + case EllipticCurveTypes.nist256p1Hybrid: + return Nist256p1HybridPublicKey.isValidBytes(keyBytes); case EllipticCurveTypes.sr25519: return Sr25519PublicKey.isValidBytes(keyBytes); case EllipticCurveTypes.ed25519: @@ -86,6 +91,8 @@ abstract class IPrivateKey { switch (type) { case EllipticCurveTypes.nist256p1: return Nist256p1PrivateKey.fromBytes(keyBytes); + case EllipticCurveTypes.nist256p1Hybrid: + return Nist256p1HybridPrivateKey.fromBytes(keyBytes); case EllipticCurveTypes.ed25519: return Ed25519PrivateKey.fromBytes(keyBytes); case EllipticCurveTypes.ed25519Kholaw: @@ -120,6 +127,8 @@ abstract class IPrivateKey { switch (type) { case EllipticCurveTypes.nist256p1: return Nist256p1PrivateKey.isValidBytes(keyBytes); + case EllipticCurveTypes.nist256p1Hybrid: + return Nist256p1HybridPrivateKey.isValidBytes(keyBytes); case EllipticCurveTypes.ed25519: return Ed25519PrivateKey.isValidBytes(keyBytes); case EllipticCurveTypes.ed25519Kholaw: diff --git a/lib/bip/ecc/keys/nist256p1_keys_hybrid.dart b/lib/bip/ecc/keys/nist256p1_keys_hybrid.dart new file mode 100644 index 0000000..0ba6de2 --- /dev/null +++ b/lib/bip/ecc/keys/nist256p1_keys_hybrid.dart @@ -0,0 +1,202 @@ +/* + The MIT License (MIT) + + Copyright (c) 2021 Emanuele Bellocchia + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deals + 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. + + Note: This code has been adapted from its original Python version to Dart. +*/ + +/* + The 3-Clause BSD License + + Copyright (c) 2023 Mohsen Haydari (MRTNETWORK) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions, and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, this + list of conditions, and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. Neither the name of the [organization] nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +import 'package:blockchain_utils/utils/utils.dart'; +import 'package:blockchain_utils/bip/ecc/keys/ecdsa_keys.dart'; +import 'package:blockchain_utils/bip/ecc/keys/i_keys.dart'; +import 'package:blockchain_utils/bip/ecc/curve/elliptic_curve_types.dart'; +import 'package:blockchain_utils/crypto/crypto/cdsa/curve/curves.dart'; +import 'package:blockchain_utils/crypto/crypto/cdsa/ecdsa/private_key.dart'; +import 'package:blockchain_utils/crypto/crypto/cdsa/ecdsa/public_key.dart'; +import 'package:blockchain_utils/crypto/crypto/cdsa/point/base.dart'; +import 'package:blockchain_utils/crypto/crypto/cdsa/point/ec_projective_point.dart'; + +/// A class representing a NIST P-256 public key that implements the IPublicKey interface. +class Nist256p1HybridPublicKey implements IPublicKey { + final ECDSAPublicKey publicKey; + + /// Private constructor for creating a Nist256p1HybridPublicKey instance from an ECDSAPublicKey. + Nist256p1HybridPublicKey._(this.publicKey); + + /// Factory method for creating a Nist256p1HybridPublicKey from a byte array. + factory Nist256p1HybridPublicKey.fromBytes(List keyBytes) { + final point = ProjectiveECCPoint.fromBytes( + curve: Curves.curve256, data: keyBytes, order: null); + final pub = ECDSAPublicKey(Curves.generator256, point); + return Nist256p1HybridPublicKey._(pub); + } + + /// public key compressed bytes length. + @override + int get length { + return EcdsaKeysConst.pubKeyCompressedByteLen; + } + + /// curve type + @override + EllipticCurveTypes get curve { + return EllipticCurveTypes.nist256p1Hybrid; + } + + /// check if bytes is valid for this key. + static bool isValidBytes(List keyBytes) { + try { + Nist256p1HybridPublicKey.fromBytes(keyBytes); + return true; + // ignore: empty_catches + } catch (e) {} + return false; + } + + /// public key point. + @override + ProjectiveECCPoint get point { + return publicKey.point; + } + + /// public key compressed bytes. + @override + List get compressed { + return publicKey.point.toBytes(EncodeType.comprossed); + } + + /// public key uncompressed bytes. + @override + List get uncompressed { + return publicKey.point.toBytes(EncodeType.uncompressed); + } + + @override + int get uncompressedLength { + return EcdsaKeysConst.pubKeyUncompressedByteLen; + } + + @override + String toHex( + {bool withPrefix = true, bool lowerCase = true, String? prefix = ""}) { + return BytesUtils.toHexString(compressed, + prefix: prefix, lowerCase: lowerCase); + } + + @override + operator ==(other) { + if (other is! Nist256p1HybridPublicKey) return false; + return publicKey == other.publicKey && curve == other.curve; + } + + @override + int get hashCode => publicKey.hashCode ^ curve.hashCode; +} + +/// A class representing a NIST P-256 private key that implements the IPrivateKey interface. +class Nist256p1HybridPrivateKey implements IPrivateKey { + final ECDSAPrivateKey privateKey; + + /// Private constructor for creating a Nist256p1HybridPrivateKey instance from an ECDSAPrivateKey. + Nist256p1HybridPrivateKey._(this.privateKey); + + /// Factory method for creating a Nist256p1HybridPrivateKey from a byte array. + factory Nist256p1HybridPrivateKey.fromBytes(List keyBytes) { + final prv = ECDSAPrivateKey.fromBytes(keyBytes, Curves.generator256); + return Nist256p1HybridPrivateKey._(prv); + } + + /// curve type. + @override + EllipticCurveTypes get curve { + return EllipticCurveTypes.nist256p1Hybrid; + } + + /// check if bytes is valid for this key. + static bool isValidBytes(List keyBytes) { + try { + ECDSAPrivateKey.fromBytes(keyBytes, Curves.generator256); + return true; + } catch (e) { + return false; + } + } + + /// private key bytes length + @override + int get length { + return EcdsaKeysConst.privKeyByteLen; + } + + /// accsess to public key + @override + IPublicKey get publicKey { + return Nist256p1HybridPublicKey._(privateKey.publicKey); + } + + /// private key raw bytes + @override + List get raw { + return privateKey.toBytes(); + } + + @override + String toHex({bool lowerCase = true, String? prefix = ""}) { + return BytesUtils.toHexString(raw, lowerCase: lowerCase, prefix: prefix); + } + + @override + operator ==(other) { + if (other is! Nist256p1HybridPrivateKey) return false; + return privateKey == other.privateKey && curve == other.curve; + } + + @override + int get hashCode => privateKey.hashCode ^ curve.hashCode; +} diff --git a/lib/bip/slip/slip44/slip44.dart b/lib/bip/slip/slip44/slip44.dart index eaddafd..edb8c3a 100644 --- a/lib/bip/slip/slip44/slip44.dart +++ b/lib/bip/slip/slip44/slip44.dart @@ -104,4 +104,7 @@ class Slip44 { static const int piNetwork = 314159; static const int pepecoin = 3434; static const int electraProtocol = 597; + + /// sui + static const int sui = 784; } diff --git a/lib/cbor/extention/extenton.dart b/lib/cbor/extention/extenton.dart index 20dc6af..366dead 100644 --- a/lib/cbor/extention/extenton.dart +++ b/lib/cbor/extention/extenton.dart @@ -4,6 +4,6 @@ extension QuickCastingCbor on CborObject { T cast() { if (this is T) return this as T; throw CborException("cbor object casting faild", - details: {"excepted": "$T", "value": runtimeType.toString()}); + details: {"expected": "$T", "value": runtimeType.toString()}); } } diff --git a/lib/cbor/utils/cbor_utils.dart b/lib/cbor/utils/cbor_utils.dart index d241964..296eb70 100644 --- a/lib/cbor/utils/cbor_utils.dart +++ b/lib/cbor/utils/cbor_utils.dart @@ -144,7 +144,7 @@ class CborUtils { } if (value is! T) { throw CborException("decode length casting faild.", - details: {"excepted": "$T", "value": value.runtimeType}); + details: {"expected": "$T", "value": value.runtimeType}); } return _DecodeCborResult(value: value as T, consumed: consumed); } diff --git a/lib/crypto/crypto/schnorrkel/keys/keys.dart b/lib/crypto/crypto/schnorrkel/keys/keys.dart index 9113e80..a3d8203 100644 --- a/lib/crypto/crypto/schnorrkel/keys/keys.dart +++ b/lib/crypto/crypto/schnorrkel/keys/keys.dart @@ -129,7 +129,7 @@ class VRFPreOut { factory VRFPreOut(List bytes) { if (bytes.length != SchnorrkelKeyCost.vrfPreOutLength) { throw ArgumentException( - "Invalid VRFPreOut bytes length. excepted: ${SchnorrkelKeyCost.vrfPreOutLength} got: ${bytes.length}"); + "Invalid VRFPreOut bytes length. expected: ${SchnorrkelKeyCost.vrfPreOutLength} got: ${bytes.length}"); } return VRFPreOut._(output: bytes); } diff --git a/lib/helper/extensions/extensions.dart b/lib/helper/extensions/extensions.dart index 09b269a..681a6ce 100644 --- a/lib/helper/extensions/extensions.dart +++ b/lib/helper/extensions/extensions.dart @@ -34,7 +34,7 @@ extension ListHelper on List { List exceptedLen(int len, {String? message}) { if (length != len) { throw ArgumentException(message ?? 'Invalid length. ', - details: {"excepted": len, "length": length}); + details: {"expected": len, "length": length}); } return this; } @@ -86,10 +86,32 @@ extension MapHelper on Map { } extension BigIntHelper on BigInt { + BigInt get asUint256 { + if (isNegative || this > maxU256) { + throw ArgumentException("Invalid Unsigned BigInt 256.", details: { + "expected": maxU256.bitLength, + "bitLength": bitLength, + "value": toString() + }); + } + return this; + } + + BigInt get asUint128 { + if (isNegative || this > maxU128) { + throw ArgumentException("Invalid Unsigned BigInt 128.", details: { + "expected": maxU128.bitLength, + "bitLength": bitLength, + "value": toString() + }); + } + return this; + } + BigInt get asUint64 { if (isNegative || this > maxU64) { throw ArgumentException("Invalid Unsigned BigInt 64.", details: { - "excepted": maxU64.bitLength, + "expected": maxU64.bitLength, "bitLength": bitLength, "value": toString() }); @@ -100,7 +122,7 @@ extension BigIntHelper on BigInt { BigInt get asInt64 { if (this > maxInt64 || this < minInt64) { throw ArgumentException("Invalid Signed BigInt 64.", details: { - "excepted": maxU64.bitLength, + "expected": maxU64.bitLength, "bitLength": bitLength, "value": toString() }); @@ -113,7 +135,7 @@ extension IntHelper on int { int get asInt32 { if (this > maxInt32 || this < minInt32) { throw ArgumentException("Invalid Signed int 32.", details: { - "excepted": mask32.bitLength, + "expected": mask32.bitLength, "bitLength": bitLength, "value": toString() }); @@ -124,7 +146,7 @@ extension IntHelper on int { int get asUint32 { if (isNegative || this > maxUint32) { throw ArgumentException("Invalid Unsigned int 32.", details: { - "excepted": mask32.bitLength, + "expected": mask32.bitLength, "bitLength": bitLength, "value": toString() }); @@ -135,7 +157,18 @@ extension IntHelper on int { int get asUint8 { if (isNegative || this > mask8) { throw ArgumentException("Invalid Unsigned int 8.", details: { - "excepted": mask32.bitLength, + "expected": mask32.bitLength, + "bitLength": bitLength, + "value": toString() + }); + } + return this; + } + + int get asUint16 { + if (isNegative || this > mask16) { + throw ArgumentException("Invalid Unsigned int 16.", details: { + "expected": mask16.bitLength, "bitLength": bitLength, "value": toString() }); diff --git a/lib/layout/constant/constant.dart b/lib/layout/constant/constant.dart index a87d603..46fbcb6 100644 --- a/lib/layout/constant/constant.dart +++ b/lib/layout/constant/constant.dart @@ -220,6 +220,10 @@ class LayoutConst { static BigIntLayout u128({String? property}) => BigIntLayout(16, property: property); + /// [BigIntLayout] (little-endian unsigned int layouts) interpreted as Numbers. + static BigIntLayout u256({String? property}) => + BigIntLayout(32, property: property); + /// [BigIntLayout] (little-endian signed int layouts) interpreted as Numbers. static BigIntLayout i128({String? property}) => BigIntLayout(16, sign: true, property: property); @@ -655,4 +659,80 @@ class LayoutConst { static CompactLayout compact(Layout layout, {String? property}) => CompactLayout(layout, property: property); + + /// Serializes and deserializes raw bytes with a length prefix encoded as LEB128. + /// + /// The structure is: [...LEB128, data bytes...] + /// [property] is an optional key to map the serialized data. + static CustomLayout, List> bcsBytes( + {String? property}) { + return bcsVector(LayoutConst.u8(), property: property); + } + + /// Serializes and deserializes UTF-8 encoded strings. + /// + /// Internally uses [bcsBytes] to handle the byte representation of the string. + /// - [decoder]: Converts bytes back to a UTF-8 string. + /// - [encoder]: Converts a string to UTF-8 bytes. + /// [property] is optional for mapping the data. + static Layout bcsString({String? property}) { + return CustomLayout, String>( + layout: bcsBytes(), + decoder: (bytes) { + return StringUtils.decode(bytes); + }, + encoder: (src) { + return StringUtils.encode(src); + }, + property: property); + } + + /// Serializes and deserializes a vector (list) of elements using the provided [elementLayout]. + /// + /// The vector layout includes: + /// - Length prefix (using [bcsOffset]). + /// - Serialized list of elements (`values`). + /// + /// [T] is the type of elements in the list. + /// [property] is optional for nested mapping. + static CustomLayout, List> bcsVector( + Layout elementLayout, + {String? property}) { + final layout = LayoutConst.struct( + [LayoutConst.seq(elementLayout, bcsOffset(), property: 'values')]); + return CustomLayout, List>( + layout: layout, + encoder: (data) => {"values": data}, + decoder: (data) => (data["values"] as List).cast(), + property: property); + } + + /// Creates an offset layout using LEB128 encoding (used for compact integer representation). + /// + /// This is commonly used to define offsets or lengths in BCS data structures. + static LEB128U32OffsetLayout bcsOffset({String? property}) => + LEB128U32OffsetLayout(property: property); + + /// Handles lazy serialization and deserialization of enums (variant types). + /// + /// [variants] - A list of variants defining the enum structure. + /// [property] - Optional mapping key for nested structures. + static CustomLayout, Map> bcsLazyEnum( + List variants, { + String? property, + }) { + final unionLayout = LazyUnion.offset(bcsOffset()); + variants + .asMap() + .forEach((index, variant) => unionLayout.addVariant(variant)); + return CustomLayout, Map>( + layout: unionLayout, + decoder: (value) { + return {"key": value.keys.first, "value": value.values.first}; + }, + encoder: (src) { + return src; + }, + property: property); + } } diff --git a/lib/layout/core/core.dart b/lib/layout/core/core.dart index 4a023d9..21e3485 100644 --- a/lib/layout/core/core.dart +++ b/lib/layout/core/core.dart @@ -19,4 +19,5 @@ export 'types/bit_sequence.dart'; export 'types/xdr_bytes.dart'; export 'types/lazy_union.dart'; export 'types/lazy_struct.dart'; -// export 'types/'; +export 'types/leb128.dart'; +export 'types/leb128_offset.dart'; diff --git a/lib/layout/core/types/lazy_union.dart b/lib/layout/core/types/lazy_union.dart index 3e93da4..125a7f1 100644 --- a/lib/layout/core/types/lazy_union.dart +++ b/lib/layout/core/types/lazy_union.dart @@ -27,6 +27,12 @@ class LazyUnion extends Layout> { span: -1, property: property); } + factory LazyUnion.offset(ExternalOffsetLayout discr, {String? property}) { + return LazyUnion._( + discriminator: UnionLayoutDiscriminatorLayout(discr), + span: -1, + property: property); + } @override int getSpan(LayoutByteReader? bytes, @@ -141,7 +147,12 @@ class LazyVariantLayout extends Layout> { if (!this.span.isNegative) { return this.span; } - final int contentOffset = union.discriminator.layout.span; + int contentOffset = union.discriminator.layout.span; + if (contentOffset.isNegative) { + contentOffset = union.discriminator.layout + .getSpan(bytes, offset: offset, source: layout.index); + } + assert(contentOffset >= 0, "span cannot be negative."); int span = 0; span = layout.layout(property: layout.property).getSpan(bytes, @@ -158,7 +169,12 @@ class LazyVariantLayout extends Layout> { details: {"property": property}); } - final int contentOffset = union.discriminator.layout.span; + int contentOffset = union.discriminator.layout.span; + if (contentOffset.isNegative) { + contentOffset = + union.discriminator.decode(bytes, offset: offset).consumed; + } + assert(contentOffset >= 0, "span cannot be negative."); final Map dest = {}; int consumed = 0; @@ -174,7 +190,12 @@ class LazyVariantLayout extends Layout> { @override int encode(Map source, LayoutByteWriter writer, {int offset = 0}) { - final int contentOffset = union.discriminator.layout.span; + int contentOffset = union.discriminator.layout.span; + if (contentOffset.isNegative) { + contentOffset = + union.discriminator.encode(this.layout.index, writer, offset: offset); + } + assert(contentOffset >= 0, "span cannot be negative."); if (!source.containsKey(property)) { throw LayoutException("variant lacks property", details: {"property": property}); diff --git a/lib/layout/core/types/leb128.dart b/lib/layout/core/types/leb128.dart new file mode 100644 index 0000000..3cd2621 --- /dev/null +++ b/lib/layout/core/types/leb128.dart @@ -0,0 +1,103 @@ +import 'package:blockchain_utils/layout/byte/byte_handler.dart'; +import 'package:blockchain_utils/layout/core/core.dart'; + +/// A layout class for encoding and decoding integers using LEB128 (Little Endian Base 128) format. +/// +/// LEB128 is a variable-length encoding method that is efficient for representing small numbers compactly. +/// This layout is used in serialization frameworks like BCS (Binary Canonical Serialization). +class LEB128DIntLayout extends Layout { + /// Constructor for [LEB128IntLayout]. + /// + /// [layout] is the underlying integer layout used for validation. + /// [property] is an optional key associated with this layout for structured data handling. + LEB128DIntLayout(this.layout, {String? property}) + : super(-1, property: property); + + /// The integer layout used to validate values before encoding. + final IntegerLayout layout; + + /// Decodes a LEB128-encoded integer from a byte list starting at [startIndex]. + /// + /// This method reads bytes until the most significant bit (MSB) is 0, + /// which signals the end of the encoded integer. + static int readVarint(List bytes, {int startIndex = 0}) { + int result = 0; + int shift = 0; + for (int i = startIndex; i < bytes.length; i++) { + final int byte = bytes[i]; + result |= (byte & 0x7F) << shift; + shift += 7; + if ((byte & 0x80) == 0) { + break; + } + } + + return result; + } + + /// Encodes an integer [value] using LEB128 format. + /// + /// The method breaks the integer into 7-bit chunks, setting the MSB + /// to 1 for all but the last byte to indicate continuation. + static List writeVarint(int value) { + final List dest = []; + while (value >= 0x80) { + dest.add((value & 0x7F) | 0x80); + value >>= 7; + } + dest.add(value & 0x7F); + return dest; + } + + /// Calculates the span (number of bytes) occupied by a LEB128-encoded integer. + /// + /// - [bytes]: The byte reader containing the data. + /// - [offset]: The starting position to read from. + /// - [source]: Optional integer source (not used here). + /// + /// Returns the number of bytes consumed by the LEB128-encoded integer. + @override + int getSpan(LayoutByteReader? bytes, {int offset = 0, int? source}) { + int span = 0; + while ((bytes!.at(offset + span) & 0x80) != 0) { + span++; + } + return span + 1; + } + + /// Decodes a LEB128-encoded integer from the byte stream starting at [offset]. + /// + /// - [bytes]: The byte stream to decode from. + /// - [offset]: The starting position for decoding. + /// + /// Returns a [LayoutDecodeResult] containing the decoded integer and bytes consumed. + @override + LayoutDecodeResult decode(LayoutByteReader bytes, {int offset = 0}) { + final span = getSpan(bytes, offset: offset); + final decode = readVarint(bytes.sublist(offset, offset + span)); + return LayoutDecodeResult(consumed: span, value: decode); + } + + /// Encodes an integer [source] using LEB128 and writes it to the byte writer. + /// + /// - [source]: The integer value to encode. + /// - [writer]: The byte writer to output the encoded data. + /// - [offset]: The position in the writer to start writing. + /// + /// Returns the number of bytes written after encoding. + @override + int encode(int source, LayoutByteWriter writer, {int offset = 0}) { + layout.validate(source); + final encode = writeVarint(source); + writer.setAll(offset, encode); + return encode.length; + } + + /// Creates a copy (clone) of the current layout with an optional new [property]. + /// + /// Useful when reusing the layout structure with different property associations. + @override + LEB128DIntLayout clone({String? newProperty}) { + return LEB128DIntLayout(layout, property: newProperty); + } +} diff --git a/lib/layout/core/types/leb128_offset.dart b/lib/layout/core/types/leb128_offset.dart new file mode 100644 index 0000000..80bea92 --- /dev/null +++ b/lib/layout/core/types/leb128_offset.dart @@ -0,0 +1,70 @@ +import 'package:blockchain_utils/layout/byte/byte_handler.dart'; +import 'package:blockchain_utils/layout/constant/constant.dart'; +import 'package:blockchain_utils/layout/core/core/core.dart'; +import 'leb128.dart'; +import 'numeric.dart'; + +/// A layout class for handling LEB128 (Little Endian Base 128) encoded integers. +/// +/// LEB128 is a variable-length encoding used to store integers compactly. +/// This layout is commonly used to represent offsets, lengths, or counts in BCS (Binary Canonical Serialization). +class LEB128U32OffsetLayout extends ExternalOffsetLayout { + /// Constructor for the LEB128U32OffsetLayout. + /// [property] is an optional key to associate this layout with specific data fields. + LEB128U32OffsetLayout({super.property}); + + /// Internal layout used for encoding and decoding LEB128 integers. + final LEB128DIntLayout layout = LEB128DIntLayout(LayoutConst.u32()); + + /// Indicates that this layout represents a count (such as the length of a vector). + @override + bool isCount() { + return true; + } + + /// Decodes a LEB128-encoded integer from the provided byte reader starting at [offset]. + /// + /// - [bytes]: The byte stream to read from. + /// - [offset]: The position in the byte stream to start reading (default is 0). + /// + /// Returns a [LayoutDecodeResult] containing the decoded integer value. + @override + LayoutDecodeResult decode(LayoutByteReader bytes, {int offset = 0}) { + final decode = layout.decode(bytes, offset: offset); + return decode; + } + + /// Encodes an integer [source] using LEB128 encoding and writes it to the provided byte writer. + /// + /// - [source]: The integer value to encode. + /// - [writer]: The byte writer to write the encoded bytes to. + /// - [offset]: The position in the byte stream to start writing (default is 0). + /// + /// Returns the number of bytes written after encoding. + @override + int encode(int source, LayoutByteWriter writer, {int offset = 0}) { + final encodeLength = LEB128DIntLayout.writeVarint(source); + writer.setAll(offset, encodeLength); + return encodeLength.length; + } + + /// Creates a copy (clone) of the current layout with an optional new property name. + /// + /// Useful when working with nested layouts or reusing layout definitions with different properties. + @override + LEB128U32OffsetLayout clone({String? newProperty}) { + return LEB128U32OffsetLayout(property: newProperty); + } + + /// Determines the span (length in bytes) of the LEB128-encoded data. + /// + /// - [bytes]: Optional byte reader to inspect the data for dynamic spans. + /// - [offset]: Starting position in the byte stream (default is 0). + /// - [source]: Optional source value to calculate the expected span without decoding. + /// + /// Returns the number of bytes required to represent the encoded data. + @override + int getSpan(LayoutByteReader? bytes, {int offset = 0, int? source}) { + return layout.getSpan(bytes, offset: offset, source: source); + } +} diff --git a/lib/layout/core/types/numeric.dart b/lib/layout/core/types/numeric.dart index 282c4f0..4012723 100644 --- a/lib/layout/core/types/numeric.dart +++ b/lib/layout/core/types/numeric.dart @@ -17,8 +17,6 @@ abstract class ExternalLayout extends Layout { abstract class ExternalOffsetLayout extends ExternalLayout { const ExternalOffsetLayout({String? property}) : super(-1, property: property); - LayoutDecodeResult getLenAndSpan(LayoutByteReader bytes, - {int offset = 0}); } /// Represents a layout that greedily consumes bytes until the end. @@ -125,6 +123,7 @@ class IntegerLayout extends BaseIntiger { IntegerLayout(int span, {this.sign = false, this.order = Endian.little, String? property}) : super(span, property: property) { + assert(!span.isNegative, "Invalid integer layout span"); if (6 < this.span) { throw LayoutException("span must not exceed 6 bytes", details: { "property": property, diff --git a/lib/secret_wallet/web3_storage_defination.dart b/lib/secret_wallet/web3_storage_defination.dart index a8a5f27..dc0ab94 100644 --- a/lib/secret_wallet/web3_storage_defination.dart +++ b/lib/secret_wallet/web3_storage_defination.dart @@ -59,7 +59,7 @@ abstract class KDFParam { return KDF2.fromJson(params); default: throw Web3SecretStorageDefinationV3Exception("Invalid kdf.", details: { - "excepted": ["scrypt", "pbkdf2"].join(", "), + "expected": ["scrypt", "pbkdf2"].join(", "), "kdf": kdf }); } @@ -75,7 +75,7 @@ class KDF2 extends KDFParam { if (salt.length != _SecretStorageConst.saltLength) { throw Web3SecretStorageDefinationV3Exception("Invalid salt length.", details: { - "excepted": _SecretStorageConst.saltLength, + "expected": _SecretStorageConst.saltLength, "length": salt.length }); } @@ -84,7 +84,7 @@ class KDF2 extends KDFParam { factory KDF2.fromJson(Map json) { if (json["prf"] != "hmac-sha256") { throw Web3SecretStorageDefinationV3Exception("Invalid prf.", - details: {"excepted": "hmac-sha256", "prf": json["prf"]}); + details: {"expected": "hmac-sha256", "prf": json["prf"]}); } return KDF2( iterations: json["c"], @@ -103,7 +103,7 @@ class KDF2 extends KDFParam { final String prf = v.value[2].value; if (prf != "hmac-sha256") { throw Web3SecretStorageDefinationV3Exception("Invalid prf.", - details: {"excepted": "hmac-sha256", "prf": prf}); + details: {"expected": "hmac-sha256", "prf": prf}); } final List salt = v.value[3].value; return KDF2(iterations: c, salt: salt, dklen: dklen); @@ -157,7 +157,7 @@ class KDFScrypt extends KDFParam { if (salt.length != _SecretStorageConst.saltLength) { throw Web3SecretStorageDefinationV3Exception("Invalid salt length.", details: { - "excepted": _SecretStorageConst.saltLength, + "expected": _SecretStorageConst.saltLength, "length": salt.length }); } @@ -227,7 +227,7 @@ class CryptoParam { if (iv.length != _SecretStorageConst.ivLength) { throw Web3SecretStorageDefinationV3Exception("Invalid iv length.", details: { - "excepted": _SecretStorageConst.ivLength, + "expected": _SecretStorageConst.ivLength, "length": iv.length }); } @@ -375,7 +375,7 @@ class Web3SecretStorageDefinationV3 { } if (crypto["cipher"] != "aes-128-ctr") { throw Web3SecretStorageDefinationV3Exception("Invalid Cypher.", - details: {"excepted": "aes-128-ctr", "cipher": crypto["cipher"]}); + details: {"expected": "aes-128-ctr", "cipher": crypto["cipher"]}); } final iv = BytesUtils.fromHexString(crypto['cipherparams']['iv']); final encryptText = List.from(encryptedPrivateKey); @@ -419,7 +419,7 @@ class Web3SecretStorageDefinationV3 { final String cipher = params.value[0].value; if (cipher != "aes-128-ctr") { throw Web3SecretStorageDefinationV3Exception("Invalid cypher type.", - details: {"excepted": "aes-128-ctr", "cypher": cipher}); + details: {"expected": "aes-128-ctr", "cypher": cipher}); } final List iv = params.value[1].value; final kdf = KDFParam.fromCbor(params.value[3]); diff --git a/lib/service/models/params.dart b/lib/service/models/params.dart index 60fb3d5..4de04de 100644 --- a/lib/service/models/params.dart +++ b/lib/service/models/params.dart @@ -32,7 +32,7 @@ abstract class BaseServiceResponse { E cast() { if (this is! E) { throw ArgumentException("BaseServiceResponse casting faild.", - details: {"excepted": "$T", "type": type.name}); + details: {"expected": "$T", "type": type.name}); } return this as E; } @@ -69,8 +69,14 @@ abstract class BaseServiceRequestParams { final Map headers; final RequestServiceType type; final int requestID; + final List? successStatusCodes; + final List? errorStatusCodes; const BaseServiceRequestParams( - {required this.headers, required this.type, required this.requestID}); + {required this.headers, + required this.type, + required this.requestID, + this.successStatusCodes, + this.errorStatusCodes}); Uri toUri(String uri); List? body(); Map toJson(); @@ -97,7 +103,31 @@ abstract class BaseServiceRequestParams { throw RPCError( message: "Parsing response failed.", request: toJson(), - details: {"excepted": "$T"}); + details: {"expected": "$T"}); + } + + BaseServiceResponse parseResponse(List bodyBytes, + [int? statusCode]) { + statusCode ??= 200; + if (!ServiceProviderUtils.isSuccessStatusCode(statusCode, + allowSuccessStatusCodes: successStatusCodes)) { + return ServiceErrorResponse( + statusCode: statusCode, + error: ServiceProviderUtils.findError( + object: bodyBytes, + statusCode: statusCode, + allowStatusCode: errorStatusCodes)); + } + try { + T response = ServiceProviderUtils.toResult(bodyBytes); + return ServiceSuccessRespose( + statusCode: statusCode, response: response); + } catch (_) {} + + throw RPCError( + message: "Parsing response failed.", + request: toJson(), + details: {"expected": "$T"}); } } diff --git a/lib/service/utils/utils.dart b/lib/service/utils/utils.dart index cbc979e..9954768 100644 --- a/lib/service/utils/utils.dart +++ b/lib/service/utils/utils.dart @@ -10,13 +10,18 @@ class ServiceProviderUtils { /// If the status code is `401` or `403` and the object is a list of bytes or a string, /// it attempts to decode the error message. /// Returns: A decoded error message if applicable, otherwise `null`. - static String? findError({Object? object, required int statusCode}) { - if (statusCode == 401 || statusCode == 403) { - if (object is List) { - return StringUtils.tryDecode(object); - } else if (object is String) { - return object; - } + static String? findError( + {Object? object, required int statusCode, List? allowStatusCode}) { + String? error; + if (object is List) { + error = StringUtils.tryDecode(object); + } else if (object is String) { + error = object; + } + if (allowStatusCode != null && allowStatusCode.contains(statusCode)) { + return error; + } else if (statusCode == 401 || statusCode == 403) { + return error; } return null; } @@ -66,7 +71,11 @@ class ServiceProviderUtils { /// - [statusCode]: The HTTP status code to check. /// /// Returns: `true` if the status code is in the range 200–299, otherwise `false`. - static bool isSuccessStatusCode(int statusCode) { + static bool isSuccessStatusCode(int statusCode, + {List? allowSuccessStatusCodes}) { + if (allowSuccessStatusCodes != null) { + return allowSuccessStatusCodes.contains(statusCode); + } return statusCode >= 200 && statusCode < 300; } @@ -85,18 +94,34 @@ class ServiceProviderUtils { if (dynamic is T) { return StringUtils.toJson(StringUtils.decode(bytes)); } - if ({} is T) { + if ([] is T) { return StringUtils.toJson(StringUtils.decode(bytes)); } + if ([] is T) { + return bytes as T; + } + final resultString = StringUtils.decode(bytes); + if ([] is T) { + return StringUtils.toJson(resultString).cast() as T; + } + if ([] is T) { + return StringUtils.toJson(resultString).cast() as T; + } + if (0 is T) { + return IntUtils.parse(resultString) as T; + } + if (BigInt.zero is T) { + return BigintUtils.parse(resultString) as T; + } + if ({} is T) { + return StringUtils.toJson(resultString); + } if (>[] is T) { - return StringUtils.toJson(StringUtils.decode(bytes)) + return StringUtils.toJson(resultString) .map((e) => (e as Map).cast()) .toList() as T; } - if ([] is T) { - return bytes as T; - } - return StringUtils.decode(bytes) as T; + return resultString as T; } /// Parses a response object into a result of type `T`. @@ -144,7 +169,7 @@ class ServiceProviderUtils { throw RPCError( message: "Parsing response failed.", request: params.toJson(), - details: {"error": e.toString()}); + details: {"error": e.toString(), "excepted": "$T"}); } } } diff --git a/lib/signer/bitcoin_signer.dart b/lib/signer/bitcoin/bitcoin_signer.dart similarity index 99% rename from lib/signer/bitcoin_signer.dart rename to lib/signer/bitcoin/bitcoin_signer.dart index d98ae2d..d9d59cd 100644 --- a/lib/signer/bitcoin_signer.dart +++ b/lib/signer/bitcoin/bitcoin_signer.dart @@ -10,7 +10,7 @@ import 'package:blockchain_utils/crypto/crypto/cdsa/point/ec_projective_point.da import 'package:blockchain_utils/crypto/crypto/hash/hash.dart'; import 'package:blockchain_utils/crypto/quick_crypto.dart'; import 'package:blockchain_utils/exception/exceptions.dart'; -import 'package:blockchain_utils/signer/ecdsa_signing_key.dart'; +import 'package:blockchain_utils/signer/signing_key/ecdsa_signing_key.dart'; /// The [BitcoinSignerUtils] class provides utility methods related to Bitcoin signing operations. class BitcoinSignerUtils { diff --git a/lib/signer/const/constants.dart b/lib/signer/const/constants.dart new file mode 100644 index 0000000..ec82981 --- /dev/null +++ b/lib/signer/const/constants.dart @@ -0,0 +1,7 @@ +class CryptoSignerConst { + static const int ed25519SignatureLength = 64; + static const int ecdsaSignatureLength = 64; + static const int ecdsaRecoveryIdLength = 1; + static const int ecdsaSignatureWithRecoveryIdLength = + ecdsaSignatureLength + ecdsaRecoveryIdLength; +} diff --git a/lib/signer/cosmos/cosmos.dart b/lib/signer/cosmos/cosmos.dart deleted file mode 100644 index 42248ff..0000000 --- a/lib/signer/cosmos/cosmos.dart +++ /dev/null @@ -1,4 +0,0 @@ -export 'signers/cosmos_ed25519_signer.dart'; -export 'signers/cosmos_eth_sec256k1_signer.dart'; -export 'signers/cosmos_nist256r1_signer.dart'; -export 'signers/cosmos_secp256k1_signer.dart'; diff --git a/lib/signer/cosmos/signers/cosmos_ed25519_signer.dart b/lib/signer/cosmos/signers/cosmos_ed25519_signer.dart deleted file mode 100644 index eb49032..0000000 --- a/lib/signer/cosmos/signers/cosmos_ed25519_signer.dart +++ /dev/null @@ -1,4 +0,0 @@ -import 'package:blockchain_utils/signer/solana/solana_signer.dart'; - -typedef CosmosED25519Signer = SolanaSigner; -typedef CosmosED25519Verifier = SolanaVerifier; diff --git a/lib/signer/cosmos/signers/cosmos_eth_sec256k1_signer.dart b/lib/signer/cosmos/signers/cosmos_eth_sec256k1_signer.dart deleted file mode 100644 index b0616f6..0000000 --- a/lib/signer/cosmos/signers/cosmos_eth_sec256k1_signer.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'package:blockchain_utils/signer/eth/evm_signer.dart'; - -/// Ethereum Signer class for cryptographic operations, including signing and verification. -/// -/// The `CosmosETHSecp256k1Signer` class facilitates the creation of Ethereum signatures and -/// provides methods for signing messages, personal messages, and converting to -/// verification keys. It uses the ECDSA (Elliptic Curve Digital Signature Algorithm) -/// for cryptographic operations on the secp256k1 elliptic curve. -class CosmosETHSecp256k1Signer { - const CosmosETHSecp256k1Signer._(this._signer); - - final ETHSigner _signer; - - /// Factory method to create an CosmosETHSecp256k1Signer from a byte representation of a private key. - factory CosmosETHSecp256k1Signer.fromKeyBytes(List keyBytes) { - return CosmosETHSecp256k1Signer._(ETHSigner.fromKeyBytes(keyBytes)); - } - - /// Signs a personal message digest with an optional payload length. - /// - /// The Ethereum personal sign prefix is applied to the message, and the resulting - /// signature is returned as a byte list. Optionally, a payload length can be provided. - /// - /// Parameters: - /// - [digest]: The personal message digest to be signed. - /// - [payloadLength]: An optional payload length to include in the message prefix. - /// - /// Returns: - /// - A byte list representing the signature of the personal message. - List sign(List digest, {bool hashMessage = true}) { - return _signer.sign(digest, hashMessage: hashMessage).toBytes(false); - } - - /// Converts the CosmosETHSecp256k1Signer to an CosmosETHSecp256k1Verifier for verification purposes. - /// - /// Returns: - /// - An CosmosETHSecp256k1Verifier representing the verification key. - CosmosETHSecp256k1Verifier toVerifyKey() { - return CosmosETHSecp256k1Verifier._(_signer.toVerifyKey()); - } -} - -/// Ethereum Verifier class for cryptographic operations, including signature verification. -/// -/// The `CosmosETHSecp256k1Verifier` class allows the verification of Ethereum signatures and -/// public keys. It uses the ECDSA (Elliptic Curve Digital Signature Algorithm) -/// for cryptographic operations on the secp256k1 elliptic curve. -class CosmosETHSecp256k1Verifier { - final ETHVerifier _verifier; - - CosmosETHSecp256k1Verifier._(this._verifier); - - /// Factory method to create an CosmosETHSecp256k1Verifier from a byte representation of a public key. - factory CosmosETHSecp256k1Verifier.fromKeyBytes(List keyBytes) { - return CosmosETHSecp256k1Verifier._(ETHVerifier.fromKeyBytes(keyBytes)); - } - - /// Verifies an Ethereum signature against a message digest. - /// - /// Parameters: - /// - [digest]: The message digest. - /// - [signature]: The signature bytes. - /// - [hashMessage]: Whether to hash the message before verification (default is true). - /// - /// Returns: - /// - True if the signature is valid, false otherwise. - bool verify(List digest, List signature, - {bool hashMessage = true}) { - return _verifier.verify(digest, signature, hashMessage: hashMessage); - } -} diff --git a/lib/signer/solana/solana_signer.dart b/lib/signer/ed25519/ed25519.dart similarity index 70% rename from lib/signer/solana/solana_signer.dart rename to lib/signer/ed25519/ed25519.dart index 61ed91c..926bd54 100644 --- a/lib/signer/solana/solana_signer.dart +++ b/lib/signer/ed25519/ed25519.dart @@ -3,8 +3,8 @@ import 'package:blockchain_utils/crypto/crypto/crypto.dart'; import 'package:blockchain_utils/exception/exceptions.dart'; import 'package:blockchain_utils/utils/utils.dart'; -/// Constants used by the Solana signer for cryptographic operations. -class SolanaSignerConst { +/// Constants used by the Ed25519 signer for cryptographic operations. +class Ed25519SignerConst { /// The ED25519 elliptic curve generator point. static final EDPoint ed25519Generator = Curves.generatorED25519; @@ -12,22 +12,22 @@ class SolanaSignerConst { BigintUtils.orderLen(ed25519Generator.curve.p) * 2; } -/// Class for signing Solana transactions using either EDDSA algorithm. -class SolanaSigner { - /// Constructs a new SolanaSigner instance with the provided signing keys. +/// Class for signing Ed25519. +class Ed25519Signer { + /// Constructs a new Ed25519Signer instance with the provided signing keys. /// /// This constructor is marked as private and takes an EDDSA private key [_signingKey] - const SolanaSigner._(this._signingKey); + const Ed25519Signer._(this._signingKey); /// The EDDSA private key for signing. final EDDSAPrivateKey _signingKey; - /// Factory method to create an SolanaSigner instance from key bytes. - factory SolanaSigner.fromKeyBytes(List keyBytes) { + /// Factory method to create an Ed25519Signer instance from key bytes. + factory Ed25519Signer.fromKeyBytes(List keyBytes) { // Create an EDDSA private key from the key bytes using the ED25519 curve. final signingKey = EDDSAPrivateKey( - SolanaSignerConst.ed25519Generator, keyBytes, () => SHA512()); - return SolanaSigner._(signingKey); + Ed25519SignerConst.ed25519Generator, keyBytes, () => SHA512()); + return Ed25519Signer._(signingKey); } /// Signs the provided digest using the ED25519 algorithm. @@ -60,30 +60,30 @@ class SolanaSigner { return _signEdward(digest); } - /// Returns an SolanaVerifier instance based on the available signing key type. + /// Returns an Ed25519Verifier instance based on the available signing key type. /// - /// This method constructs and returns an SolanaVerifier instance for signature verification. + /// This method constructs and returns an Ed25519Verifier instance for signature verification. /// - /// returns An SolanaVerifier instance based on the available signing key type. - SolanaVerifier toVerifyKey() { + /// returns An Ed25519Verifier instance based on the available signing key type. + Ed25519Verifier toVerifyKey() { final keyBytes = _signingKey.publicKey.toBytes(); - return SolanaVerifier.fromKeyBytes(keyBytes); + return Ed25519Verifier.fromKeyBytes(keyBytes); } } -/// Class representing an Solana Verifier for signature verification. -class SolanaVerifier { +/// Class representing an Ed25519Verifier for signature verification. +class Ed25519Verifier { final EDDSAPublicKey _eddsaPublicKey; - /// Private constructor to create an SolanaVerifier instance. - const SolanaVerifier._(this._eddsaPublicKey); + /// Private constructor to create an Ed25519Verifier instance. + const Ed25519Verifier._(this._eddsaPublicKey); - /// Factory method to create an SolanaVerifier instance from key bytes. - factory SolanaVerifier.fromKeyBytes(List keyBytes) { + /// Factory method to create an Ed25519Verifier instance from key bytes. + factory Ed25519Verifier.fromKeyBytes(List keyBytes) { final pub = Ed25519PublicKey.fromBytes(keyBytes); final verifyingKey = EDDSAPublicKey( - SolanaSignerConst.ed25519Generator, pub.compressed.sublist(1)); - return SolanaVerifier._(verifyingKey); + Ed25519SignerConst.ed25519Generator, pub.compressed.sublist(1)); + return Ed25519Verifier._(verifyingKey); } /// Verifies the EDDSA signature for the provided digest. diff --git a/lib/signer/eth/evm_signer.dart b/lib/signer/eth/evm_signer.dart index 6a53ef2..e61a4f5 100644 --- a/lib/signer/eth/evm_signer.dart +++ b/lib/signer/eth/evm_signer.dart @@ -1,7 +1,7 @@ import 'package:blockchain_utils/crypto/crypto/crypto.dart'; import 'package:blockchain_utils/crypto/quick_crypto.dart'; import 'package:blockchain_utils/exception/exceptions.dart'; -import 'package:blockchain_utils/signer/ecdsa_signing_key.dart'; +import 'package:blockchain_utils/signer/signing_key/ecdsa_signing_key.dart'; import 'package:blockchain_utils/signer/eth/eth_signature.dart'; import 'package:blockchain_utils/utils/utils.dart'; diff --git a/lib/signer/cosmos/signers/cosmos_secp256k1_signer.dart b/lib/signer/secp256k1/secp256k1_signer.dart similarity index 68% rename from lib/signer/cosmos/signers/cosmos_secp256k1_signer.dart rename to lib/signer/secp256k1/secp256k1_signer.dart index b79e9f7..e604ca7 100644 --- a/lib/signer/cosmos/signers/cosmos_secp256k1_signer.dart +++ b/lib/signer/secp256k1/secp256k1_signer.dart @@ -2,25 +2,25 @@ import 'package:blockchain_utils/crypto/crypto/cdsa/cdsa.dart'; import 'package:blockchain_utils/crypto/crypto/hash/hash.dart'; import 'package:blockchain_utils/crypto/quick_crypto.dart'; import 'package:blockchain_utils/exception/exceptions.dart'; -import 'package:blockchain_utils/signer/ecdsa_signing_key.dart'; +import 'package:blockchain_utils/signer/signing_key/ecdsa_signing_key.dart'; import 'package:blockchain_utils/signer/eth/evm_signer.dart'; -/// Cosmos Signer class for cryptographic operations, including signing and verification. +/// Secp256k1 Signer class for cryptographic operations, including signing and verification. /// -/// The [CosmosSecp256k1Signer] class facilitates the creation of Cosmos signatures and +/// The [Secp256k1Signer] class facilitates the creation of Ecdsa signatures and /// provides methods for signing messages, personal messages, and converting to /// verification keys. It uses the ECDSA (Elliptic Curve Digital Signature Algorithm) /// for cryptographic operations on the secp256k1 elliptic curve. -class CosmosSecp256k1Signer { - const CosmosSecp256k1Signer._(this._ecdsaSigningKey); +class Secp256k1Signer { + const Secp256k1Signer._(this._ecdsaSigningKey); final EcdsaSigningKey _ecdsaSigningKey; - /// Factory method to create a [CosmosSecp256k1Signer] from a byte representation of a private key. - factory CosmosSecp256k1Signer.fromKeyBytes(List keyBytes) { + /// Factory method to create a [Secp256k1Signer] from a byte representation of a private key. + factory Secp256k1Signer.fromKeyBytes(List keyBytes) { final signingKey = ECDSAPrivateKey.fromBytes(keyBytes, ETHSignerConst.secp256); - return CosmosSecp256k1Signer._(EcdsaSigningKey(signingKey)); + return Secp256k1Signer._(EcdsaSigningKey(signingKey)); } List _signEcdsa(List digest, {bool hashMessage = true}) { @@ -62,32 +62,32 @@ class CosmosSecp256k1Signer { return _signEcdsa(digest, hashMessage: hashMessage); } - /// Converts the [CosmosSecp256k1Signer] to a [CosmosVerifier] for verification purposes. + /// Converts the [Secp256k1Signer] to a [Secp256k1Verifier] for verification purposes. /// /// Returns: - /// - A [CosmosVerifier] representing the verification key. - CosmosVerifier toVerifyKey() { - return CosmosVerifier.fromKeyBytes( + /// - A [Secp256k1Verifier] representing the verification key. + Secp256k1Verifier toVerifyKey() { + return Secp256k1Verifier.fromKeyBytes( _ecdsaSigningKey.privateKey.publicKey.toBytes()); } } -/// Cosmos Verifier class for cryptographic operations, including signature verification. +/// Secp256k1 Verifier class for cryptographic operations, including signature verification. /// -/// The [CosmosVerifier] class allows the verification of Cosmos signatures and +/// The [Secp256k1Verifier] class allows the verification of Secp256k1 signatures and /// public keys. It uses the ECDSA (Elliptic Curve Digital Signature Algorithm) /// for cryptographic operations on the secp256k1 elliptic curve. -class CosmosVerifier { +class Secp256k1Verifier { final ECDSAVerifyKey edsaVerifyKey; - CosmosVerifier._(this.edsaVerifyKey); + Secp256k1Verifier._(this.edsaVerifyKey); - /// Factory method to create a [CosmosVerifier] from a byte representation of a public key. - factory CosmosVerifier.fromKeyBytes(List keyBytes) { + /// Factory method to create a [Secp256k1Verifier] from a byte representation of a public key. + factory Secp256k1Verifier.fromKeyBytes(List keyBytes) { final point = ProjectiveECCPoint.fromBytes( curve: ETHSignerConst.secp256.curve, data: keyBytes, order: null); final verifyingKey = ECDSAPublicKey(ETHSignerConst.secp256, point); - return CosmosVerifier._(ECDSAVerifyKey(verifyingKey)); + return Secp256k1Verifier._(ECDSAVerifyKey(verifyingKey)); } bool _verifyEcdsa(List digest, List sigBytes) { final signature = @@ -95,7 +95,7 @@ class CosmosVerifier { return edsaVerifyKey.verify(signature, digest); } - /// Verifies a Cosmos signature against a message digest. + /// Verifies a Secp256k1 signature against a message digest. /// /// Parameters: /// - [message]: The message digest. @@ -103,7 +103,11 @@ class CosmosVerifier { /// /// Returns: /// - True if the signature is valid, false otherwise. - bool verify(List message, List signature) { + bool verify(List message, List signature, + {bool hashMessage = false}) { + if (hashMessage) { + message = QuickCrypto.sha256Hash(message); + } return _verifyEcdsa(message, signature); } } diff --git a/lib/signer/cosmos/signers/cosmos_nist256r1_signer.dart b/lib/signer/secp256r1/secp256r1_signer.dart similarity index 71% rename from lib/signer/cosmos/signers/cosmos_nist256r1_signer.dart rename to lib/signer/secp256r1/secp256r1_signer.dart index 980b876..52ced30 100644 --- a/lib/signer/cosmos/signers/cosmos_nist256r1_signer.dart +++ b/lib/signer/secp256r1/secp256r1_signer.dart @@ -2,7 +2,7 @@ import 'package:blockchain_utils/crypto/crypto/cdsa/cdsa.dart'; import 'package:blockchain_utils/crypto/crypto/hash/hash.dart'; import 'package:blockchain_utils/crypto/quick_crypto.dart'; import 'package:blockchain_utils/exception/exceptions.dart'; -import 'package:blockchain_utils/signer/ecdsa_signing_key.dart'; +import 'package:blockchain_utils/signer/signing_key/ecdsa_signing_key.dart'; class _NistSignerConst { /// The projective ECC point representing the secp256r1 elliptic curve. @@ -18,22 +18,22 @@ class _NistSignerConst { static final BigInt orderHalf = curveOrder >> 1; } -/// Cosmos nist256p1 Signer class for cryptographic operations, including signing and verification. +/// Nist256p1Signer Signer class for cryptographic operations, including signing and verification. /// -/// The [CosmosNist256p1Signer] class facilitates the creation of Cosmos signatures and +/// The [Nist256p1Signer] class facilitates the creation of Nist256p1 signatures and /// provides methods for signing messages, personal messages, and converting to /// verification keys. It uses the ECDSA (Elliptic Curve Digital Signature Algorithm) /// for cryptographic operations on the secp256R1 elliptic curve. -class CosmosNist256p1Signer { - const CosmosNist256p1Signer._(this._ecdsaSigningKey); +class Nist256p1Signer { + const Nist256p1Signer._(this._ecdsaSigningKey); final EcdsaSigningKey _ecdsaSigningKey; - /// Factory method to create a [CosmosNist256p1Signer] from a byte representation of a private key. - factory CosmosNist256p1Signer.fromKeyBytes(List keyBytes) { + /// Factory method to create a [Nist256p1Signer] from a byte representation of a private key. + factory Nist256p1Signer.fromKeyBytes(List keyBytes) { final signingKey = ECDSAPrivateKey.fromBytes(keyBytes, _NistSignerConst.nist256256); - return CosmosNist256p1Signer._(EcdsaSigningKey(signingKey)); + return Nist256p1Signer._(EcdsaSigningKey(signingKey)); } List _signEcdsa(List digest, {bool hashMessage = true}) { @@ -76,32 +76,32 @@ class CosmosNist256p1Signer { return _signEcdsa(digest, hashMessage: hashMessage); } - /// Converts the [CosmosNist256p1Signer] to a [CosmosNist256p1Verifier] for verification purposes. + /// Converts the [Nist256p1Signer] to a [Nist256p1Verifier] for verification purposes. /// /// Returns: - /// - A [CosmosNist256p1Verifier] representing the verification key. - CosmosNist256p1Verifier toVerifyKey() { - return CosmosNist256p1Verifier.fromKeyBytes( + /// - A [Nist256p1Verifier] representing the verification key. + Nist256p1Verifier toVerifyKey() { + return Nist256p1Verifier.fromKeyBytes( _ecdsaSigningKey.privateKey.publicKey.toBytes()); } } -/// [CosmosNist256p1Verifier] class for cryptographic operations, including signature verification. +/// [Nist256p1Verifier] class for cryptographic operations, including signature verification. /// -/// The [CosmosNist256p1Verifier] class allows the verification of Cosmos signatures and +/// The [Nist256p1Verifier] class allows the verification of Nist256p1 signatures and /// public keys. It uses the ECDSA (Elliptic Curve Digital Signature Algorithm) /// for cryptographic operations on the secp256r1 elliptic curve. -class CosmosNist256p1Verifier { +class Nist256p1Verifier { final ECDSAVerifyKey edsaVerifyKey; - CosmosNist256p1Verifier._(this.edsaVerifyKey); + Nist256p1Verifier._(this.edsaVerifyKey); - /// Factory method to create a [CosmosNist256p1Verifier] from a byte representation of a public key. - factory CosmosNist256p1Verifier.fromKeyBytes(List keyBytes) { + /// Factory method to create a [Nist256p1Verifier] from a byte representation of a public key. + factory Nist256p1Verifier.fromKeyBytes(List keyBytes) { final point = ProjectiveECCPoint.fromBytes( curve: _NistSignerConst.nist256256.curve, data: keyBytes, order: null); final verifyingKey = ECDSAPublicKey(_NistSignerConst.nist256256, point); - return CosmosNist256p1Verifier._(ECDSAVerifyKey(verifyingKey)); + return Nist256p1Verifier._(ECDSAVerifyKey(verifyingKey)); } bool _verifyEcdsa(List digest, List sigBytes) { final signature = @@ -109,7 +109,7 @@ class CosmosNist256p1Verifier { return edsaVerifyKey.verify(signature, digest); } - /// Verifies a Cosmos signature against a message digest. + /// Verifies a Nist256p1 signature against a message digest. /// /// Parameters: /// - [message]: The message digest. @@ -117,7 +117,11 @@ class CosmosNist256p1Verifier { /// /// Returns: /// - True if the signature is valid, false otherwise. - bool verify(List message, List signature) { + bool verify(List message, List signature, + {bool hashMessage = false}) { + if (hashMessage) { + message = QuickCrypto.sha256Hash(message); + } return _verifyEcdsa(message, signature); } } diff --git a/lib/signer/signer.dart b/lib/signer/signer.dart index c46ed1b..1d2212d 100644 --- a/lib/signer/signer.dart +++ b/lib/signer/signer.dart @@ -1,9 +1,12 @@ -export 'xrp_signer.dart'; -export 'bitcoin_signer.dart'; +export 'xrp/xrp_signer.dart'; +export 'bitcoin/bitcoin_signer.dart'; export 'eth/eth_signature.dart'; export 'eth/evm_signer.dart'; export 'tron/tron_signer.dart'; -export 'solana/solana_signer.dart'; export 'cardano/cardano_signer.dart'; export 'substrate/substrate.dart'; -export 'cosmos/cosmos.dart'; +export 'const/constants.dart'; +export 'secp256k1/secp256k1_signer.dart'; +export 'secp256r1/secp256r1_signer.dart'; +export 'ed25519/ed25519.dart'; +export 'signing_key/ecdsa_signing_key.dart'; diff --git a/lib/signer/ecdsa_signing_key.dart b/lib/signer/signing_key/ecdsa_signing_key.dart similarity index 100% rename from lib/signer/ecdsa_signing_key.dart rename to lib/signer/signing_key/ecdsa_signing_key.dart diff --git a/lib/signer/substrate/signers/substrate_ecdsa.dart b/lib/signer/substrate/signers/substrate_ecdsa.dart index 0340b15..25d5221 100644 --- a/lib/signer/substrate/signers/substrate_ecdsa.dart +++ b/lib/signer/substrate/signers/substrate_ecdsa.dart @@ -2,7 +2,7 @@ import 'package:blockchain_utils/utils/utils.dart'; import 'package:blockchain_utils/crypto/crypto/crypto.dart'; import 'package:blockchain_utils/crypto/quick_crypto.dart'; import 'package:blockchain_utils/exception/exceptions.dart'; -import 'package:blockchain_utils/signer/ecdsa_signing_key.dart'; +import 'package:blockchain_utils/signer/signing_key/ecdsa_signing_key.dart'; import 'package:blockchain_utils/signer/eth/eth_signature.dart'; import 'package:blockchain_utils/signer/eth/evm_signer.dart'; import 'package:blockchain_utils/signer/substrate/core/signer.dart'; @@ -223,7 +223,7 @@ class SubstrateEcdsaVerifier implements BaseSubstrateVerifier { {List? context, List? extra}) { if (vrfSign.length != _SubstrateEcdsaSignerCons.vrfLength) { throw ArgumentException( - "Invalid vrf length. excepted: ${_SubstrateEcdsaSignerCons.vrfLength} got: ${vrfSign.length}"); + "Invalid vrf length. expected: ${_SubstrateEcdsaSignerCons.vrfLength} got: ${vrfSign.length}"); } final List signature = vrfSign.sublist(QuickCrypto.blake2b256DigestSize); diff --git a/lib/signer/substrate/signers/substrate_eddsa.dart b/lib/signer/substrate/signers/substrate_eddsa.dart index 5fa77d4..05b2db3 100644 --- a/lib/signer/substrate/signers/substrate_eddsa.dart +++ b/lib/signer/substrate/signers/substrate_eddsa.dart @@ -1,13 +1,13 @@ import 'package:blockchain_utils/crypto/quick_crypto.dart'; import 'package:blockchain_utils/exception/exceptions.dart'; -import 'package:blockchain_utils/signer/solana/solana_signer.dart'; +import 'package:blockchain_utils/signer/ed25519/ed25519.dart'; import 'package:blockchain_utils/signer/substrate/core/signer.dart'; import 'package:blockchain_utils/signer/substrate/core/verifier.dart'; import 'package:blockchain_utils/utils/utils.dart'; class _SubstrateED25519SignerConstant { static final int vrfLength = - SolanaSignerConst.signatureLen + QuickCrypto.blake2b256DigestSize; + Ed25519SignerConst.signatureLen + QuickCrypto.blake2b256DigestSize; } /// Class for signing Substrate transactions using either EDDSA algorithm. @@ -16,11 +16,11 @@ class SubstrateED25519Signer implements BaseSubstrateSigner { const SubstrateED25519Signer._(this._signer); /// The EDDSA private key for signing. - final SolanaSigner _signer; + final Ed25519Signer _signer; /// Factory method to create an SubstrateED25519Signer instance from key bytes. factory SubstrateED25519Signer.fromKeyBytes(List keyBytes) { - return SubstrateED25519Signer._(SolanaSigner.fromKeyBytes(keyBytes)); + return SubstrateED25519Signer._(Ed25519Signer.fromKeyBytes(keyBytes)); } /// Returns an SubstrateED25519Signer instance based on the available signing key type. @@ -60,14 +60,14 @@ class SubstrateED25519Signer implements BaseSubstrateSigner { /// Class representing an Substrate ED25519 Verifier for signature verification. class SubstrateED25519Verifier implements BaseSubstrateVerifier { - final SolanaVerifier _verifier; + final Ed25519Verifier _verifier; /// Private constructor to create an SolanaVerifier instance. SubstrateED25519Verifier._(this._verifier); /// Factory method to create an SolanaVerifier instance from key bytes. factory SubstrateED25519Verifier.fromKeyBytes(List keyBytes) { - final verifier = SolanaVerifier.fromKeyBytes(keyBytes); + final verifier = Ed25519Verifier.fromKeyBytes(keyBytes); return SubstrateED25519Verifier._(verifier); } @@ -87,7 +87,7 @@ class SubstrateED25519Verifier implements BaseSubstrateVerifier { {List? context, List? extra}) { if (vrfSign.length != _SubstrateED25519SignerConstant.vrfLength) { throw ArgumentException( - "Invalid vrf length. excepted: ${_SubstrateED25519SignerConstant.vrfLength} got: ${vrfSign.length}"); + "Invalid vrf length. expected: ${_SubstrateED25519SignerConstant.vrfLength} got: ${vrfSign.length}"); } final List signature = vrfSign.sublist(QuickCrypto.blake2b256DigestSize); diff --git a/lib/signer/substrate/signers/substrate_sr25519.dart b/lib/signer/substrate/signers/substrate_sr25519.dart index 10a9034..87baebb 100644 --- a/lib/signer/substrate/signers/substrate_sr25519.dart +++ b/lib/signer/substrate/signers/substrate_sr25519.dart @@ -94,7 +94,7 @@ class SubstrateSr25519Signer implements BaseSubstrateSigner { if (vrfSign.length != _SubstrateSr25519SignerConst.vrfResultLength && vrfSign.length != SchnorrkelKeyCost.vrfProofLength) { throw ArgumentException( - "Invalid VrfSign bytes length. excepted: ${_SubstrateSr25519SignerConst.vrfResultLength}, ${SchnorrkelKeyCost.vrfProofLength} got: ${vrfSign.length} "); + "Invalid VrfSign bytes length. expected: ${_SubstrateSr25519SignerConst.vrfResultLength}, ${SchnorrkelKeyCost.vrfProofLength} got: ${vrfSign.length} "); } final MerlinTranscript script = _SubstrateSr25519SignerUtils.substrateVrfSignScript(message, context); @@ -151,7 +151,7 @@ class SubstrateSr25519Verifier implements BaseSubstrateVerifier { {List? context, List? extra}) { if (vrfSign.length != _SubstrateSr25519SignerConst.vrfResultLength) { throw ArgumentException( - "Invalid VrfSign bytes length. excepted: ${_SubstrateSr25519SignerConst.vrfResultLength} got: ${vrfSign.length} "); + "Invalid VrfSign bytes length. expected: ${_SubstrateSr25519SignerConst.vrfResultLength} got: ${vrfSign.length} "); } final MerlinTranscript vrfScript = _SubstrateSr25519SignerUtils.vrfScript(extra: extra); diff --git a/lib/signer/tron/tron_signer.dart b/lib/signer/tron/tron_signer.dart index 0c5bd4f..65610c5 100644 --- a/lib/signer/tron/tron_signer.dart +++ b/lib/signer/tron/tron_signer.dart @@ -1,7 +1,7 @@ import 'package:blockchain_utils/crypto/crypto/crypto.dart'; import 'package:blockchain_utils/crypto/quick_crypto.dart'; import 'package:blockchain_utils/exception/exceptions.dart'; -import 'package:blockchain_utils/signer/ecdsa_signing_key.dart'; +import 'package:blockchain_utils/signer/signing_key/ecdsa_signing_key.dart'; import 'package:blockchain_utils/signer/eth/eth_signature.dart'; import 'package:blockchain_utils/signer/eth/evm_signer.dart'; import 'package:blockchain_utils/utils/utils.dart'; diff --git a/lib/signer/xrp_signer.dart b/lib/signer/xrp/xrp_signer.dart similarity index 99% rename from lib/signer/xrp_signer.dart rename to lib/signer/xrp/xrp_signer.dart index eb28caf..60e0e69 100644 --- a/lib/signer/xrp_signer.dart +++ b/lib/signer/xrp/xrp_signer.dart @@ -2,7 +2,7 @@ import 'package:blockchain_utils/bip/ecc/bip_ecc.dart'; import 'package:blockchain_utils/crypto/crypto/crypto.dart'; import 'package:blockchain_utils/crypto/quick_crypto.dart'; import 'package:blockchain_utils/exception/exceptions.dart'; -import 'package:blockchain_utils/signer/ecdsa_signing_key.dart'; +import 'package:blockchain_utils/signer/signing_key/ecdsa_signing_key.dart'; import 'package:blockchain_utils/utils/utils.dart'; /// Constants used by the XRP signer for cryptographic operations. diff --git a/lib/utils/binary/binary_operation.dart b/lib/utils/binary/binary_operation.dart index b956e3d..3adb6c9 100644 --- a/lib/utils/binary/binary_operation.dart +++ b/lib/utils/binary/binary_operation.dart @@ -112,6 +112,9 @@ void zero(List array) { } final BigInt maxU64 = BigInt.parse("18446744073709551615"); +final BigInt maxU128 = BigInt.parse("340282366920938463463374607431768211455"); +final BigInt maxU256 = BigInt.parse( + "115792089237316195423570985008687907853269984665640564039457584007913129639935"); final BigInt maskBig8 = BigInt.from(mask8); diff --git a/lib/utils/binary/utils.dart b/lib/utils/binary/utils.dart index 77ceefd..70aca1d 100644 --- a/lib/utils/binary/utils.dart +++ b/lib/utils/binary/utils.dart @@ -120,10 +120,10 @@ class BytesUtils { /// If [data] is null, returns null. Otherwise, attempts to parse the /// hexadecimal string using the [fromHexString] function. If successful, /// returns the resulting List of integers; otherwise, returns null. - static List? tryFromHexString(String? data) { + static List? tryFromHexString(String? data, {bool paddingZero = false}) { if (data == null) return null; try { - return fromHexString(data); + return fromHexString(data, paddingZero: paddingZero); } catch (e) { return null; } diff --git a/lib/utils/numbers/utils/bigint_utils.dart b/lib/utils/numbers/utils/bigint_utils.dart index 7c86389..381034e 100644 --- a/lib/utils/numbers/utils/bigint_utils.dart +++ b/lib/utils/numbers/utils/bigint_utils.dart @@ -378,7 +378,7 @@ class BigintUtils { /// from BigInt, int, `List`, and String types. If [v] is a String and /// represents a hexadecimal number (prefixed with '0x' or not), it is parsed /// accordingly. - /// + /// allowHex: convert hexadecimal to integer /// Parameters: /// - [v]: The dynamic value to be parsed into a BigInt. /// @@ -388,39 +388,38 @@ class BigintUtils { /// Throws: /// - [ArgumentException] if the input value cannot be parsed into a BigInt. /// - static BigInt parse(dynamic v) { + static BigInt parse(dynamic v, {bool allowHex = true}) { try { if (v is BigInt) return v; if (v is int) return BigInt.from(v); - if (v is List) { - return fromBytes(v, sign: true); - } if (v is String) { BigInt? parse = BigInt.tryParse(v); - if (parse == null && StringUtils.ixHexaDecimalNumber(v)) { + if (parse == null && allowHex && StringUtils.ixHexaDecimalNumber(v)) { parse = BigInt.parse(StringUtils.strip0x(v), radix: 16); } return parse!; } // ignore: empty_catches } catch (e) {} - throw const ArgumentException("invalid input for parse bigint"); + throw ArgumentException("invalid input for parse bigint", + details: {"value": "$v"}); } /// Tries to parse a dynamic value [v] into a BigInt, returning null if parsing fails. /// /// Attempts to parse the dynamic value [v] into a BigInt using the [parse] method. /// If successful, returns the resulting BigInt; otherwise, returns null. - /// + /// allowHex: convert hexadecimal to integer /// Parameters: /// - [v]: The dynamic value to be parsed into a BigInt. /// /// Returns: /// - A BigInt if parsing is successful; otherwise, returns null. /// - static BigInt? tryParse(dynamic v) { + static BigInt? tryParse(dynamic v, {bool allowHex = true}) { + if (v == null) return null; try { - return parse(v); + return parse(v, allowHex: allowHex); } on ArgumentException { return null; } diff --git a/lib/utils/numbers/utils/int_utils.dart b/lib/utils/numbers/utils/int_utils.dart index a3880d3..1ab19a8 100644 --- a/lib/utils/numbers/utils/int_utils.dart +++ b/lib/utils/numbers/utils/int_utils.dart @@ -165,29 +165,33 @@ class IntUtils { /// from int, BigInt, `List`, and String types. If [v] is a String and /// represents a hexadecimal number (prefixed with '0x' or not), it is parsed /// accordingly. - /// + /// allowHex: convert hexadecimal to integer /// Parameters: /// - [v]: The dynamic value to be parsed into an integer. /// /// Returns: /// - An integer representation of the parsed value. /// - static int parse(dynamic v) { + static int parse(dynamic v, {bool allowHex = true}) { try { if (v is int) return v; - if (v is BigInt) return v.toInt(); - if (v is List) { - return fromBytes(v, sign: true); + if (v is BigInt) { + if (!v.isValidInt) { + throw ArgumentException("value is to large for integer.", + details: {"value": "$v"}); + } + return v.toInt(); } if (v is String) { int? parse = int.tryParse(v); - if (parse == null && StringUtils.ixHexaDecimalNumber(v)) { + if (parse == null && allowHex && StringUtils.ixHexaDecimalNumber(v)) { parse = int.parse(StringUtils.strip0x(v), radix: 16); } return parse!; } } catch (_) {} - throw const ArgumentException("invalid input for parse int"); + throw ArgumentException("invalid input for parse int", + details: {"value": "$v"}); } /// Tries to parse a dynamic value [v] into an integer, returning null if parsing fails. @@ -195,17 +199,17 @@ class IntUtils { /// If the input value [v] is null, directly returns null. Otherwise, attempts to /// parse the dynamic value [v] into an integer using the [parse] method. /// If successful, returns the resulting integer; otherwise, returns null. - /// + /// allowHex: convert hexadecimal to integer /// Parameters: /// - [v]: The dynamic value to be parsed into an integer. /// /// Returns: /// - An integer if parsing is successful; otherwise, returns null. /// - static int? tryParse(dynamic v) { + static int? tryParse(dynamic v, {bool allowHex = true}) { if (v == null) return null; try { - return parse(v); + return parse(v, allowHex: allowHex); } on ArgumentException { return null; } diff --git a/lib/utils/string/string.dart b/lib/utils/string/string.dart index 3886a96..285baea 100644 --- a/lib/utils/string/string.dart +++ b/lib/utils/string/string.dart @@ -169,7 +169,7 @@ class StringUtils { final decode = jsonDecode(data, reviver: reviver); if (decode is! T) { throw ArgumentException( - "Invalid json casting. excepted: $T got: ${decode.runtimeType}"); + "Invalid json casting. expected: $T got: ${decode.runtimeType}"); } return decode; } diff --git a/pubspec.yaml b/pubspec.yaml index 319f913..61d313e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: blockchain_utils description: Comprehensive Crypto & Blockchain Toolkit, Pure Dart, Cross-Platform, Encoding, Cryptography, Addresses, Mnemonics, & More. -version: 4.0.1 +version: 4.1.0 homepage: "https://github.com/mrtnetwork/blockchain_utils" repository: "https://github.com/mrtnetwork/blockchain_utils" Author: mrhaydari.t@gmail.com diff --git a/test/address/aptos/aptos_test.dart b/test/address/aptos/aptos_test.dart index 29c9882..a22b34f 100644 --- a/test/address/aptos/aptos_test.dart +++ b/test/address/aptos/aptos_test.dart @@ -1,4 +1,5 @@ -import 'package:blockchain_utils/bip/address/aptos_addr.dart'; +import 'package:blockchain_utils/bip/address/exception/exception.dart'; +import 'package:blockchain_utils/bip/bip.dart'; import '../../quick_hex.dart'; import 'package:blockchain_utils/utils/utils.dart'; import 'package:test/test.dart'; @@ -16,4 +17,129 @@ void main() { expect(decode.toHex(), i["decode"]); } }); + _testMultiKey(); + _testMultiKey2(); + _testMultiEdAccount4(); + _testMultiEdAccount(); + _testMultiEdAccount2(); + _testMultiEdAccount3(); + _testSignleKeyEd25519(); + _testSignleKeySecp256k1(); +} + +void _testMultiKey2() { + test("MultiKey account 2", () { + final privateKey1 = Ed25519PrivateKey.fromBytes(List.filled(32, 12)); + final privateKey2 = Ed25519PrivateKey.fromBytes(List.filled(32, 13)); + final privateKey3 = + Secp256k1PrivateKeyEcdsa.fromBytes(List.filled(32, 14)); + final account = AptosAddrEncoder().encodeMultiKey(publicKeys: [ + privateKey1.publicKey, + privateKey2.publicKey, + privateKey3.publicKey, + ], requiredSignature: 3); + expect(account, + "0x4b7c50ba0047625f75407f5dc73a0d00524c50016ea483d44439d5ee72369d05"); + }); +} + +void _testMultiKey() { + test("MultiKey account", () { + final privateKey1 = Ed25519PrivateKey.fromBytes(List.filled(32, 12)); + final privateKey2 = Ed25519PrivateKey.fromBytes(List.filled(32, 13)); + final privateKey3 = Ed25519PrivateKey.fromBytes(List.filled(32, 14)); + final account = AptosAddrEncoder().encodeMultiKey(publicKeys: [ + privateKey1.publicKey, + privateKey2.publicKey, + privateKey3.publicKey, + ], requiredSignature: 3); + expect(account, + "0x82a01ce96b00669a8ac358eac6551cc17dbb16f1841a5ecfe69f4750e20fe56c"); + }); +} + +void _testMultiEdAccount4() { + test("Multi Ed25519 account wrong public keys", () { + final privateKey1 = Ed25519PrivateKey.fromBytes(List.filled(32, 12)); + expect( + () => AptosAddrEncoder().encodeMultiEd25519Key(publicKeys: [ + privateKey1.publicKey, + ], threshold: 2), + throwsA(TypeMatcher())); + }); + test("Multi Ed25519 account wrong threshold", () { + final privateKey1 = Ed25519PrivateKey.fromBytes(List.filled(32, 12)); + expect( + () => AptosAddrEncoder().encodeMultiEd25519Key(publicKeys: [ + privateKey1.publicKey, + privateKey1.publicKey, + privateKey1.publicKey, + privateKey1.publicKey, + ], threshold: 7), + throwsA(TypeMatcher())); + }); +} + +void _testMultiEdAccount3() { + test("Multi Ed25519 account", () { + final privateKey1 = Ed25519PrivateKey.fromBytes(List.filled(32, 12)); + final privateKey2 = Ed25519PrivateKey.fromBytes(List.filled(32, 12)); + final privateKey3 = Ed25519PrivateKey.fromBytes(List.filled(32, 12)); + final account = AptosAddrEncoder().encodeMultiEd25519Key(publicKeys: [ + privateKey1.publicKey, + privateKey2.publicKey, + privateKey3.publicKey + ], threshold: 2); + expect(account, + "0x46b83ee243d37f8754a0bd90505b58bb19a098840e90ec11934192f04267fc41"); + }); +} + +void _testMultiEdAccount2() { + test("Multi Ed25519 account 3", () { + final privateKey1 = Ed25519PrivateKey.fromBytes(List.filled(32, 12)); + final privateKey2 = Ed25519PrivateKey.fromBytes(List.filled(32, 13)); + final privateKey3 = Ed25519PrivateKey.fromBytes(List.filled(32, 14)); + final account = AptosAddrEncoder().encodeMultiEd25519Key(publicKeys: [ + privateKey1.publicKey, + privateKey2.publicKey, + privateKey3.publicKey + ], threshold: 2); + expect(account, + "0xdab62cecd6d92eca7968f1184bce94992b062868d23ea09752c6d04ce6318407"); + }); +} + +void _testMultiEdAccount() { + test("Multi Ed25519 account 2", () { + final privateKey1 = Ed25519PrivateKey.fromBytes(List.filled(32, 12)); + final privateKey2 = Ed25519PrivateKey.fromBytes(List.filled(32, 13)); + final privateKey3 = Ed25519PrivateKey.fromBytes(List.filled(32, 14)); + final account = AptosAddrEncoder().encodeMultiEd25519Key(publicKeys: [ + privateKey1.publicKey, + privateKey2.publicKey, + privateKey3.publicKey + ], threshold: 3); + expect(account, + "0xbca0f886d39361116fc4dbe2c80f9ca7edae425030df03b4a158fd448faac91b"); + }); +} + +void _testSignleKeyEd25519() { + test("Ed25519 singleKey account", () { + final privateKey = Ed25519PrivateKey.fromBytes(List.filled(32, 12)); + final account = AptosAddrEncoder().encodeSingleKey(privateKey.publicKey); + expect(account, + "0x6f1468722b30e87e8be765554d244db068b8de2222bb9600cf5a03139922ef86"); + }); +} + +void _testSignleKeySecp256k1() { + test("Secp256k1 singleKey account", () { + final privateKey = + Secp256k1PrivateKeyEcdsa.fromBytes(List.filled(32, 12)); + final account = AptosAddrEncoder().encodeSingleKey(privateKey.publicKey); + expect(account, + "0x89dd43dcedf165f975202fae5f8aecf03013ebc14bb3c09a1431313b4ee52b02"); + }); } diff --git a/test/address/aptos/test_vector.dart b/test/address/aptos/test_vector.dart index 748672c..39d754d 100644 --- a/test/address/aptos/test_vector.dart +++ b/test/address/aptos/test_vector.dart @@ -39,7 +39,7 @@ final List> testVector = [ "public": "38752a8c0a8c3b717c1dca8b9065f9986f8e9a53460c6c1c44fc3ed73c1c4123", "address": - "0xa923c0b56623e687d1b0dd7bb701c700b1bc084eceb021f8368d557f8240ba9", + "0x0a923c0b56623e687d1b0dd7bb701c700b1bc084eceb021f8368d557f8240ba9", "decode": "0a923c0b56623e687d1b0dd7bb701c700b1bc084eceb021f8368d557f8240ba9", "params": {} @@ -237,7 +237,7 @@ final List> testVector = [ "public": "7bb8a4cc00d8d8109630068401a49bad1bc0f5476180a7f6783d45dc3d5b232b", "address": - "0x9fa67a3ed77b0b19e1ddfb70a8a9c46b3f95a27974033c87844c7337cf40dcb", + "0x09fa67a3ed77b0b19e1ddfb70a8a9c46b3f95a27974033c87844c7337cf40dcb", "decode": "09fa67a3ed77b0b19e1ddfb70a8a9c46b3f95a27974033c87844c7337cf40dcb", "params": {} @@ -399,7 +399,7 @@ final List> testVector = [ "public": "0136e0495a98bdaecc3429650273089f14a259404ee807d479d0fb6c988f7e51", "address": - "0x676a73d2a779e53e67885cf7c69433c509ed1d9fd96537dd8d9d381afb3d657", + "0x0676a73d2a779e53e67885cf7c69433c509ed1d9fd96537dd8d9d381afb3d657", "decode": "0676a73d2a779e53e67885cf7c69433c509ed1d9fd96537dd8d9d381afb3d657", "params": {} @@ -444,7 +444,7 @@ final List> testVector = [ "public": "e007c4a308f44e420ebb8ea9a0a919de4fde93c897488c1fa6a7c7fab22feebf", "address": - "0x7d439eba2cd32a21e0d7b62d95b3d366429872c34ae32e81212e4afcce78f50", + "0x07d439eba2cd32a21e0d7b62d95b3d366429872c34ae32e81212e4afcce78f50", "decode": "07d439eba2cd32a21e0d7b62d95b3d366429872c34ae32e81212e4afcce78f50", "params": {} diff --git a/test/address/sui/sui_test.dart b/test/address/sui/sui_test.dart new file mode 100644 index 0000000..a235bb5 --- /dev/null +++ b/test/address/sui/sui_test.dart @@ -0,0 +1,76 @@ +import 'package:blockchain_utils/bip/address/exception/exception.dart'; +import 'package:blockchain_utils/blockchain_utils.dart'; +import 'package:test/test.dart'; + +import 'test_vector.dart'; + +void main() { + test("sui secp256k1 address", () { + for (final i in secp256k1) { + final publicKey = BytesUtils.fromHexString(i["publickKey"]); + final address = SuiSecp256k1AddrEncoder().encodeKey(publicKey); + expect(address, i["address"]); + } + }); + test("sui secp256r1 address", () { + for (final i in secp256r1) { + final publicKey = BytesUtils.fromHexString(i["publickKey"]); + final address = SuiSecp256r1AddrEncoder().encodeKey(publicKey); + expect(address, i["address"]); + } + }); + test("sui ed25519 address", () { + for (final i in ed25519) { + final publicKey = BytesUtils.fromHexString(i["publickKey"]); + final address = SuiAddrEncoder().encodeKey(publicKey); + expect(address, i["address"]); + } + }); + test("sui multisig address", () { + for (final i in multisig) { + final publicKey = (i["publicKeys"] as List).map((e) { + final IPublicKey key = switch (e["type"]) { + "secp256r1" => + Nist256p1PublicKey.fromBytes(BytesUtils.fromHexString(e["key"])), + "secp256k1" => Secp256k1PublicKeyEcdsa.fromBytes( + BytesUtils.fromHexString(e["key"])), + "Ed25519" => + Ed25519PublicKey.fromBytes(BytesUtils.fromHexString(e["key"])), + _ => throw UnimplementedError() + }; + return SuiPublicKeyAndWeight(publicKey: key, weight: e["weight"]); + }).toList(); + final address = SuiAddrEncoder() + .encodeMultisigKey(pubKey: publicKey, threshold: i["threshold"]); + expect(address, i["address"]); + } + }); + + test("invalid multisig keys", () { + final key1 = _generateRandomKey(EllipticCurveTypes.ed25519); + final key2 = _generateRandomKey(EllipticCurveTypes.secp256k1); + final key3 = _generateRandomKey(EllipticCurveTypes.ed25519Monero); + expect( + () => SuiAddrEncoder().encodeMultisigKey(pubKey: [ + SuiPublicKeyAndWeight(publicKey: key1, weight: 1), + SuiPublicKeyAndWeight(publicKey: key2, weight: 2), + ], threshold: 5), + throwsA(TypeMatcher())); + expect( + () => SuiAddrEncoder().encodeMultisigKey(pubKey: [ + SuiPublicKeyAndWeight(publicKey: key1, weight: -1), + SuiPublicKeyAndWeight(publicKey: key2, weight: 1), + SuiPublicKeyAndWeight(publicKey: key3, weight: 1), + ], threshold: 5), + throwsA(TypeMatcher())); + }); +} + +IPublicKey _generateRandomKey(EllipticCurveTypes type) { + while (true) { + try { + return IPrivateKey.fromBytes(QuickCrypto.generateRandom(32), type) + .publicKey; + } catch (_) {} + } +} diff --git a/test/address/sui/test_vector.dart b/test/address/sui/test_vector.dart new file mode 100644 index 0000000..434668b --- /dev/null +++ b/test/address/sui/test_vector.dart @@ -0,0 +1,553 @@ +const List> secp256k1 = [ + { + "address": + "0x6fdcc2f897040a34c33e1064fda3edab9148b78148300cd7a33d137b1e006ca4", + "publickKey": + "03ac0ece872e1e61c9d7629f5e8047602b36b8692fe058e9386dc4314c6f92cda8" + }, + { + "address": + "0x5960c7eb1d1133e33f8ca46367e8fb2988d32a7e865f613fd248ccf6a27fb77d", + "publickKey": + "025860c703248f4f41c1f3e3b2d9756099a8d7bf31633f1bdeaef652a467bfd961" + }, + { + "address": + "0x74d08692f313874d7332d9195271c52595a929f07d1451e162bd73fb4621b359", + "publickKey": + "039041ffe43a529080557cfa5b429d01fc1eefc64806da8d63746de7646e0b501e" + }, + { + "address": + "0x6c09301bcb9b08b4ecf0739e8722a913b4c41b6c13526818ade266d4f3df09e0", + "publickKey": + "02d8dcd23f1f9813cd6ef2110c8e866a7dd25db889b12482308f93407153682c65" + }, + { + "address": + "0xa4ee854f079b75f3e77c36b8b7791f387231de37af6a05b2ca761721e754d47c", + "publickKey": + "03c6b1eb20904f5ade830a61ea2286cee028f8be973276fd89e8a0b746b78d0649" + }, + { + "address": + "0xbd4aed1aa03f2934ee26284ac20e7b29dbef499d8be2bbbfc4ce852ff524cbbe", + "publickKey": + "0263d08afb347cfaecac495b73717a80e2999a71125e268233e74a119f6e995a5b" + }, + { + "address": + "0x61d2004303d115627a8c51a89bf0ddc2bbc4e6b538932887d1000e98c3819d73", + "publickKey": + "033d43bf5992a6c609809d38d73a4ee09b5a1e986b6f17af339691a4ca5415829d" + }, + { + "address": + "0x5967d723aadee8acb7061aa0849f5c15f3c584a17ce934ee3c9bdaa9c5773c2b", + "publickKey": + "0283ed9df3cb6254b062d3a26ceaa7b0dc74f30af553146e206ef5e42214012144" + }, + { + "address": + "0x767bcd0e20b57d2a5cdc82e1a8030110c1e7958575b88b5d174a1047f601fa3c", + "publickKey": + "03da5a2a5cbf49b37ffd42529eede3511b02965d57998342cace07ad393ece2396" + }, + { + "address": + "0xca3b384792e66e2a255fdbf274d495af1b05e1c94536c5e0c9dd64e57fa312b9", + "publickKey": + "02f010bc46a75746036c2009c468854410f0b0e8cc98fc46c4a8c88e581884283d" + } +]; + +const List> ed25519 = [ + { + "address": + "0x494312728a1904dc6571d8ca59577a15c5da5c95448ee667cda69bf2194b8e52", + "publickKey": + "df7b6d7719ce754064db2b237bd941ce39065cd8438912cfd8d784d5ffc30687" + }, + { + "address": + "0xd56d84d660ef19d4003eae1488e7f9b0b79b1e28307f3622638a0b23f7646e1c", + "publickKey": + "9bc18c5c1ef052bbf985b00e8d31360be2010e178b3b65ca09515ea039a51899" + }, + { + "address": + "0xc52c3643e56c012160e93af72aeb54fa07620bba3fe21351df4b22f170701d69", + "publickKey": + "4fe73fa8c1fef271803a73ebfaac57f4a2143405571599766e4ca651198d5d8a" + }, + { + "address": + "0xb4ba107f9374cbccf7299697160bc8a75bc0c502c51ae3aa1119265a040c6c86", + "publickKey": + "be8253ac6e2000ae81cdc19b9e0c2f8787dbf028ce9e8570df8b32af5b0f32e2" + }, + { + "address": + "0x66d78e748ffb2e398175abf95d0296d82abe1a60762620facaecf356786f5f21", + "publickKey": + "b1b815470de67a56d3097ee6822cb3714c85d3816064b0b8358930935cc05615" + }, + { + "address": + "0x764c481c454c687bb36dce41c5132409e132bda1029bea30bbd99e56b9b6073c", + "publickKey": + "580ea1e53f76b3c948bc24ea35f52b56a247ce652a967d94ad057bc2ca2d1176" + }, + { + "address": + "0xff050dc161e05e660382c2555b123f972204ff1cccb6891b0afb04e8ec3cc891", + "publickKey": + "0e1523048ea90912f37d2b8a5f5128170aaf71788f82ea4ba75cce3eb1c5d545" + }, + { + "address": + "0xb747e1e0f9301c04ad746c3e25e981d718f19cecd87352ffc61338b375826656", + "publickKey": + "c53596eb943a7a066b41f0a0dd41fa161c8fc183913c2349840781e46a44f859" + }, + { + "address": + "0xac066f1a4d2d45868ed4099bba7e856372ed9aac2dbfe11584f01afb96db2a90", + "publickKey": + "087b5fd1942cac4acedbf3e2df3dc64ea66ab46a2dadf94d598bb44a997f729d" + }, + { + "address": + "0x98f8572f17b23ebce77ed62049586b2b78d511102fcae41e70478be66707ce6c", + "publickKey": + "ca15d6ea6a7acc591b10052c5d90a45fb2c6d9b41f8235a05b6175e5bdd812ee" + } +]; + +const List> secp256r1 = [ + { + "address": + "0x6dea367a6b60fb51fd70781fd1f820f86b0e7aa96fa039e1487de64791058d4a", + "publickKey": + "02a83c623c92bd9be0edf28a4ce591439377ebba3afdf9518dd729777639936b0e" + }, + { + "address": + "0xdcdba6b234e8460629166e87bf0df20c09d25cbe75dd355a5549e18814726884", + "publickKey": + "0337a37b0989f0deba10af1ed2627ed4a4bf33592d95d0e955d0f1942739fb23cc" + }, + { + "address": + "0xe4b345052e36d6586fa2daa080e157038ba074b176380de0abd3f6d08e470580", + "publickKey": + "035e0e0a755e78da6f2cf4ea40da7625ffe01ae4e16bd988d0d05ef8e9ec17ce00" + }, + { + "address": + "0xe9a2b4baec75f7bf743e1cc4ede8ffa9582f4ba8e3500ddb0c82387b5705d493", + "publickKey": + "02fdf865f05dffbc74b9f10dadc1df2f019ac7529d025d7214e29f85e0827c1539" + }, + { + "address": + "0x39b03e08d9a0deeac8b7f68f8ae2cb719f7c141242a31666acbd74f35700d859", + "publickKey": + "02ff221fc5e92f8b710941bde884416b54f27c04092b6280d93728b5fe0b5b6e2e" + }, + { + "address": + "0x682672a695604134125765832cb1de626a2e2a9a0e39e0712e39a2fe4aa40866", + "publickKey": + "0305113c0c27ee7abcea47d51c2da84c1dad146be7975346810727c80a378e3640" + }, + { + "address": + "0x0540be51f34e9fe1e153d754b84e63118c30083e480b5de1bc05beb16148a3cc", + "publickKey": + "02946887d37ec5cc78c46380e930424b3a33b401c5be0aee09bb024c838f7c9a53" + }, + { + "address": + "0x0e8d498a3f64363d9ed1bbd80cd729d593466044068d2191adc3f9cd9017a55a", + "publickKey": + "036cb9069968084070345b8ccb78aaa35b1f051d3495ed3f9da16106370e32ad35" + }, + { + "address": + "0xc9d410deeae2a12103d6908d334ef574774ead6a6255982ea9795abb332132d3", + "publickKey": + "0395dfd56ce27d2a44e53e9b3689a4ac709572792d5458409cd5945316286e599e" + }, + { + "address": + "0xba7af126bcc5a15d5131ef30fb9fd00917e5a029ce431f7d39c631fc17a2a5bc", + "publickKey": + "03b8c33b06b976baf0edbc98841cb1f319148d9b3fc6895014374f41e2b99b4aa7" + } +]; + +const List> multisig = [ + { + "address": + "0x17f99fb14575e80ac43b76a8d709601096c7db4a5962ae371986939969975002", + "threshold": 3, + "publicKeys": [ + { + "key": + "02220f4b9b53d220b515f62b0673acc5533484cb1bccef10092732451e04782db3", + "type": "secp256r1", + "weight": 1 + }, + { + "key": + "6c6a6f1596747772a504a5dbdba87b65d2c2287d4370ea2b7283c922da184cb8", + "type": "Ed25519", + "weight": 1 + }, + { + "key": + "03da4db89aa30dfe1a8b5edbe45d2bcc3446187191b94001c54d2c90135e9fdbdd", + "type": "secp256k1", + "weight": 1 + } + ] + }, + { + "address": + "0xd756802002247d9d5a63bdcc1db2a66fda410f2652b901d0fb0539a54638a814", + "threshold": 3, + "publicKeys": [ + { + "key": + "03af2d9155c0ccf738164bacdbc2e5d376cd20c2331487ac0a3bd1197fff2fbf5a", + "type": "secp256r1", + "weight": 1 + }, + { + "key": + "17da85e4f4ff5539d79c29caec9b506d9664116c57e3a0ef99608c07eeb3e06d", + "type": "Ed25519", + "weight": 1 + }, + { + "key": + "03c7b7a767df6f424ce89949c43bffacf1976e2c91f3966abe7640a97753284f6a", + "type": "secp256k1", + "weight": 1 + } + ] + }, + { + "address": + "0x6b169330d0977e80273ebcafd233c86a849aa3b61b989112358f93c4e52a520a", + "threshold": 3, + "publicKeys": [ + { + "key": + "0315a23f7fd7b05ee17ffdf461a5fc823b575d602e89f6b8a7e7de480279cdd892", + "type": "secp256r1", + "weight": 1 + }, + { + "key": + "e2afd3700ef6f6c22908881cb648a2fbebc43c470230b5990fb271f5355f7e21", + "type": "Ed25519", + "weight": 1 + }, + { + "key": + "02137e678aa5fc51a0d6b1d8dbc37063609a813df2f5a0cea2996b5e4ecfe2ab8d", + "type": "secp256k1", + "weight": 1 + } + ] + }, + { + "address": + "0x10757cf96c233019574137893cf65ec1f494c4ce325ef6af525f54a2c3a7bac0", + "threshold": 3, + "publicKeys": [ + { + "key": + "039032a65d37782438ac3b7fc3b2e979552eae9ee489b2a3804779ac8470690e0c", + "type": "secp256r1", + "weight": 1 + }, + { + "key": + "de0db903e196d58705b9bad2a4bf83f293df068b506fbc3d6437c85c873c8f68", + "type": "Ed25519", + "weight": 1 + }, + { + "key": + "0256bbb740c10c9d69ed662db2be5e41962fe82b0cdca3029319c394786d8b2e78", + "type": "secp256k1", + "weight": 1 + } + ] + }, + { + "address": + "0x24df66b9076e39073b38730a536fa3a2eedc565568abd3f30f1345575bcfab0c", + "threshold": 3, + "publicKeys": [ + { + "key": + "0291c0cbae34a6a8e5ca155166ee1d91e7461a5b22955e014c893ac799c74777e5", + "type": "secp256r1", + "weight": 1 + }, + { + "key": + "10565c6f090f4974f800fa46d32093cf35e6fc803ea91680b0c11b3421cd1731", + "type": "Ed25519", + "weight": 1 + }, + { + "key": + "026a4ad71e93109a5c4d95ea7030df53e79e8ccabe9d927240cd0c417de9d2bb1b", + "type": "secp256k1", + "weight": 1 + } + ] + }, + { + "address": + "0xc7977def3d6fe55ebe9a3d76db273b38e193c6222751cef5a7a11f1253285367", + "threshold": 3, + "publicKeys": [ + { + "key": + "02b6113aa5d4685fe516d07ade3c140111134f3dd58ec2b7f4f9035a1626765c3f", + "type": "secp256r1", + "weight": 1 + }, + { + "key": + "340d3443e35e721a307669141d6865beafb35c8409e468736518891d41e480ca", + "type": "Ed25519", + "weight": 1 + }, + { + "key": + "0274396a304274ee701dd633763e9a4c5009a7b18648433c8784ea42c245e3fb15", + "type": "secp256k1", + "weight": 1 + } + ] + }, + { + "address": + "0x4b2507d07cc0aaa2d0dd866ccc9cf65fad9599c93e5feacbccc4108d35a8d2bb", + "threshold": 3, + "publicKeys": [ + { + "key": + "0391a0231b6aeed045f94dbf17a776fe0d4b110c7dcdb98629970190ea5520226f", + "type": "secp256r1", + "weight": 1 + }, + { + "key": + "2dcd9e312df8b9bcaa1f0832f531c62c68c2c707e6b212bf032104673689452b", + "type": "Ed25519", + "weight": 1 + }, + { + "key": + "020ec258d1ab949303fcb2665fe10b94d9c67bdfb1bf6fe8a6fef1da9f484e3c4f", + "type": "secp256k1", + "weight": 1 + } + ] + }, + { + "address": + "0x0579b33e955bbe3e5ccd27c3dc10354768e1ae94d0290a6ad8ed3402f1539f8b", + "threshold": 3, + "publicKeys": [ + { + "key": + "03efad9e0b6904e7b95196887e8d193141d967eceb43afd1de8463a5a65217f980", + "type": "secp256r1", + "weight": 1 + }, + { + "key": + "8ef220886ca064ed12dff10e625e87ced3053182ee836db0fe62b7d0484da9d1", + "type": "Ed25519", + "weight": 1 + }, + { + "key": + "0282cf30d2e03cbede695c36056f1b05907c9bfa6c6fb802980f17030c697792e5", + "type": "secp256k1", + "weight": 1 + } + ] + }, + { + "address": + "0xc72692583af84bb11f470282792f3d67f2f2d411cf6a71aa7fa1190e73faa734", + "threshold": 3, + "publicKeys": [ + { + "key": + "03f7f925bfc0fa1f3318660eeeb98ce06be391a8d107d1b8835b5972ee85dc4a05", + "type": "secp256r1", + "weight": 1 + }, + { + "key": + "05eb038f098d8df698ae332d3cd1a21daad0a20482db475e1edeea84d99cde08", + "type": "Ed25519", + "weight": 1 + }, + { + "key": + "0293d4bf21de3717e72385b7368264afd2c86ec2733e40298aeeca74eea8e532c7", + "type": "secp256k1", + "weight": 1 + } + ] + }, + { + "address": + "0xb961b7bf0f4eee416e16b37c685fd88d0e8fda55678ef9660f77c4b3fafddbc7", + "threshold": 3, + "publicKeys": [ + { + "key": + "02ca661b3b4aa3cac5aa857a4f85eb1e199b48e2ce51be305cf9823377c35aa520", + "type": "secp256r1", + "weight": 1 + }, + { + "key": + "00e4c163df1e9501b5ce983f200599889e302b025d0a1f79091afd4393712405", + "type": "Ed25519", + "weight": 1 + }, + { + "key": + "025f03afd070983d67438b6224d0150e0754006af795456bd91874a608ddd09285", + "type": "secp256k1", + "weight": 1 + } + ] + }, + { + "address": + "0x3a8325de3d0abcb8e090309552a6bbb11ca56cca6e98e33ff20d461778ba82d4", + "threshold": 2, + "publicKeys": [ + { + "key": + "0241887cf395b997320ca098780c0928f22f133dc0d1cd291cc24de871f07809a8", + "type": "secp256r1", + "weight": 1 + }, + { + "key": + "44d2169bafa18899b148b8803a19ff9e1527ee51cec60bd152cfe99fc8005e55", + "type": "Ed25519", + "weight": 1 + } + ] + }, + { + "address": + "0xa3d498d87a2a87e836dfc7e397f39e836a2e7a673a4c123555a95b3901f2c922", + "threshold": 2, + "publicKeys": [ + { + "key": + "02bfba1f8185ec6c4fcc365776a7785e3c504f15f93fcf91b9e6d936f24b255c53", + "type": "secp256r1", + "weight": 1 + }, + { + "key": + "13ee90bef10d87f33dca8c12e53cf9200fc9b02e99d91eb729e9808b408bb9e9", + "type": "Ed25519", + "weight": 1 + } + ] + }, + { + "address": + "0x1508d9d931e921a98643088cac5685d7a9bb60a3a8d583337f4bea640f5ee8b8", + "threshold": 5, + "publicKeys": [ + { + "key": + "02ace9ffc16ffb80018df6f2b096021ecae253362081f41f7404f5f1c775f29f03", + "type": "secp256r1", + "weight": 1 + }, + { + "key": + "b039687b35d9c508ba23ba1b04fee4bc97c8f852badebd09f2ae0d238311762d", + "type": "Ed25519", + "weight": 1 + }, + { + "key": + "039ef7a05c4747818aef9dca8e3c34f25665846a0734400c0d090ee75e67d526a7", + "type": "secp256k1", + "weight": 1 + }, + { + "key": + "020cc047ca3317b156d5d6dc5c3d3a65a8c8a284aff10d13f96d2e64f153acbdba", + "type": "secp256k1", + "weight": 1 + }, + { + "key": + "036e9fe2aebac7d746bca90f08f63032a2ca874e6d1eec37996155d165ebe421d6", + "type": "secp256k1", + "weight": 3 + } + ] + }, + { + "address": + "0x7a33a9774f18bdb8cb673868fe2bcb4de2d7b22f754d05ecbf27db32ee81722a", + "threshold": 5, + "publicKeys": [ + { + "key": + "032a1d95939ec38ae1a0438031189c6d8063ee6016ebd413c07952ada4d7d2656c", + "type": "secp256r1", + "weight": 1 + }, + { + "key": + "6d7644ec8fa51bc7be05a3ddc0f41776a6890c15c611ff727077285dd5681474", + "type": "Ed25519", + "weight": 1 + }, + { + "key": + "0325affc9bf2fac96bc2c98a43ff2505254a3aa92994f38a16e09956b1ed0a0436", + "type": "secp256k1", + "weight": 1 + }, + { + "key": + "029456d88a3c387d1011b96a3012e46c168c849106fb7d52e0d6491458c069e331", + "type": "secp256k1", + "weight": 1 + }, + { + "key": + "02477bf445f3088e642e518aadb631e211e3d0dca2c6095a7fa30921e9e689abe4", + "type": "secp256k1", + "weight": 3 + } + ] + } +]; diff --git a/test/bip/bip44/test_vector.dart b/test/bip/bip44/test_vector.dart index d5043fa..2843bec 100644 --- a/test/bip/bip44/test_vector.dart +++ b/test/bip/bip44/test_vector.dart @@ -131,7 +131,7 @@ final List> testVector = [ "account_public": "xpub6DXTe9rzS56ezLEDmb2D9FEWPr5NCeueDQDxWv1AquSAWSDA5HtPwKmgZWbLtbvyiB924SNvUTVLJQmM9NYQ6e373zixmdAmugmZtNiGNaX", "address": - "0x2f961b62e1ff88d77cb469c3a7bb3f9b3c2a7a68426d5240397fae3a4726ec5" + "0x02f961b62e1ff88d77cb469c3a7bb3f9b3c2a7a68426d5240397fae3a4726ec5" }, { "account": diff --git a/test/signer/bitcoin_test.dart b/test/signer/bitcoin_test.dart index 3698c72..e62dcbb 100644 --- a/test/signer/bitcoin_test.dart +++ b/test/signer/bitcoin_test.dart @@ -1,4 +1,4 @@ -import 'package:blockchain_utils/signer/bitcoin_signer.dart'; +import 'package:blockchain_utils/signer/bitcoin/bitcoin_signer.dart'; import 'package:blockchain_utils/utils/binary/utils.dart'; import 'package:test/test.dart';