From c5bf8027722e3b43642679245fe8bfa31826ab6e Mon Sep 17 00:00:00 2001 From: redDwarf03 Date: Mon, 30 Dec 2024 18:17:44 +0100 Subject: [PATCH] chore: :poop: Force transaction version 3 --- CHANGELOG.md | 1 + lib/src/model/keychain.dart | 5 +- lib/src/model/keychain.freezed.dart | 2 +- lib/src/model/keychain.g.dart | 2 +- lib/src/model/transaction.dart | 8 +-- lib/src/model/transaction.freezed.dart | 2 +- lib/src/model/transaction.g.dart | 2 +- test/keychain_test.dart | 92 ++++++++++++++++++++++++++ test/transaction_test.dart | 40 +++++++++++ 9 files changed, 144 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98747cdb..44d21bd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - BREAKING CHANGES: Add param to `newKeychainTransaction` method to allow many services instead of only one - BREAKING CHANGES: Add `ArchethicKeychainNotExistsException` to catch error when no keychain exists (impact on `getKeychain` method) +- Force transaction version 3 # 6.0.0-beta diff --git a/lib/src/model/keychain.dart b/lib/src/model/keychain.dart index 2236b861..61db10af 100644 --- a/lib/src/model/keychain.dart +++ b/lib/src/model/keychain.dart @@ -20,12 +20,13 @@ part 'keychain.freezed.dart'; part 'keychain.g.dart'; const int keychainOriginId = 0; +const kVersion3 = 3; @freezed class Keychain with _$Keychain { const factory Keychain({ @Uint8ListConverter() Uint8List? seed, - @Default(1) final int version, + @Default(kVersion3) final int version, @Default({}) final Map services, }) = _Keychain; const Keychain._(); @@ -66,7 +67,7 @@ class Keychain with _$Keychain { }); return concatUint8List([ - toByteArray(version, length: 4), + toByteArray(kVersion3, length: 4), Uint8List.fromList([seed!.length]), seed!, Uint8List.fromList([services.length]), diff --git a/lib/src/model/keychain.freezed.dart b/lib/src/model/keychain.freezed.dart index fd9e73d7..85ffc851 100644 --- a/lib/src/model/keychain.freezed.dart +++ b/lib/src/model/keychain.freezed.dart @@ -135,7 +135,7 @@ class __$$KeychainImplCopyWithImpl<$Res> class _$KeychainImpl extends _Keychain { const _$KeychainImpl( {@Uint8ListConverter() this.seed, - this.version = 1, + this.version = kVersion3, final Map services = const {}}) : _services = services, super._(); diff --git a/lib/src/model/keychain.g.dart b/lib/src/model/keychain.g.dart index 6207f95c..57900292 100644 --- a/lib/src/model/keychain.g.dart +++ b/lib/src/model/keychain.g.dart @@ -9,7 +9,7 @@ part of 'keychain.dart'; _$KeychainImpl _$$KeychainImplFromJson(Map json) => _$KeychainImpl( seed: const Uint8ListConverter().fromJson(json['seed'] as List?), - version: (json['version'] as num?)?.toInt() ?? 1, + version: (json['version'] as num?)?.toInt() ?? kVersion3, services: (json['services'] as Map?)?.map( (k, e) => MapEntry(k, Service.fromJson(e as Map)), ) ?? diff --git a/lib/src/model/transaction.dart b/lib/src/model/transaction.dart index 6f2dd5ea..944ec5f4 100644 --- a/lib/src/model/transaction.dart +++ b/lib/src/model/transaction.dart @@ -25,7 +25,7 @@ import 'package:freezed_annotation/freezed_annotation.dart'; part 'transaction.freezed.dart'; part 'transaction.g.dart'; -const int cVersion = 3; +const int kVersion = 3; const Map txTypes = { /// User based transaction types @@ -95,7 +95,7 @@ class Transaction with _$Transaction { ValidationStamp? validationStamp, /// Version of the transaction (used for backward compatiblity) - @Default(cVersion) int version, + @Default(kVersion) int version, }) = _Transaction; const Transaction._(); @@ -112,7 +112,7 @@ class Transaction with _$Transaction { /// Convert the transaction in JSON String toNodeRPC() { final json = jsonEncode({ - 'version': version, + 'version': kVersion, 'address': address?.address ?? '', 'type': type, 'data': { @@ -542,7 +542,7 @@ class Transaction with _$Transaction { Uint8List.fromList(toByteArray(data!.recipients.length)); return concatUint8List([ - toByteArray(version, length: 4), + toByteArray(kVersion, length: 4), Uint8List.fromList(hexToUint8List(address!.address!)), Uint8List.fromList([txTypes[type]!]), bufCodeSize, diff --git a/lib/src/model/transaction.freezed.dart b/lib/src/model/transaction.freezed.dart index d1600396..dcfdf74c 100644 --- a/lib/src/model/transaction.freezed.dart +++ b/lib/src/model/transaction.freezed.dart @@ -391,7 +391,7 @@ class _$TransactionImpl extends _Transaction { this.previousSignature, this.type, this.validationStamp, - this.version = cVersion}) + this.version = kVersion}) : _crossValidationStamps = crossValidationStamps, _inputs = inputs, super._(); diff --git a/lib/src/model/transaction.g.dart b/lib/src/model/transaction.g.dart index e1257ccb..b910140e 100644 --- a/lib/src/model/transaction.g.dart +++ b/lib/src/model/transaction.g.dart @@ -36,7 +36,7 @@ _$TransactionImpl _$$TransactionImplFromJson(Map json) => ? null : ValidationStamp.fromJson( json['validationStamp'] as Map), - version: (json['version'] as num?)?.toInt() ?? cVersion, + version: (json['version'] as num?)?.toInt() ?? kVersion, ); Map _$$TransactionImplToJson(_$TransactionImpl instance) => diff --git a/test/keychain_test.dart b/test/keychain_test.dart index c7db9a94..589f29c9 100644 --- a/test/keychain_test.dart +++ b/test/keychain_test.dart @@ -524,4 +524,96 @@ void main() { }); }, ); + + test( + 'should create a keychain with version forced to 3', + tags: [TestTags.integration], + () async { + final walletSeed = generateRandomSeed(); + const endpoint = 'https://testnet.archethic.net'; + final apiService = ApiService(endpoint); + final walletKeyPair = crypto.deriveKeyPair(walletSeed, 0); + + /// Generate keyChain Seed from random value + final keychainSeed = uint8ListToHex( + Uint8List.fromList( + List.generate(32, (int i) => Random.secure().nextInt(256)), + ), + ); + dev.log('keychainSeed: $keychainSeed'); + + /// Many services for wallet + const kServiceName1 = 'main-uco-1'; + const kDerivationPathWithoutIndex1 = "m/650'/$kServiceName1/"; + const index = '0'; + const kDerivationPath1 = '$kDerivationPathWithoutIndex1$index'; + dev.log('kDerivationPath1: $kDerivationPath1'); + + const kServiceName2 = 'main-uco-2'; + const kDerivationPathWithoutIndex2 = "m/650'/$kServiceName2/"; + const kDerivationPath2 = '$kDerivationPathWithoutIndex2$index'; + dev.log('kDerivationPath2: $kDerivationPath2'); + + const kServiceName3 = 'main-uco-3'; + const kDerivationPathWithoutIndex3 = "m/650'/$kServiceName3/"; + const kDerivationPath3 = '$kDerivationPathWithoutIndex3$index'; + dev.log('kDerivationPath3: $kDerivationPath3'); + + final originPrivateKey = apiService.getOriginKey(); + dev.log('originPrivateKey: $originPrivateKey'); + + // We simulate version 4 + const blockchainTxVersion = 4; + + /// Create Keychain from keyChain seed and wallet public key to encrypt secret + final keychainTransaction = apiService.newKeychainTransaction( + keychainSeed, + [ + uint8ListToHex( + Uint8List.fromList(walletKeyPair.publicKey!), + ), + ], + Uint8List.fromList(hexToUint8List(originPrivateKey)), + blockchainTxVersion, + servicesMap: { + kServiceName1: kDerivationPath1, + kServiceName2: kDerivationPath2, + kServiceName3: kDerivationPath3, + }, + ); + dev.log( + 'keychainTransaction: ${keychainTransaction.toNodeRPC()}', + ); + + /// Create Keychain Access for wallet + final accessKeychainTx = apiService.newAccessKeychainTransaction( + walletSeed, + Uint8List.fromList( + hexToUint8List(keychainTransaction.address!.address!), + ), + Uint8List.fromList(hexToUint8List(originPrivateKey)), + blockchainTxVersion, + ); + dev.log('accessKeychainTx: ${accessKeychainTx.toNodeRPC()}'); + + await ArchethicTransactionSender( + apiService: apiService, + ).send( + transaction: keychainTransaction, + ); + + await ArchethicTransactionSender( + apiService: apiService, + ).send( + transaction: accessKeychainTx, + ); + + /// Get KeyChain Wallet + final keychain = await apiService.getKeychain(walletSeed); + + expect(keychain.services.keys.elementAt(0), 'main-uco-1'); + expect(keychain.services.keys.elementAt(1), 'main-uco-2'); + expect(keychain.services.keys.elementAt(2), 'main-uco-3'); + }, + ); } diff --git a/test/transaction_test.dart b/test/transaction_test.dart index ecbf7c2e..fb6198d3 100644 --- a/test/transaction_test.dart +++ b/test/transaction_test.dart @@ -860,6 +860,46 @@ condition inherit: [ transaction: tx, ); }); + + test( + 'sending Tx with named recipient action should work (version 4 forced)', + () async { + final apiService = ApiService('https://testnet.archethic.net'); + + const txChainAddress = + '00009a4e6ef5a1358db5e3406b825848b396c43ef1bde2bb0a7cc32ac9ff9512aa09'; + final originPrivateKey = apiService.getOriginKey(); + final lastTx = (await apiService + .getTransactionIndex([txChainAddress]))[txChainAddress] ?? + 0; + const text = 'Test trigger HelloWorld 001'; + final tx = Transaction( + // Version 4 forced + version: 4, + type: 'data', + data: Data.fromJson({ + 'code': '', + 'ownerships': >[], + 'ledger': { + 'uco': {'transfers': []}, + 'token': {'transfers': []}, + }, + }), + ) + .addRecipient( + '00000E7C4C2EB7A16DA0A15811317FA828D162122AD79E1356550E5ED19CF559BF3F', + ) + .setContent(text) + .build(seed, lastTx) + .transaction + .originSign(originPrivateKey); + + await ArchethicTransactionSender( + apiService: apiService, + ).send( + transaction: tx, + ); + }); }, ); }