From 67de2caffc7bd0c05a7a2ce42ca01ab8d5ba2856 Mon Sep 17 00:00:00 2001 From: AionJayT <jay.tseng@aion.network> Date: Mon, 4 Mar 2019 16:30:20 -0500 Subject: [PATCH 01/15] rework avm integration with mpm branch --- .gitmodules | 4 +- aion_fastvm | 2 +- aion_vm_api | 2 +- build.gradle | 1 + gradle.properties | 8 + lib/org-aion-avm-api.jar | Bin 0 -> 18049 bytes lib/org-aion-avm-core.jar | Bin 413264 -> 369222 bytes lib/org-aion-avm-rt.jar | Bin 161160 -> 154725 bytes lib/org-aion-avm-userlib.jar | Bin 0 -> 36184 bytes modAion/build.gradle | 5 +- modAion/src/module-info.java | 4 +- .../src/org/aion/zero/api/BlockConstants.java | 5 +- .../aion/zero/db/AionContractDetailsImpl.java | 43 +- .../org/aion/zero/db/AionRepositoryCache.java | 36 +- .../org/aion/zero/types/A0BlockHeader.java | 32 +- .../org/aion/zero/types/AionInternalTx.java | 15 +- .../org/aion/zero/types/AionTransaction.java | 35 +- .../aion/zero/types/AionTxExecSummary.java | 41 +- .../org/aion/zero/types/AionTxReceipt.java | 6 +- .../src/org/aion/zero/types/IAionBlock.java | 6 +- .../org/aion/types/AionTransactionTest.java | 28 +- modAionBase/.classpath | 6 - modAionBase/.gitignore | 2 - modAionBase/.project | 17 - modAionBase/build.gradle | 10 - modAionBase/src/module-info.java | 10 - modAionBase/src/org/aion/base/Constant.java | 6 - .../src/org/aion/base/db/DetailsProvider.java | 10 - .../src/org/aion/base/db/Flushable.java | 5 - .../base/db/IByteArrayKeyValueDatabase.java | 3 - .../aion/base/db/IByteArrayKeyValueStore.java | 3 - .../org/aion/base/db/IContractDetails.java | 186 ----- .../src/org/aion/base/db/IDatabase.java | 144 ---- .../src/org/aion/base/db/IKeyValueStore.java | 159 ---- .../src/org/aion/base/db/IPruneConfig.java | 37 - .../src/org/aion/base/db/IRepository.java | 99 --- .../org/aion/base/db/IRepositoryCache.java | 93 --- .../org/aion/base/db/IRepositoryConfig.java | 21 - .../org/aion/base/db/IRepositoryQuery.java | 150 ---- .../org/aion/base/db/PersistenceMethod.java | 14 - .../src/org/aion/base/timer/ITimer.java | 12 - .../org/aion/base/timer/PoisonPillTask.java | 13 - .../src/org/aion/base/timer/StackTimer.java | 71 -- .../aion/base/timer/StackTimerRunnable.java | 128 ---- .../src/org/aion/base/timer/TimerDummy.java | 21 - .../src/org/aion/base/timer/TimerTask.java | 64 -- .../src/org/aion/base/type/AionAddress.java | 149 ---- .../src/org/aion/base/type/Hash256.java | 166 ----- .../src/org/aion/base/type/IBlock.java | 38 - .../src/org/aion/base/type/IBlockHeader.java | 53 -- .../org/aion/base/type/IBlockIdentifier.java | 9 - .../src/org/aion/base/type/IBlockSummary.java | 10 - .../org/aion/base/type/IPowBlockHeader.java | 15 - .../src/org/aion/base/type/ISolution.java | 4 - .../src/org/aion/base/type/ITransaction.java | 22 - .../org/aion/base/type/ITxExecSummary.java | 19 - .../src/org/aion/base/type/ITxReceipt.java | 16 - .../src/org/aion/base/util/ByteArrayMap.java | 132 ---- .../org/aion/base/util/ByteArrayWrapper.java | 108 --- .../src/org/aion/base/util/ByteUtil.java | 704 ------------------ .../src/org/aion/base/util/Bytesable.java | 11 - .../src/org/aion/base/util/Copyable.java | 11 - .../aion/base/util/FastByteComparisons.java | 46 -- .../src/org/aion/base/util/Functional.java | 82 -- modAionBase/src/org/aion/base/util/Hex.java | 109 --- .../src/org/aion/base/util/HexEncoder.java | 164 ---- .../base/util/ImmutableByteArrayWrapper.java | 92 --- .../src/org/aion/base/util/MAFast.java | 55 -- .../src/org/aion/base/util/NativeLoader.java | 144 ---- .../src/org/aion/base/util/TypeConverter.java | 53 -- modAionBase/src/org/aion/base/util/Utils.java | 328 -------- .../src/org/aion/base/vm/IDataWord.java | 19 - .../org/aion/base/vm/VirtualMachineSpecs.java | 8 - .../test/org/aion/base/type/AddressTest.java | 205 ----- .../test/org/aion/base/type/Hash256Test.java | 195 ----- .../test/org/aion/base/util/BIUtilTest.java | 166 ----- .../aion/base/util/ByteArrayWrapperTest.java | 81 -- .../test/org/aion/base/util/ByteUtilTest.java | 445 ----------- modAionImpl/build.gradle | 10 +- modAionImpl/src/module-info.java | 2 +- .../src/org/aion/equihash/EquiUtils.java | 5 +- .../src/org/aion/equihash/EquiValidator.java | 10 +- .../src/org/aion/equihash/Equihash.java | 13 +- .../src/org/aion/equihash/EquihashMiner.java | 7 +- .../src/org/aion/equihash/FullStepRow.java | 2 +- .../aion/equihash/OptimizedEquiValidator.java | 24 +- .../{Solution.java => SolutionImpl.java} | 6 +- modAionImpl/src/org/aion/net/Peer.java | 2 +- .../src/org/aion/utils/NativeLibrary.java | 2 +- .../src/org/aion/zero/impl/A0BCConfig.java | 2 +- .../org/aion/zero/impl/AionBlockLoader.java | 1 - .../aion/zero/impl/AionBlockchainImpl.java | 45 +- .../src/org/aion/zero/impl/AionGenesis.java | 19 +- .../src/org/aion/zero/impl/AionHub.java | 10 +- .../src/org/aion/zero/impl/AionHubUtils.java | 16 +- .../org/aion/zero/impl}/ExecutorPipeline.java | 4 +- .../aion/zero/impl/GenesisBlockLoader.java | 8 +- .../aion/zero/impl/StandaloneBlockchain.java | 51 +- .../aion/zero/impl/blockchain/AionImpl.java | 67 +- .../impl/blockchain/AionPendingStateImpl.java | 20 +- .../impl/blockchain/ChainConfiguration.java | 10 +- .../aion/zero/impl/blockchain/IAionChain.java | 10 +- .../zero/impl/blockchain/PendingTxCache.java | 3 +- .../src/org/aion/zero/impl/cli/Cli.java | 2 +- .../zero/impl/config/CfgConsensusPow.java | 4 +- .../org/aion/zero/impl/core/BloomFilter.java | 2 +- .../src/org/aion/zero/impl/core/DiffCalc.java | 4 +- .../aion/zero/impl/core/IAionBlockchain.java | 8 +- .../org/aion/zero/impl/db/AionBlockStore.java | 14 +- .../zero/impl/db/AionRepositoryDummy.java | 37 +- .../aion/zero/impl/db/AionRepositoryImpl.java | 97 ++- .../db/AionTransactionStoreSerializer.java | 2 +- .../zero/impl/db/ContractDetailsAion.java | 6 +- .../aion/zero/impl/db/PendingBlockStore.java | 18 +- .../org/aion/zero/impl/db/RecoveryUtils.java | 6 +- ...yConfig.java => RepositoryConfigImpl.java} | 20 +- .../src/org/aion/zero/impl/pow/AionPoW.java | 8 +- .../zero/impl/query/StateQueryInterface.java | 4 +- .../aion/zero/impl/sync/FastSyncManager.java | 2 +- .../src/org/aion/zero/impl/sync/SyncMgr.java | 13 +- .../impl/sync/TaskDropImportedBlocks.java | 2 +- .../aion/zero/impl/sync/TaskImportBlocks.java | 2 +- .../zero/impl/sync/TaskImportTrieData.java | 4 +- .../aion/zero/impl/sync/TaskShowStatus.java | 2 +- .../aion/zero/impl/sync/TrieNodeWrapper.java | 2 +- .../sync/handler/BlockPropagationHandler.java | 2 +- .../handler/BroadcastNewBlockHandler.java | 2 +- .../impl/sync/handler/BroadcastTxHandler.java | 4 +- .../sync/handler/ReqBlocksBodiesHandler.java | 4 +- .../sync/handler/RequestTrieDataHandler.java | 2 +- .../sync/handler/ResBlocksBodiesHandler.java | 2 +- .../sync/handler/ResBlocksHeadersHandler.java | 2 +- .../impl/sync/handler/ResStatusHandler.java | 2 +- .../zero/impl/sync/msg/BroadcastNewBlock.java | 6 +- .../aion/zero/impl/sync/msg/BroadcastTx.java | 8 +- .../zero/impl/sync/msg/ResponseTrieData.java | 2 +- .../org/aion/zero/impl/types/AionBlock.java | 6 +- .../zero/impl/types/AionBlockSummary.java | 6 +- .../zero/impl/types/RetValidPreBlock.java | 3 +- .../zero/impl/valid/AionDifficultyRule.java | 6 +- .../org/aion/zero/impl/valid/TXValidator.java | 10 +- .../org/aion/db/AionContractDetailsTest.java | 132 ++-- .../test/org/aion/db/DoubleDataWordTest.java | 60 +- .../EquihashSolutionsGenerationTest210_9.java | 2 +- .../benchmark/BatchHeaderBenchmark.java | 2 +- .../test/org/aion/zero/impl/AionHubTest.java | 4 +- .../impl/BlockchainAccountStateBenchmark.java | 16 +- .../zero/impl/BlockchainAccountStateTest.java | 12 +- .../impl/BlockchainConcurrentImportTest.java | 2 +- .../zero/impl/BlockchainDataRecoveryTest.java | 26 +- .../aion/zero/impl/BlockchainEnergyTest.java | 7 +- .../aion/zero/impl/BlockchainForkingTest.java | 4 +- .../impl/BlockchainImplementationTest.java | 2 +- .../impl/BlockchainIndexIntegrityTest.java | 11 +- .../zero/impl/BlockchainIntegrationTest.java | 26 +- .../aion/zero/impl/BlockchainRewardTest.java | 3 +- .../aion/zero/impl/BlockchainTestUtils.java | 8 +- .../org/aion/zero/impl/ConsensusTest.java | 10 +- .../zero/impl/GenesisSpecificationTest.java | 12 +- .../zero/impl/GenesisTestNetJsonTest.java | 31 +- .../aion/zero/impl/MockRepositoryConfig.java | 16 +- .../org/aion/zero/impl/PendingStateTest.java | 8 +- .../zero/impl/StandaloneBlockchainTest.java | 4 +- .../blockchain/AionTxExecSummaryTest.java | 6 +- .../impl/blockchain/PendingTxCacheTest.java | 89 +-- .../test/org/aion/zero/impl/cli/CliTest.java | 2 +- .../aion/zero/impl/core/BloomFilterTest.java | 8 +- .../zero/impl/db/AionRepositoryImplTest.java | 93 ++- .../org/aion/zero/impl/db/BlockInfoTest.java | 4 +- .../aion/zero/impl/db/FlushCopiesTest.java | 36 +- .../zero/impl/db/PendingBlockStoreTest.java | 2 +- .../zero/impl/sync/TaskImportBlocksTest.java | 16 +- .../impl/sync/msg/ResponseTrieDataTest.java | 2 +- .../zero/impl/types/A0BlockHeaderTest.java | 10 +- .../aion/zero/impl/valid/AionPOWRuleTest.java | 2 +- .../impl/valid/EquihashSolutionRuleTest.java | 2 +- .../zero/impl/vm/AvmBulkTransactionTest.java | 24 +- .../aion/zero/impl/vm/AvmHelloWorldTest.java | 14 +- .../test/org/aion/zero/impl/vm/Benchmark.java | 36 +- .../aion/zero/impl/vm/ContractIntegTest.java | 104 +-- .../zero/impl/vm/FvmBulkTransactionTest.java | 18 +- .../zero/impl/vm/InternalTransactionTest.java | 13 +- .../aion/zero/impl/vm/OldTxExecutorTest.java | 66 +- .../aion/zero/impl/vm/OpcodeIntegTest.java | 131 ++-- .../aion/zero/impl/vm/SolidityTypeTest.java | 60 +- .../aion/zero/impl/vm/StatefulnessTest.java | 20 +- .../zero/impl/vm/TransactionExecutorTest.java | 32 +- .../zero/impl/vm/contracts/ContractUtils.java | 2 +- modApiServer/build.gradle | 4 +- modApiServer/src/module-info.java | 2 +- modApiServer/src/org/aion/api/server/Api.java | 11 +- .../src/org/aion/api/server/ApiAion.java | 45 +- .../org/aion/api/server/ApiTxResponse.java | 2 +- .../src/org/aion/api/server/IApiAion.java | 2 +- .../api/server/nrgprice/INrgPriceAdvisor.java | 6 +- .../api/server/nrgprice/NrgPriceAdvisor.java | 6 +- .../nrgprice/strategy/NrgBlockPrice.java | 3 +- .../strategy/NrgBlockPriceAveraging.java | 3 +- .../src/org/aion/api/server/pb/ApiAion0.java | 71 +- .../src/org/aion/api/server/pb/Message.java | 10 +- .../api/server/pb/TxWaitingMappingUpdate.java | 2 +- .../org/aion/api/server/rpc/ApiWeb3Aion.java | 212 +++--- .../org/aion/api/server/types/ArgFltr.java | 8 +- .../org/aion/api/server/types/ArgTxCall.java | 38 +- .../src/org/aion/api/server/types/Blk.java | 91 +-- .../src/org/aion/api/server/types/EvtBlk.java | 10 +- .../src/org/aion/api/server/types/EvtTx.java | 10 +- .../src/org/aion/api/server/types/Fltr.java | 8 +- .../org/aion/api/server/types/FltrBlk.java | 4 +- .../src/org/aion/api/server/types/FltrCt.java | 3 +- .../src/org/aion/api/server/types/FltrLg.java | 15 +- .../src/org/aion/api/server/types/FltrTx.java | 4 +- .../aion/api/server/types/NumericalValue.java | 2 +- .../src/org/aion/api/server/types/Tx.java | 24 +- .../api/server/types/TxPendingStatus.java | 2 +- .../org/aion/api/server/types/TxRecpt.java | 20 +- .../org/aion/api/server/types/TxRecptLg.java | 27 +- .../src/org/aion/api/server/zmq/HdlrZmq.java | 3 +- .../api/server/zmq/ProtocolProcessor.java | 6 +- .../test/org/aion/api/server/ApiAionTest.java | 76 +- .../test/org/aion/api/server/ApiTest.java | 12 +- .../test/org/aion/api/server/ApiUtilTest.java | 2 +- .../org/aion/api/server/TxRecptLgTest.java | 5 +- .../org/aion/api/server/pb/ApiAion0Test.java | 98 +-- .../aion/api/server/rpc/ApiWeb3AionTest.java | 30 +- .../aion/api/server/types/ArgTxCallTest.java | 11 +- modBoot/build.gradle | 6 +- modBoot/src/module-info.java | 2 +- modBoot/src/org/aion/Aion.java | 2 +- modCrypto/build.gradle | 3 +- modCrypto/src/org/aion/crypto/HashUtil.java | 2 +- .../org/aion/crypto/ed25519/ECKeyEd25519.java | 2 +- modDbImpl/build.gradle | 4 +- modDbImpl/src/module-info.java | 4 +- .../aion/db/generic/CacheIteratorWrapper.java | 2 +- .../aion/db/generic/DatabaseWithCache.java | 8 +- .../org/aion/db/generic/LockedDatabase.java | 10 +- .../db/generic/SpecialLockedDatabase.java | 6 +- .../org/aion/db/generic/TimedDatabase.java | 12 +- .../src/org/aion/db/impl/AbstractDB.java | 8 +- modDbImpl/src/org/aion/db/impl/DBVendor.java | 2 +- .../src/org/aion/db/impl/DatabaseFactory.java | 18 +- modDbImpl/src/org/aion/db/impl/IDriver.java | 4 +- .../src/org/aion/db/impl/h2/H2MVMap.java | 2 +- .../src/org/aion/db/impl/leveldb/LevelDB.java | 2 +- .../src/org/aion/db/impl/mockdb/MockDB.java | 4 +- .../org/aion/db/impl/mockdb/MockDBDriver.java | 4 +- .../aion/db/impl/mockdb/PersistentMockDB.java | 4 +- .../src/org/aion/db/impl/mongodb/MongoDB.java | 5 +- .../aion/db/impl/rocksdb/RocksDBWrapper.java | 2 +- .../aion/db/impl/AccessWithExceptionTest.java | 44 +- .../org/aion/db/impl/ConcurrencyTest.java | 30 +- .../org/aion/db/impl/DatabaseFactoryTest.java | 14 +- .../test/org/aion/db/impl/DriverBaseTest.java | 12 +- .../org/aion/db/impl/DriverBenchmarkTest.java | 8 +- .../aion/db/impl/h2/H2MVMapDriverTest.java | 6 +- .../db/impl/leveldb/LevelDBDriverTest.java | 6 +- .../aion/db/impl/mockdb/MockDBDriverTest.java | 4 +- .../db/impl/rocksdb/RocksDBDriverTest.java | 6 +- modMcf/build.gradle | 6 +- modMcf/src/module-info.java | 2 - .../org/aion/mcf/account/AccountManager.java | 2 +- modMcf/src/org/aion/mcf/account/Keystore.java | 26 +- .../mcf/blockchain/AbstractPendingTx.java | 6 +- .../mcf/blockchain/AbstractSyncQueue.java | 17 +- .../org/aion/mcf/blockchain/IChainCfg.java | 6 +- .../aion/mcf/blockchain/IGenericChain.java | 4 +- .../aion/mcf/blockchain/IPendingState.java | 10 +- .../mcf/blockchain/IPendingStateInternal.java | 6 +- .../org/aion/mcf/blockchain/IPowChain.java | 6 +- .../org/aion/mcf/blockchain/ISyncQueue.java | 4 +- .../aion/mcf/blockchain/TxExecutorBase.java | 10 +- .../valid/IBlockHeaderValidRule.java | 4 +- .../aion/mcf/blockchain/valid/IValidRule.java | 2 +- modMcf/src/org/aion/mcf/config/CfgDb.java | 2 +- .../src/org/aion/mcf/config/CfgDbDetails.java | 2 +- modMcf/src/org/aion/mcf/config/CfgPrune.java | 4 +- modMcf/src/org/aion/mcf/core/IBlockchain.java | 14 +- .../org/aion/mcf/core/TxTouchedStorage.java | 34 +- .../aion/mcf/db/AbstractContractDetails.java | 6 +- .../org/aion/mcf/db/AbstractRepository.java | 42 +- .../aion/mcf/db/AbstractRepositoryCache.java | 34 +- .../aion/mcf/db/ContractDetailsCacheImpl.java | 18 +- modMcf/src/org/aion/mcf/db/DatabaseUtils.java | 8 +- .../src/org/aion/mcf/db/DetailsDataStore.java | 60 +- .../src/org/aion/mcf/db/IBlockStoreBase.java | 4 +- .../src/org/aion/mcf/db/IBlockStorePow.java | 4 +- .../src/org/aion/mcf/db/TransactionStore.java | 10 +- .../org/aion/mcf/ds/ArchivedDataSource.java | 12 +- .../src/org/aion/mcf/ds/DataSourceArray.java | 2 +- .../src/org/aion/mcf/ds/ObjectDataSource.java | 12 +- modMcf/src/org/aion/mcf/ds/XorDataSource.java | 10 +- .../src/org/aion/mcf/evt/IListenerBase.java | 8 +- modMcf/src/org/aion/mcf/evt/IPowListener.java | 12 +- .../org/aion/mcf/mine/AbstractMineRunner.java | 4 +- modMcf/src/org/aion/mcf/mine/IMiner.java | 4 +- .../src/org/aion/mcf/mine/IMinerListener.java | 4 +- modMcf/src/org/aion/mcf/trie/Cache.java | 14 +- .../aion/mcf/trie/JournalPruneDataSource.java | 16 +- modMcf/src/org/aion/mcf/trie/SecureTrie.java | 8 +- modMcf/src/org/aion/mcf/trie/Trie.java | 8 +- modMcf/src/org/aion/mcf/trie/TrieImpl.java | 20 +- .../mcf/trie/scan/CollectFullSetOfNodes.java | 2 +- .../aion/mcf/trie/scan/CollectMappings.java | 2 +- .../aion/mcf/trie/scan/ExtractToDatabase.java | 6 +- .../src/org/aion/mcf/tx/AbstractTxTask.java | 6 +- .../src/org/aion/mcf/types/AbstractBlock.java | 12 +- .../aion/mcf/types/AbstractBlockHeader.java | 7 +- .../mcf/types/AbstractBlockHeaderWrapper.java | 4 +- .../aion/mcf/types/AbstractBlockSummary.java | 19 +- .../aion/mcf/types/AbstractBlockWrapper.java | 6 +- .../aion/mcf/types/AbstractTransaction.java | 8 +- .../org/aion/mcf/types/AbstractTxReceipt.java | 11 +- ...entifier.java => BlockIdentifierImpl.java} | 11 +- .../org/aion/mcf/valid/BlockNumberRule.java | 4 +- .../mcf/valid/DependentBlockHeaderRule.java | 4 +- .../GrandParentBlockHeaderValidator.java | 4 +- .../GrandParentDependantBlockHeaderRule.java | 4 +- .../mcf/valid/ParentBlockHeaderValidator.java | 4 +- .../src/org/aion/mcf/valid/TimeStampRule.java | 4 +- .../aion/mcf/valid/TransactionTypeRule.java | 2 +- .../{DataWord.java => DataWordImpl.java} | 34 +- .../org/aion/mcf/vm/types/DoubleDataWord.java | 11 +- .../vm/types/KernelInterfaceForFastVM.java | 24 +- modMcf/src/org/aion/mcf/vm/types/Log.java | 5 +- .../aion/mcf/account/AccountManagerTest.java | 64 +- .../org/aion/mcf/account/KeystoreTest.java | 14 +- .../aion/mcf/db/AionRepositoryCacheTest.java | 62 +- .../org/aion/mcf/db/IContractDetailsTest.java | 94 +-- .../org/aion/mcf/ds/DataSourceArrayTest.java | 4 +- .../mcf/trie/JournalPruneDataSourceTest.java | 4 +- modMcf/test/org/aion/mcf/trie/TrieTest.java | 11 +- .../org/aion/valid/BlockNumberRuleTest.java | 6 +- .../org/aion/valid/DifficultyRuleTest.java | 14 +- .../org/aion/valid/TimeStampRuleTest.java | 6 +- modP2pImpl/build.gradle | 3 +- modPrecompiled/build.gradle | 5 +- modPrecompiled/src/module-info.java | 3 +- .../org/aion/precompiled/ContractFactory.java | 19 +- .../contracts/ATB/BridgeController.java | 4 +- .../contracts/ATB/BridgeDeserializer.java | 2 +- .../contracts/ATB/BridgeFuncSig.java | 2 +- .../contracts/ATB/BridgeStorageConnector.java | 62 +- .../contracts/ATB/BridgeUtilities.java | 2 +- .../contracts/ATB/TokenBridgeContract.java | 21 +- .../contracts/AionAuctionContract.java | 149 ++-- .../contracts/AionNameServiceContract.java | 127 ++-- .../contracts/EDVerifyContract.java | 4 +- .../contracts/MultiSignatureContract.java | 67 +- .../contracts/TRS/AbstractTRS.java | 33 +- .../contracts/TRS/PrivateTRScontract.java | 6 +- .../contracts/TRS/TRSqueryContract.java | 24 +- .../contracts/TRS/TRSstateContract.java | 15 +- .../contracts/TRS/TRSuseContract.java | 33 +- .../contracts/TotalCurrencyContract.java | 23 +- .../type/StatefulPrecompiledContract.java | 6 +- .../org/aion/precompiled/TRS/TRShelpers.java | 32 +- .../precompiled/TRS/TRSlinkedListTest.java | 10 +- .../precompiled/TRS/TRSqueryContractTest.java | 12 +- .../precompiled/TRS/TRSstateContractTest.java | 120 +-- .../precompiled/TRS/TRSuseContractTest.java | 12 +- .../ATB/BridgeControllerOwnerTest.java | 10 +- .../ATB/BridgeControllerRingTest.java | 8 +- .../ATB/BridgeRingInitializationTest.java | 8 +- .../ATB/BridgeStorageConnectorTest.java | 11 +- .../contracts/ATB/BridgeTestUtils.java | 16 +- .../contracts/ATB/BridgeTransferTest.java | 20 +- .../ATB/TokenBridgeContractTest.java | 94 +-- .../contracts/AionAuctionContractTest.java | 204 ++--- .../AionNameServiceContractTest.java | 174 ++--- .../precompiled/contracts/BenchmarkTest.java | 18 +- .../aion/precompiled/contracts/DummyRepo.java | 33 +- .../contracts/EDVerifyContractTest.java | 24 +- .../contracts/MultiSignatureContractTest.java | 140 ++-- .../contracts/TotalCurrencyContractTest.java | 18 +- .../aion/precompiled/encoding/AbiEncoder.java | 2 +- .../aion/precompiled/encoding/AddressFVM.java | 2 +- .../aion/precompiled/encoding/Bytes32FVM.java | 2 +- .../aion/precompiled/encoding/Uint128FVM.java | 2 +- modRlp/build.gradle | 4 +- modTxPool/build.gradle | 3 +- modTxPool/src/module-info.java | 1 - modTxPool/src/org/aion/txpool/ITxPool.java | 6 +- .../src/org/aion/txpool/TxPoolModule.java | 6 +- modTxPoolImpl/build.gradle | 4 +- modTxPoolImpl/src/module-info.java | 2 +- .../aion/txpool/common/AbstractTxPool.java | 23 +- .../org/aion/txpool/common/AccountState.java | 2 +- .../org/aion/txpool/common/TxDependList.java | 3 +- .../src/org/aion/txpool/zero/TxPoolA0.java | 24 +- .../org/aion/txpool/test/TxnPoolTest.java | 321 ++++---- modUtil/build.gradle | 111 +++ modUtil/src/main/java/module-info.java | 10 + .../org/aion/util/biginteger}/BIUtil.java | 2 +- .../java}/org/aion/util/bytes/ByteUtil.java | 2 +- .../java}/org/aion/util/conversions/Hex.java | 8 +- .../org/aion/util/conversions/HexEncoder.java | 28 +- .../main/java/org/aion/util/file}/File.java | 2 +- .../java/org/aion/util/file/NativeLoader.java | 138 ++++ .../java/org/aion/util/map}/AbstractMap.java | 2 +- .../main/java/org/aion/util/map}/HashMap.java | 4 +- .../main/java/org/aion/util/others}/MAF.java | 7 +- .../main/java/org/aion/util/others/Utils.java | 70 ++ .../org/aion/util/string/StringUtils.java | 95 +++ .../java/org/aion/util/time}/TimeInstant.java | 4 +- .../java/org/aion/util/time}/TimeUtils.java | 24 +- modUtil/src/module-info.java | 5 - modUtil/src/org/aion/util/NativeLoader.java | 142 ---- .../util/bytes}/AddressValidationTest.java | 20 +- .../java/org/aion/util/bytes/BIUtilTest.java | 155 ++++ .../aion/util/bytes/ByteUtilExtendTest.java | 483 ++++++++++++ .../org/aion/util/bytes/ByteUtilTest.java | 16 +- .../java/org/aion/util/bytes}/MAFTest.java | 65 +- .../org/aion/util/bytes}/SizeParseTest.java | 18 +- modVM/build.gradle | 15 +- modVM/src/module-info.java | 2 +- modVM/src/org/aion/vm/AionCapabilities.java | 43 ++ modVM/src/org/aion/vm/BulkExecutor.java | 39 +- modVM/src/org/aion/vm/ExecutionBatch.java | 11 +- .../org/aion/vm/KernelInterfaceForAVM.java | 13 +- .../org/aion/vm/KernelTransactionContext.java | 32 +- modVM/src/org/aion/vm/PostExecutionWork.java | 8 +- .../org/aion/vm/VmFactoryImplementation.java | 8 +- settings.gradle | 2 +- 424 files changed, 4376 insertions(+), 8366 deletions(-) create mode 100644 lib/org-aion-avm-api.jar create mode 100644 lib/org-aion-avm-userlib.jar delete mode 100644 modAionBase/.classpath delete mode 100644 modAionBase/.gitignore delete mode 100644 modAionBase/.project delete mode 100644 modAionBase/build.gradle delete mode 100644 modAionBase/src/module-info.java delete mode 100644 modAionBase/src/org/aion/base/Constant.java delete mode 100644 modAionBase/src/org/aion/base/db/DetailsProvider.java delete mode 100644 modAionBase/src/org/aion/base/db/Flushable.java delete mode 100644 modAionBase/src/org/aion/base/db/IByteArrayKeyValueDatabase.java delete mode 100644 modAionBase/src/org/aion/base/db/IByteArrayKeyValueStore.java delete mode 100644 modAionBase/src/org/aion/base/db/IContractDetails.java delete mode 100644 modAionBase/src/org/aion/base/db/IDatabase.java delete mode 100644 modAionBase/src/org/aion/base/db/IKeyValueStore.java delete mode 100644 modAionBase/src/org/aion/base/db/IPruneConfig.java delete mode 100644 modAionBase/src/org/aion/base/db/IRepository.java delete mode 100644 modAionBase/src/org/aion/base/db/IRepositoryCache.java delete mode 100644 modAionBase/src/org/aion/base/db/IRepositoryConfig.java delete mode 100644 modAionBase/src/org/aion/base/db/IRepositoryQuery.java delete mode 100644 modAionBase/src/org/aion/base/db/PersistenceMethod.java delete mode 100644 modAionBase/src/org/aion/base/timer/ITimer.java delete mode 100644 modAionBase/src/org/aion/base/timer/PoisonPillTask.java delete mode 100644 modAionBase/src/org/aion/base/timer/StackTimer.java delete mode 100644 modAionBase/src/org/aion/base/timer/StackTimerRunnable.java delete mode 100644 modAionBase/src/org/aion/base/timer/TimerDummy.java delete mode 100644 modAionBase/src/org/aion/base/timer/TimerTask.java delete mode 100644 modAionBase/src/org/aion/base/type/AionAddress.java delete mode 100644 modAionBase/src/org/aion/base/type/Hash256.java delete mode 100644 modAionBase/src/org/aion/base/type/IBlock.java delete mode 100644 modAionBase/src/org/aion/base/type/IBlockHeader.java delete mode 100644 modAionBase/src/org/aion/base/type/IBlockIdentifier.java delete mode 100644 modAionBase/src/org/aion/base/type/IBlockSummary.java delete mode 100644 modAionBase/src/org/aion/base/type/IPowBlockHeader.java delete mode 100644 modAionBase/src/org/aion/base/type/ISolution.java delete mode 100644 modAionBase/src/org/aion/base/type/ITransaction.java delete mode 100644 modAionBase/src/org/aion/base/type/ITxExecSummary.java delete mode 100644 modAionBase/src/org/aion/base/type/ITxReceipt.java delete mode 100644 modAionBase/src/org/aion/base/util/ByteArrayMap.java delete mode 100644 modAionBase/src/org/aion/base/util/ByteArrayWrapper.java delete mode 100644 modAionBase/src/org/aion/base/util/ByteUtil.java delete mode 100644 modAionBase/src/org/aion/base/util/Bytesable.java delete mode 100644 modAionBase/src/org/aion/base/util/Copyable.java delete mode 100644 modAionBase/src/org/aion/base/util/FastByteComparisons.java delete mode 100644 modAionBase/src/org/aion/base/util/Functional.java delete mode 100644 modAionBase/src/org/aion/base/util/Hex.java delete mode 100644 modAionBase/src/org/aion/base/util/HexEncoder.java delete mode 100644 modAionBase/src/org/aion/base/util/ImmutableByteArrayWrapper.java delete mode 100644 modAionBase/src/org/aion/base/util/MAFast.java delete mode 100644 modAionBase/src/org/aion/base/util/NativeLoader.java delete mode 100644 modAionBase/src/org/aion/base/util/TypeConverter.java delete mode 100644 modAionBase/src/org/aion/base/util/Utils.java delete mode 100644 modAionBase/src/org/aion/base/vm/IDataWord.java delete mode 100644 modAionBase/src/org/aion/base/vm/VirtualMachineSpecs.java delete mode 100644 modAionBase/test/org/aion/base/type/AddressTest.java delete mode 100644 modAionBase/test/org/aion/base/type/Hash256Test.java delete mode 100644 modAionBase/test/org/aion/base/util/BIUtilTest.java delete mode 100644 modAionBase/test/org/aion/base/util/ByteArrayWrapperTest.java delete mode 100644 modAionBase/test/org/aion/base/util/ByteUtilTest.java rename modAionImpl/src/org/aion/equihash/{Solution.java => SolutionImpl.java} (79%) rename {modAionBase/src/org/aion/base/util => modAionImpl/src/org/aion/zero/impl}/ExecutorPipeline.java (98%) rename modAionImpl/src/org/aion/zero/impl/db/{RepositoryConfig.java => RepositoryConfigImpl.java} (65%) rename modMcf/src/org/aion/mcf/types/{BlockIdentifier.java => BlockIdentifierImpl.java} (74%) rename modMcf/src/org/aion/mcf/vm/types/{DataWord.java => DataWordImpl.java} (83%) create mode 100644 modUtil/src/main/java/module-info.java rename {modAionBase/src/org/aion/base/util => modUtil/src/main/java/org/aion/util/biginteger}/BIUtil.java (98%) rename modUtil/src/{ => main/java}/org/aion/util/bytes/ByteUtil.java (99%) rename modUtil/src/{ => main/java}/org/aion/util/conversions/Hex.java (93%) rename modUtil/src/{ => main/java}/org/aion/util/conversions/HexEncoder.java (84%) rename {modAionBase/src/org/aion/base/io => modUtil/src/main/java/org/aion/util/file}/File.java (96%) create mode 100644 modUtil/src/main/java/org/aion/util/file/NativeLoader.java rename {modAionBase/src/org/aion/base/util => modUtil/src/main/java/org/aion/util/map}/AbstractMap.java (99%) rename {modAionBase/src/org/aion/base/util => modUtil/src/main/java/org/aion/util/map}/HashMap.java (99%) rename {modAionBase/src/org/aion/base/util => modUtil/src/main/java/org/aion/util/others}/MAF.java (88%) create mode 100644 modUtil/src/main/java/org/aion/util/others/Utils.java create mode 100644 modUtil/src/main/java/org/aion/util/string/StringUtils.java rename {modAionBase/src/org/aion/base/util => modUtil/src/main/java/org/aion/util/time}/TimeInstant.java (96%) rename {modAionBase/src/org/aion/base/util => modUtil/src/main/java/org/aion/util/time}/TimeUtils.java (68%) delete mode 100644 modUtil/src/module-info.java delete mode 100644 modUtil/src/org/aion/util/NativeLoader.java rename {modAionBase/test/org/aion/base/util => modUtil/src/test/java/org/aion/util/bytes}/AddressValidationTest.java (67%) create mode 100644 modUtil/src/test/java/org/aion/util/bytes/BIUtilTest.java create mode 100644 modUtil/src/test/java/org/aion/util/bytes/ByteUtilExtendTest.java rename modUtil/{test => src/test/java}/org/aion/util/bytes/ByteUtilTest.java (98%) rename {modAionBase/test/org/aion/base/util => modUtil/src/test/java/org/aion/util/bytes}/MAFTest.java (52%) rename {modAionBase/test/org/aion/base/util => modUtil/src/test/java/org/aion/util/bytes}/SizeParseTest.java (78%) create mode 100644 modVM/src/org/aion/vm/AionCapabilities.java diff --git a/.gitmodules b/.gitmodules index 71d8a40837..2e2370d349 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,11 +1,11 @@ [submodule "aion_fastvm"] path = aion_fastvm url = https://github.com/aionnetwork/aion_fastvm - branch = master + branch = re-architecting-2 [submodule "aion_gui"] path = aion_gui url = https://github.com/aionnetwork/aion_gui [submodule "aion_vm_api"] path = aion_vm_api url = https://github.com/aionnetwork/vm_api.git - branch = dev + branch = master diff --git a/aion_fastvm b/aion_fastvm index 95cdff6147..67fd16ec7a 160000 --- a/aion_fastvm +++ b/aion_fastvm @@ -1 +1 @@ -Subproject commit 95cdff61476efe27739e5d7ec96de255be298e58 +Subproject commit 67fd16ec7a16f07c1b6d4b3961a0dd6a6e5aec3e diff --git a/aion_vm_api b/aion_vm_api index 3212356fb9..35588e29be 160000 --- a/aion_vm_api +++ b/aion_vm_api @@ -1 +1 @@ -Subproject commit 3212356fb914a361e8bde0beb6a60a96c40c5a8d +Subproject commit 35588e29bed566126ce923d261022f5146be63ae diff --git a/build.gradle b/build.gradle index e5ea11cf41..34bb88bb68 100644 --- a/build.gradle +++ b/build.gradle @@ -24,6 +24,7 @@ allprojects { repositories { jcenter() + maven { url "https://oss.sonatype.org/content/groups/staging" } flatDir { dirs './lib' // does not recurse, don't make subdirectories in here diff --git a/gradle.properties b/gradle.properties index 502ee95f62..aa9d5fe7cb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,3 +5,11 @@ org.gradle.parallel=false # Uncomment to include modGui in build # modGuiPath=aion_gui + +# The following variables are for pushing the aion libs to the maven repo +ossrhUsername= +ossrhPassword= +signing.keyId= +signing.password= +signing.secretKeyRingFile= + diff --git a/lib/org-aion-avm-api.jar b/lib/org-aion-avm-api.jar new file mode 100644 index 0000000000000000000000000000000000000000..a8deb45d9187db3bc1d6d0c793c0c9a93b11fe63 GIT binary patch literal 18049 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 NnE325nLK}d`hPjdVtfDq literal 0 HcmV?d00001 diff --git a/lib/org-aion-avm-core.jar b/lib/org-aion-avm-core.jar index 50b771975744ceceb384edd5f4a4f1f42f8b98f8..6c47ecc2600ff7e95279e4a5ad5c13655b70dba1 100644 GIT binary patch delta 173599 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�$oX<7^$7eUa?n`kzxbu`PF5H3ftj^y* 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 M4VZwHKuN&(e>lrEo&W#< delta 217181 zcmZ^K19WH2vTtnLwr$(CZQK9YwvCCC$;7s8dtxUOCvVPo?z`{YyVl!#?NzmQS66rS zZ*_H7V`K<pE*BeKNfs0g1_%fW2<Y5}KM9@+{>+76;S&*-Q5OgZ$TCSp6#)16_icuU zM`KI<h(SqlRYA{vh=I9}L6HsINMrME_i6|Fe@gz9`)|hoexUzy=3d73PIl(5jQ@Wz zu)kuCE|&ix@wYbrR+My30Qy&J3M?=c=-;0ya^N^AO2DZ9@Ni(H8Axj22>;WHlu}^+ zf08#qNHcatz!9<k!^HnlJM0u=P;@j?7jGvw$NyP&3NZ-xKRWb51plE0AYA{Yljk2w z{8zV;6A0Y@Dq0m(<e%hBP|p9Nq#22!;4mrQpfLaZfCJ<GhsuDl{zC)7SpHiu@C6hc z#md;#>VMVcUv@Z2fFr{GhaG?00|(jE&ia3e-h)y9qmBTa_McXh1E&Z43lmk#^%K6o zt?c<1XuSW#M8w?9Sl-x4+{4_#&6UB_&e+v8F3nyQRTFLWI|IxERij^PDS9|FI2;Vl z#C~3EgDkLEm|D1|Ofka@cg4<wH3wd`N4kR5|LoD;-ZHU>HLvH|(uL>q#zMA88asSn zAlDag&E5Op#0mIvzvT@?RE{wLYf|1CfrPmhqkg3dM*3YGxnCFT%WO04J4#snLIwlw z?Wn4x{K$r3f~2K#Cp0VyKACT&F-!`c04@T*j)q0Mu0T%;zd13MCsDq=fWNplaYU_^ zG+iT9YPUpp)`G8Zv@dq7cUHEQgsrO6EVmADhpSG%N}pOh5}Ij%^UQ}DABPWeny*`U zQ5tV}q`VqQzpk02Pf6Aquaj~W<q2;^3^n%Lw1U>7IolnVSX|Q03ci-<Fx;A_Zxw&o z4`-uO7$2Vod4;cD_rNTr1%Klcj?@jp9bcUD@ha-*xoC95hk3k_YLcf|=BUQaJI(_9 zq@{H~0BbACR&XTc@A7V$<?&hc&~pg6u&FbF7>`Q7YEhlffD=R?4emO_UUb4Pfsw({ zNJMIz<0XX2xAKZshsShM9t0CW$NFM~6$jt+(5)lQq3!6j*VGjS)lyYnX^6MP;b59p z(kQ64^@<M-pS=|k($>|ZKaPpPDZd4@N6RZd!~Wj3$)RW_q^pC~<E}J@>MrDz_tmT) zNf0I!_3L<2sb`9aGA%b-97B`qRH?UX+OV~sWXG9G8F2Ho;om6BXQX31MgNmM7_9cG zzEgF7?mD>5ko{A<7c&84BEy^$Hh^#%pZ|=>ypud-yO^qHNVA5hDMK!DNG%sIXy)f5 zz;IeeeBm?SLbLEhY*&3OBM6+Juw203QKeZxBhR3hk|1Ip0Ml8gfJnS(u}?Za9sGRa ze)vQ{{0AyherAIrA(Yv(V|0mL<+gd19X;p{FbiipT6Ym7L&+`s^I&794kX>H&}{RG zBS%uVg>L&U=4klgQ#rCvq9O}GC{sNV!MxM%<${x-ueWP|&cDQ|sGw?8OeNoB*Txg_ zp<#5OX7@o*P!nPM@se<x!}#*{TFh;DP=|O^yqgi{(j_7t-n6$>-~)QMq3NA)7|4h> zf+bm75?CGS8b9Yo(&m@3v9rD>@J28yQt2NG5MV<-hATyqc$<^U6yyWkm~L^h23-VY z5su>=_<duVls+@vLZ+sv`J4mMG$csTbwu=|eS1r2W@q|@Jv6cgIaQH9u8H;>PL@7{ zEHVxDIqpMPydJm5;a$HF-Nmy`qy+Y6A4U6%Y@=!VgF)!Ww2o1O#ozvM3w#p|<hdxi z-lvn6t$YLy_ftv=04E20qQ039(l)w&k8(=n^x;8d^&)SPI;Q``d+;d$dyvGk1RW8c z!5^{ZipYtG#d+H?iH-G6P&J?WPIVtq6rg><HDTP@`=~ebi3qL#Ouwa?c_XO%Dd)W4 zd&P2?agEXPaRBWXLPLg2abf$Mq?9;^i}?-le_cHk&>7&b`ysH^a8CvX0x|*t0>b`J z_akL)XXmKt=wfH~Z{K6Dtj`E4jK*I=xLH@0`u1ftDeB$eXh4HPF0znF#OIamKzYT~ zxoKB1(0d?#*pGNyGL%OwwHD!PF_Z85dnT{-IYSd@Q_FMTh$kXB278JplNG>qV*(t5 zkdF0}mnGRJgIImD%}nZM)V^QIYC<X(ZXjKX!Alld)ouh>GlMV5;#<7jUGopNjJ)&g zU_Offw-gn+;c%4ADz=ONml3o|xSj-9bKC_)h%W(RvJjpOs>;iq-S#c-HE{%Ah~?SO zV_JlC{u-k+<MV<NzEtM3!UI52G<!I7$^k8b`^Vx+92A=<y8@3uhV#)w=iCYZ8K)ZF z7KHx!xgC-<whE~NV^WgFl11gp$2ixjnCH0#kAmbWXdU<9K|x>l$XyVWl@Ijmrbjbo zGxdke`E}2MKhDzSjo##PJuVxvT4K2+iy_R^Rza04Z$Tom1xG=Dz7>EuSh-4Dw&V7P zPXS1*aV}HBKx@0rcVY(@i`Yb4mV0L^$b#iq(kK{3?<4x8ssuB?m__8}Ve9dO5GmaT zMY+I#5AT1${EOuSD%O0$Uo4;gVu}10OM6E%cRO=>YX=L*|H3eF+GUXmEi56B;}<oV zFo49K>T|#Tn9(KKns*Sk!E~8Y36H&mNS9#Tnvyr&p8|sq2yvKbH&r3Vbk;9<rv09O zwfFnu1AGsz1j2yDF2A!~@k!H^U6j`~rG42y#gL0zH6z;i6IU9~7w`R!3vVn4&jrs3 z&!y|YYx*wS_)0FG=9PYwH=Z=!H2xWn5Wpn><A?G>H;0)B64fVUG&a|@7*llkp+<A) zQpv!Y#Y4Dgrv`nb71rjs{op@NbWQzG*v)?EMZFf2lBE`Ayry#<G9GQibG|>qnwPHW zL#IB5<-<<TG0Tzx8Sb!6+r2^?=WR_l*nf_zay-Eh46R-|aJ(p9C88~EvjD^_2spMs z9Y1|^dojmmuk0d2$e;s^8pz91R8e(lpr$Bdw_F6YQ$0=-eREHCy}Oe((|}z#^sIN# zzts8HU2U6%-B0M)1AySZ^C9nQUyu?GeB#(u3XJ$Q*l#lxaEIhgQrg7X6{F})a@L3i zN^lg=h9L46H^?1OJtO%KU=ObCk!U`^|D!v)@HbUMIRPX83(A6r+(kKXARu8lARy*{ z&4n_Mq`|>5tfatk0cjetuBehoUn7Vs>8nO?SaAKBdr0FgiQb`LQ<GS*V9CIn``98Q z3pxf}8|oUgA&n1QD(%Plq70#`RZcYfNlMZ)l``mQGRKLwRRe}&#Ne+M<fgpoVR|%I zQ;(0U+l$+ai@ki`Uy?xYca`C!PgcY8Kpcl!Kp_6`T=8E(00ko<`V!#Wp-LbhWYvPj zXb*|$gC{8?roa%c%|KAD3ZxTs52?Y1@Fbw@I|rZ(Ct+a*8s-c(y%i_%LG>W@owlrt zQ{*)i&n-8x?0Ed7lq{(Q=QycdGm7+aoo+K;vyF03u|i6%aK->pg^5V=D>t(>S9#uT z+zuk8#hX$%KoGn0A_v4nmhvEeQV9n2v@<1jE$jmOa)(rnG6Zx@*;J6O1j`Ng^;nzj zSZW|all|aTs~Ll3B6(LAIlk;1pJ*ICYtIU~28>##cg}9%PtA;y_=_iA?@d-cU}RLS ztE28;qASFt8LfOlc@G_waH|}2W!^(+9tsrvDzJWNfSMeSg>fWq5?53Z-rvsJXEKu{ zF2Hik$iD*AC@2>g=%vKVC~JXN-M7SLFbTM5<R_)zw`6d%$wR#QO<Nc(IF^m=ti=@; zS!Hrf4uv`BZXx)Ox73vPc5oJFyd4Bg*Yed=z=J2DVuvwYQMR})?|`7Qo=mIu`QcaD zx)_nL03P=a3z6A03qH(RovGj;wUZL6qZnP~rnGezFF-1TKjO($aNzJwr7o}{D)+<V zp%Xbn5)pL$@yy))A`>ArB@y!D`6%4C)O=gf9AK=2TJ8f-PJawt@Hm+2taFtdlKRyg zqE128(C+I6)uA8N3N4ag3s_s4Cuv#vWYM9o0lp?m&5FTfcdrBGDB|#Br4VDJy7b(k z)H8*YtK7G!e=B>518roM0{Bd$aCt(~&dV3B&ABjH^I2+Y_J6B2-)3GOx)z;Py=2q5 z?Km8FmC!on_j<}~P+Qv3?KiN!V|IM1^MT|@(o=Hx@8!AD(N56iBs2x$*VNRgD-DUD z1JFAHZI9p*mIn@-t!TaOzx(rgp<Q=2O1juJ{t)XV1s&6R=}u5NYWzSE#t)NJO87H; z07_Z+j4#N`Hegpd21QssVY6`ylN#JGH}I<*)@+6DXfw04faIa3DxZ>Gw8Q`0oVG)k zG1vAQjh;FL(ycsQFQ3QeD&rN87`L+F5#ZWLt>t2B9Dj4QPHLoq(d;s-TbZMDk8Wpd zgAaI3>|uksx@_WTx<HYaKR4d5iT<v&G6npKYf916bhSIZd_z#r+eTF8zhG^C=-k-F z5uz_v>R$4w)A0j__riAs`m&?kA&YaQ5CJbze~;81?zYU^Ej~xKAt*^d{&|6{0f-U{ zDuuoSat|T#!vtM^w;e!Kngs~ihZ7bN_lJGK<8%@lJ!$^9@rh*z8GhQv?8P*9i%5(0 zWz1J{QB_9QUe(wqxEw;%^33|UAT${?Qg)aeCQ0s#BNu}FU30<Ez3bE90o~mEr$U>+ zgMJ~?;<<9<eeEbg@hsTdZ$IJr4lqPmaKH(u`Gfy~y$zKsOnM-eYkuex|M=i`g|zJr zz1<c|pl4j|iyq+|N1SLExY#zfDhM9aL_Op15LED+F$CMpIYPhh#HrE~#?a-)_Ik@w zl4;Ul587HV<qxg3BKLR5Loe5NjuiS9@sjclv0E`L`!9=7jL=Urt|<M;IfrvzEyWlQ zR}Rs0g3IG;x)dYAoqN<nZDwYiJ4)Wq)-0qOr)E(1+4;Kt!0-?ppKtRI-Y;QV-9HwJ zgXI?=jzk}c1>O<!{f${o8UvnAat-^t9yL#b|HBb0q>TDt4q*WS8S<v|fnojQd;xz$ z<$n*O!@AIZdg{x9C+5i+at41m%ug{keob$<1%XJnlI8uPmfo;fNdIN!pVvhuO2KMu zp`s(E#(}9OL`&3Zh3iZoR8Ef`{bLUVs<@3fx@|8y_$}(E-?!`Tq*=1uLaJf_VXxut zmgi0TNxtvOcQ>QoN!&XjSur|bV+3yE1dfxy!P<Oc&`A~7Qkqtmfct*T-Id@(wp!@K z+F{}sboqteX}R6Zf_He1%keW?9{;Nt-=;h`A7u){onBtd7F)<{;wT?+L4ow{v5B^O z&?CLu0|~=?`;xqCyPUy9SRqMBx@#G3!tlLLnlPnT)UR}lTNZ=skwZCvH$QEP=IuJm zGoE)Yt^e}<kEGEFIiFa~@v3Tf1JY94F!S2P73fKy{T7ALyNMFvlP8C&efWE;)KQ5d zLB-RYqh{_DU9S|TExN^4DZ^^J!3x)$8QrjPj;x>jDYGXYMeD@%yvNPbVjV)|U80ZA zGjcn_H!_y0C)+Fsp01DpU5D!<2JABoXVBtASaX*IDI7FmpKZK@2Dr!eSCV+MIaoY* z*VuRWokNOVJH`~o74Q0)_pPPjjIqOQho02k!*Yclxs&H;@m<P=ZbKi23~0@<mAX{$ zIuz@=nIptoNY+<TCM+F7g%ew`ssytI*$)SAKedyDvg9|%fG9h_xz4_)z5p{o<z;*C zH|{Hi@i#T+?o{ikAFZmvRldQE!Q?I>tmH-qY5ID<8C$rBMyY_6M2SW!H9gk#`A-tL zSNVM@O?9?4@84nS@>TWW)V_JG4i|6wI}hDYx|<x$EO!XyHE&K-IIN<L_7)AhGRR4D z>pz>h6;42nOW@i7nJ}P4@sH0sMVZ*Jp`!5RgbSCZRVuLAr&TP^j~d`2%(N&hA?k5) z(v(8kzX+7Hgv+CoDl@JBfM2fXmG4Gt<W)rCMDQ}rUfm>!^&xB+Wpn#z1Gr4ojSCxF z8KD*)ubpak9zAvC9+Hv%jDqyj7`82z1N(<F&Y`oIej>{O+FnXgH59~@TNy2uB}jhv zTp>5rBUUMjL|$7F;WUW^z*e#H*2KFv`}?z36Nl~Chw}{N<&B5!7$I^*hyqig9C0Jl z;#?=Z&{xD%+L`GTU^9mZ(`u=2&I5N?U0CVJ6|`(VKD&tzd=|w+uv$QLCq>?BW$HD; zTM${fdp*AaY6Tk0PiqD@ws)8Dt^;QlG$8ss*ca=PRox;~orCbpHZ%2}Y=(2og~n{o zZno{kH>S3QWxacL;T%{=fxSI>^c$~%21wE438I+GQlVfGjC$B<3X2EsL|hZjGB=`) zBf?6!uP<<a^8nb+WZv1W!aJ($JSK9rd$8EH?EX{&w0U;JpGZH6%VA|a6k4uYBC(@I z__4$zM{2qovlpZ(UqEhss*4OiG(QHSQaX&*c0tjN2XT{_Oi1k-H_0n#Kn@rkK7Y)# zvicv{Z;eb)yM*gLLz>ELH%WYWyOP0mSb^X#dXq~BlLi$}$NpJLjh|bSmeB)zxEyE_ zVM=lVxD~w$$cxGz@a2wGbqclo@K9Ah8oNtaF45@OMN4I|m_tsusXhDKo>CH}<KuOT z5GpB$Q|PacFp4Do+_fVcenoEd|KMY3r*Wl2`&s8-t5F|ChH}2}s*oob=M}Hh$czot zLB*XGO5C4MaCM#F9?BRF%Y-#06S=(dr9?LeuqCHx?g}+dLz>qtoNBET1ljj<wqUK< zw>Fi8K|YF8*ugyxhE$Y(p>A8L4Bu6gH!%_?OEj*S|KtGu0jc|N-Q2o@wSjt_B!?%f zM~#!xRhhbGJJL~o4x;~)95({%%gND^i;Zt_If#b4$|kQyhDE;B8*)HBE(ZlZ!+xU& zaH?idzCev>ZDx}ik-3b40scs$x(gmqi@_F{H|)nU;--NT;EaQ;VE%qEzbIx@DFiJ; zpkX61bPxFkQYaA|sI&T>1+h~cB8Qjdm=R$HiRJ;86CP;}oPZ&wEIZ9Gc>(51xxl1a zuC<l~4qgh2bMq$%=}pfErJB=W!@uYlkWibTSULa7ItDYwQfun0C@y_gNmS6qZ^1MN z{2-O>Zd6=tRx*Lnc__{bYOsskK<cR=(my(r%2352SrZ?)+u~xR5-b*o)X0OL92#8{ zdC7*Ior^>!Q^d<}_2>GUDQ5Jl*b90Qn;e%?x27Cztl*hxIzJVrh2>L4B<62y0=!tB zd{n4ZbhV%=<|ntYLrw}ymj=F)I{-4?f6LTqK2Yb%;_)qyRd}V(^YihZd~y#;Ro92b zD$lN08rkbj1~AvZ?ND%k7arKBI;ic~n7LNSd#Vs}U2zDpEz*kP#_zvX*m>#@A|U(@ z|Hh_oI!Ad1ktb+-3oKwY7w3iwP@Nw~5Qf;6016^kjpGg7mWs4vD%lnYg3X6c1FvD* z*K$q<b#~C_!V4M9@}JhI;oU->mOcz}`^xS2d^l+|{tE6lJYpo@Ubk{YIbg*7K>rbm zIDSY#z;<fxGrZ?(t#tAZCom-n1W*lBDL$iw6Cg<_@tfLvKeL1&JgLV4d;qoGhdd_s zQ@#-A5pulXe#nOq8b9()>@K`+LI5PKdL|RFKic^6_inI1O8N4WN1tD<LjZk(L)r}5 z{p>H;NJgxJEH9%Us38EFm`}KvxtK@akN}Zcpk4Xl#jx=vi0|A2))!2Y1;$tJ5Wp}a z)`#s^+)OX~i|0pifA0xE5bHy-MkHY}{9DYER98N>H4uNKkUk{8%yLKpu=2_X(MzWC ztzP<-b9!yQ{zz*Qi?|Ts_SM<{y@#2_K|ZFdf3z1^tS){NpeUYXsK;{9b(s`{P!Ts| z%_ENs-~jglWnKm~#6g~{6M^_nDOh<0z<pu%Rz38JgMKr=um=FP_c+Cf9c|34YOKT4 zK|XC5=+##j;N({q;ORwOFLcU-8tFtQ%TL_sN9SCc`O3vrL#e{k>(R+ZHl$&|RiBPZ z`BwzW;EbaE&)+DE)&|x#*zACDSITj9iZ}-66iPIe4pN91MIWpxa5v=Eq;kTv<}GYp z;p`c5g=v#lS$_e%J2-adqMN}xTIS|3qGa3b>#M@+ibQLN=48B)B8|GHOl73<u$y`O zIOePxYJL=@5z{og_)t;GSC1Vt?pLsA>e^YUZ=i_kR?#muABTICd!!fD?g{JjZqCij z&xk+C=Ii7xfD3V*`^~a>4iV$*E9qM(^>>0PGUVO2$5#UGQ-!b>yHY^;%a0v_^AGsk zqlxV7G~zXVDO+qylxax@cw40%RNa4xzoCz8D0fWPs@sCZ*tW7`!k^rd@J3-W$E$Z+ zAR@^_HCX!+t?>IvM!_pETGPn`(f4HLcJx_~m!f-afeCw4OQ8%jSli{&dZik&{HBm9 zms91G+Gzx2NK}3mnwu1?2bK*!({~=fB&n!$Ah#*U&qGthtw=nFeQYN=QEd^zUxVuL zQez3nf!*wjY8LyBZTBsR9q|rt2GQZwggx9!lVN_K6VDaMK*&|3r7F0EyI$S4IH;Vq zX(~k78$)R&=eGTt#}E*PdG~7IMoAp>^QrbMY3%__cg}=AMMabb<`NVRO6comY5g44 z+%Rd+2+{hZsN-MV+A__14&ffm?ZS)-%Q^8%>-%^PRZd;aeiz(vj#jY->nk~dxHu)` zmecnZIuq_xO0a|Bia_l&$1NSDV`LhkKzFW+$#wVG;B=24w{;XrKBP&4#3A6z!?UVA zuSEnXy?5#|Ob9E}5EuWokmr8N^ojWdNv4>ZdRdbLvy?81*(U0vEJb4>ttr6djzul* z?#p=28KPynTsnIAlEm50DB>74_|;O~Xs&Codf1^)pQCfWPse>>t9xOy>g&1eXk!Bs zHrA+|2T87Rxn*B*{Zm>Tt4QsAB0-lkpdB0lUpdfOsXAbB7^<9Z?xD9v7`L1yta^6* z{mXq_a`n_dRpA-|8rN8diCpC94T!1NOnEY1->OT9!4G6c9K~-*!78TQ1-(=BH!gg0 zoP2|({dT#ARv8lx?DKLsPfO0kp=8|_hGJ?E#e(Dd(v7xK3ZA7~%{*3Z9J_Y;mfJKy z;g)K5B|LoOO~O%jv#w*KT<2KJn%D?DY7FnRmm5h2YOJQs)h;f2z-2U`HAOc4l;?Hi z0Dc@YG~QX;1)quTXFnK`O9*<Z3c^e$x!C3aZcVobd?*R|R5JSFWl~RhgA&G29@`Fe zl&b%G5)t97nPqZShkfqlANe9G;zl$8ef}2xC;)W|A@l=T7N|Hy2t%G)Nvu$BRHk!7 z$5z)5en(@de8L}}##cTnuC$9xOXW6wVi+W7;X^?q;E{gknmpu(<KWfp(ly@_XWJP~ zPAqsvakH5x<{`B$%4}ZBT)(hMl7ElG9vo!-b#Au)&VhbywJ3gmvRJAd4g>&Dm|{x) z5ZpRiNZASQC!6#BeUGWW`?;OoOS#2p7?x~&eWx$i^ZlmYCdESAZl301yjnwq1E1tn z&vQGrTAt_5o6Nl!T0nUph<O>GR9}DnmDt=mTN!g9BLSd8Fr;huACO%t9i@3y1>RkG z^QoVO%sK``K}obSG`o;l4@v~cgdL*g@Nrs#754<VM^T^2q}k!-yn*1Sw>F`=VR73| z6ml8s$RD%^RHfK~*^c5$C?S@((9z0sWv~l>-ua;40BfDGlkzwB8zmz;arwpVrHD(@ zXu2F*=?CCKQfOS*L1uCZE!5U7XmnB=>V^!;j7u}_>HipIcGooY`H2XC)&AkI&!5hd z!TxtvIBpp^#7e16I40*`k*AT&c6r}1@_rNBI7ar>Du^Au0YM9k(pvk)Fz$S=42UVU zwT17|t&T`c$(?nx&nxn7h6wo*uP@e!WkUF&7!lupX$vBWqrVAsVh<DH`<67|D3dyp zWwr}DsqZ)!jj@?3%>V_U$o?@Cs~W<4sJ%pb{01^n{4fH~QF+6yHGGM;VrSuauxaY} z?NBaY#mN1-HO^LO8+V&deFa(O<-)@ga$gyjSywE_qhA*mb^C7I0pU+5tXq<B@M-F1 z|MAH))s`<%w^2~|`Qko#g7_3bx((K$SovrA#S?bBYNK`j>iiji(1F_fdOt1p$+9h8 z(Erfn1#7Yuwrg!JN!48Q!PGuMgP^LjemPjWTVg@j*OYiv|E&?^!frl?Gg02GCe-YP z)NTI94@YT1O7_TX8AY0XIk{PN?wz}1(;#aWT=A8*8`nE=0WtX+he&fmd4j4lW1;<0 zz=iS;e#O)uQZqaNMJt?n9Kpnh(v0y0k3zfEF?(4}cG<4D>qLU8u<v@!dm|5i(b?z^ zrIIU68@}7$umfb~Y$@1R$C9VN22)8yMURrEJC}Y}H|iHpQb*MFmDd-)y0O!T&ua|Y z#jUMiWS;tA)nN82W1YiL?dkm3gRnD1Qwz0!yZzwv5y$2P@KkMxz(0b&ku2SjcVWLY z`}V}HXwJFfw6m_deChnDIbO96SBQAoWy93R>WzTtSBLY$4;TiLHB<BpTsS@nrLvAx zU<BNHOwcN7d&F9>ET^O67is_Oz9-K1hjLX}`ud$%S1J>MYu@i&-fz$6l<n=5Bkq*C z&p#E?e_i$tSRzOtn(huoSWF~sjQK@}=tfUpUJ@@$FxdNk)@JW<MJ*F@fdYumnIyz+ z1QN?$RLPoi&i&mYy7G%uP3?;2xh?!OcEXe`q4DljRLu|8{LmdZ`Soj4im)_n+Do6T zgj`h3hjRK)+nn-<R(CG3(eSiexDx!_nW+SgWBjZkU`j2ICp#f%m1e|&bxtezj!n6i z@(GLHJa}$$$yVoM`S8A!pyK#yP&RjbZd6<Jmq@LP2dn0Vo^v8igeJFSZL%iC15LtQ z`|=x5URbM3m)fN4xO0`YA`(}Tcc_NO1wpq-q=xq+v5q}0nUhBc6Rn(}jBU78?U*LR zVDsn?fWjYL0IRtV(Ik)Dpej5*QL7{-zAyh<q^VvUpLnDed|eLwC|zLFOjr@^DH44M zEn7b}hiJ4ulRFs6RY^Sw7(t+{0G#F<k58|3!UE|doPUl%`OTc*nX^w?ej(?n^h|Mu zHwFJ7`6H@<qTsQGPrT;>V_QtXaK-|xANglGpuTMP4*n7HI1rQQrq4Qj8=<z|guD;0 zCyAgmg2)uuP~LEGf?BP=Tk8|oX&@()c#za-tK_jOnH(NvOL{k0jAum`zVbTcFy<j& zw7ot_w*uAnm3?CVtn*&CY;h|!d0^G5P9eej?uK2bB!8G^8Ed$H9!8U()C;fk=ffE| zAW;mY)X0sCq_KP(Rnx8~dM%^j))3j5@$eJ9d0fQThOY%i4W!u=cAOP<`eyjSvN5Hd z(-oC-Q@YwXFg9K0#Y;!lToqfE1vgI@yIuWlTQ49`Wm3@zJb*?aHu;HQm=hJlus)z- z9zya`W4yGF1IO*O_*;phA`oVxn3Eb2aAhs^wy0f#eOauX3xg>ydU+ZN%$YZtr51u; zb8OdU%wT`Sw=o26vuQaXz`gfI#vhheUcK_HZohW<L!<=52gkp!@V0|6P(f9Oy7-Zk zSR`8EmB)ULm^ob+k=K6CC6Q;5tnv%#%W{(<biKIo%+yXbNXr(_`Q1)C+p-V_kSC$- z=OdDoL(n?q_LlTB_SX<H`|?|(OK)2<+pETv)&+@|0S^R3`$k%#HA=CL2qv*DWjpbP zR-VSSblQ}Q0Zf(88xgyDCtsc9kAqTZVGwNWeWc%%`X-Ut3CglC+`RQGlguqq(klJG zky*r3G7&ZUw4+W`U}ht(Fays-06Y<x^E3<*^0Z?|GUvaCmj-E1ah9GxyOMhbst@mM zN$#r0Q2B?{xv?~ljr7t&6v$#&$UBA+A#<e|DiG%s&n)Lo0#X@r3Vz@bJVMlpe2@sZ z>li>w%pz0^nj?v2-Im-7Z@9$jfYY-VXWhsG0yj2PFSo93J>?E<6`QB`0Je&vrqxS7 z?Rz}<g>xjWJ8!V^3Phr!KH}$(tM!)apr@7{axO}bM-Io>E-|Oa&c<%62XAbXzC72+ zMy)C^GEP8-9f4u5kh%|nKK)CxQV!S%yFvm4bONmjfcj(<X+%T?Wp3~aA^QLsNWHLW zlO=?OVWfT*K9BEXH4@#_fQQl&B;y_B4Spd)9)&ND3Mzu4_;a`**Zf%Gp9IA-=Wc(9 zC~LVC^sffapxhCa`wVVG*u4UO@}H%_G{Gs)0-0-c@?V!Mb?V2d#~RotisY-MHiW3{ zNfQ<~bf>btt9>*7-NDJD7DmP{<le(YS06r_uYauCd5%PAIWnXx1Nbw32s|M%12Q3# z=tJVa5sUAUSNoqYk`7mtk$o#uajmdv)@*i!<WF}8PKY6Ns0MUTeH==By?FgoW-D$U zz#sW;iUGHsfxB`uC)<7y@7~y+_cYi07zp<o2>VwgJA=qQ$t7C;h=A<pJ0L?4?w!eq zSAM@x#zV*#moFYeK)+(pxpq%<!AbS2;)ilTnqWDzVa4&?(+1s6ecCOh`lY@Igku@s z8#jMMsRvln1KB4CyAk45kzp}|#Vw84T>UBMk0?!*R(Z;P7Q7NGZszE8)Do+8W}JP~ zM_qOM`jZ@WzH*b*jJX@N=Cm=sn4jOE%P`Tkt7XY6YiVwrfP7cOl4G9kTb{fM)e40# zp1k0<UaPxbi;ZJ{16fM{0K^)&1|Kgr2N@_%fUo%0uHJ#}G5T%Ix!N%z!|2n(T8c+v z)zAo60m6IkWAtoU+&wp$${jcKZY*4yHu^eCmLK%u`Wc5MbXd>8qqUp$o?Yu+RiaE! z*!352hh5-3p!A}<3ib<-RjNwC=&nL^t--7G3_drtr;DW5YJ%Fjh|?3&<%g*<3%oH7 zS((x+1l)sL`SfsmBI-tsAfNHzFVz%r1|cBRItT^%8t}$K7;#TnYU&rPaJ!oN`kwge zgI99FRX+t;iZbWQd%Qq6?-FBhc=odI#S@VE1%_$^s4~y-Ax20)U}DgTW}PWGV`O^5 z$@LsEUN}|BHIP`ivs-{&xyStlF0p(@uBDl!v0s5B%mOIu<#un!ju{JnJ&b2Ejkzbi z-IY{M?XBgoSZl(YN1eEy>{I2qaDXH`p6M>wkz69-sKzUP-enEHJ9PS_q=F3K@Z0Fm zZBuXooZZZ!np$E%BKEDNzC=x5zY@xfyHA!W;(>LbMXbjjdA|bFq!PaDBIz?*e$&UV z1~4;-WM6`k#0vGT+X$=F0zj?TttiueW!Z9b;wN6XTb9w7lz}<Bt`@y`>pPc%L+i+Z zpO08Dy~=CbZ5^dj%Uf7XXX#x5ZU16P<)(iH*!i+r|FN?MCIk5uk^p-<_gfW|94gH~ zqvw$6&MfwkixfZhddQ0hsXQdYS;Lq;J*fbYTGy4)5*f!Gv`hko8AO0n0V)DjE(<;| zGs-Mkf|_{PWXsRxr6<-)@w&^{J7Q>fF>llSq=WAr2r@j+hNqordH1yU===qC^KGbv z^Izu?|9#dVNr80%<nLL7zP}&o|J|TQZc3+t1OC_Xwx%*#5m1DG?POy!`BZ;l00C_Y zr69IqrNA@d0sd~Es6E{ktY2Pl+8LlgWjBl=BbmEm3kit}xo8JNE2AhOG&6QGj%V{9 zY;FrR5Lk)d2~<Cc=%(p3=OdA5*j*^w*S0ig@Lz8K4SSmN%kB$ayIUQ13D)fzzQ4W+ zkGgl?@gH(tXFe=`bH*?R+z+V&HH>kL!=D%l6Wa;%0tE3sobE*rB4P-`KAav(en1Hb zw<E<g4#_8|eC75fM4_ak_uES?$=o4bbEgs>fBc{t7(d-#L4!s-d{*OB+hc>S=roQN z@P!bhd1X!^Y<h9#n$5l%Al>@)kR;9p*)7*S5x1;$7=F^w0(EgqRc<g_+a9JA@vy~3 zn2_!y3_$j$GlPlI^O7Uxiy7@WbIygu?7a2DWN_YB*)husrEGI*$BiF`pje<XPJzwY zZUDn2vols!Jo=hts;13M(csJtq-}RkhTY7x;#JfdPC&rSr`x9%Z^Pl;c0%#E!?2BZ zqR&4cSlntuSbbQ7z;rlzK4ahu*|qGrZTisA1FTJ__<h{U{^&3!#gJM%3}<cVi|bxH z83D>4P6)t+(8qEy#UgdU>ud7Hj4O-p*cXXyXO0g+ac~h8bg;M$_Zq;s7Kotqt3#R{ ze(?Hwb>Irbbh51u#k?OC7k}~Y%GBN5LK>AN>rxxILNN&6yVPuMz!CdlA7j=1BtwK! z2Oxx7rUeap6XGhj;g42~TPYx;Xr|zam!wjH5QSn*jydhz!JJ=u3-3?N#L@_6s~1yU zc9K&aXq?GJQ6=vRe$%LUBp$2XZ>VAiPqR{sBcYoTS=L+FVqy}S^!sd5tz=T{S+fXz z(+hT+|M+nbGjF9D+QB($l{cx#mXnql3^3Z9lghNRVIzno#>*lr8yp(Yma4&chz#a0 z7kfuWP@ExDVk5^YMNdD|XN#i19DM^(CJx7mK_tRUqH*X0s_$`k=fi^yRb!4@;m!aX z2wGzq(%!i{wShy&M_ch8<5r-zvzkJ@=xV6ak)^SYei{EIvk7ACuFkhqYojmY2}tyr z*dP%gZ_}6u`LK7}(58e^*PgS2lkofO5g<q6_+teR4>ehiZTY9F5u&%?G7LlUshr-j zGEO<0kvb}>$<;VTPl>M3Q4<0z0mVRCe<WW%wmtphdJd#Jh-uuyuXWD(9Nz{u6vS*8 ze5WeyOq9|Vn2?aK#I+VBcG1$l0{}KJFig0v21sZ5u@sT2W$15Om_c8mxg6O~V%i+w z_S-D7-g;t=LLM7SsrhZBD08C^|JSvWX`WC^3{u=k>YPxr_<a2ZT+zTr7Rf5{N>aHL z(N?XPbfk-|r)yya<sRuEwhT>;GBk~G`j^<v=^G9vlB&y@zs-3IKEJ-14Z!XX4mJn& zk6Ej154CJ17HlU=BUol0dd!mAqh<(y6g$P}9v%3kc8Gy)9)qKOufUr8kB?z)t7Ar2 zgKUGWW4=s=Ca!X;mBmA>CHn>+zP`=Rfyel<#oK7L>QAD)=;TnyGqu40j@AXP-Vjl+ zd}peOO^NlbN?9SCncp4B5P&^}%X#tnN`{z-D0uj}enl^{m|0$ffoig@Y$>Qu>#E4~ z&z<k`d@^}{UV=F+s2d-An4gC`Z!?(^SkRifQWTenOoL$OJ~|?lJXMb{hB=Ku#vrh$ z>LL8oZnw=`gu%HG3cP6UDZfvNAkpG#v|9+UL!-9e#?PanPJOiEPyjc`v5zEPi_(^( zfu7Vzun5z07P_J94zmeLJDuF7NkM^*1i^m@$@pfrPe`{OcX#}A=?LO(kG^FwMyW?Z zNDVw7(*oQBI7M{x!?P+JQ5k~u`ai;AV!H|rpI%b~?&u-d-IbOf{fuh+<%r}AIQKYv zrcxZ9>o8BUc_B}i768RncnXx;gm_uIYNncv!c2B#q*Z|0DsZ3hFUENA_PPP9Le83@ zgIJM$p)Ovm+q_Z~*6Z|X0rx<Cys#>19NxM>M)v8SzM3xi>Q!ZshMrRETS#yc9F+kC z@e!!`J<?!D=sV6~p?W<!2&y#QB2j<N+8I@Bn3geD*(SC--vCrTFS>)5&Z3XCr`J68 zH)aBP94TpOSa#<;aedI$IgkG2T@nMf<X!P(SFuCv1wJN$s^`?&s3#?BaAs`~UqplL zPWf;rgWxQ7VKL@w5E)}m)OH#BENyz>w0knxb-~r<T?CGax3DZbv4*L47+u06x_2Dd zj-#7M6bTrdW`HhG&p8RNidfIPN5;FuNw3z>=P@rvrDuh<6r?fE^obYrt_0|SGzK5< zwUpTB2MWa7L*W3wIBc&-UBp9DG)p)MT7MZYg_@tbwR+9vwesPZd*aZ9X?M(idL#O6 zq66D32!{mNlDE*%&l4|X0s12qP6e@whn!{~6algWmH;z9jDnJ;i~<33zkKXhj-80T zeJ(txm@hVqTW~L(5zIT@)QA{2DsBP|U+ZBa>Nlif{1Y}_p(WZ*)OB}kLs*qFV!2gB zar4AkISOX%-WX0Ix+w;*f(#3uF#m(0yC}8Ks$IUUR9*@MIr5W^w1E7{oKRAe45Yl8 z39W*^{x_F11N}8Qc6TrX*@9eOm3F(@C(S5ig|^}6oIp7-hN>uN<AheJ%684$0W(NQ zytX-?jAge<<t6d?5g!dCs1BV1T_~C%;Wj_A&4_cCxnEe^$9v7bW7B63=nYG?VB)_2 zOQ73wrd;TP`;#b(q8~bd>@eYuGPG9^_ABM*5r6~bOKqfJ-?nRZ*8BtfOJ%@t{DtQ$ z+pU_Cy3&m7BQCagc=xVqm-6?NqUDL@OT^IHe3mIdE|z`_?;REYV&ztdPa}&uI2Q0b zZntk&d81%kP9uiPQhD3TQUo#Jw;1#{$zjfmCiHIdoueH-U^Ih@b6J}iR5V=r;?Sqz z2jEgOVxj*nBG&Pc7o~^xQ1Ff)dv_Gk>?3h|P4cBN^gG#E@N09TtSEbm2AWrCWr31~ zpdrXkG)KM#eXrL%XZv@eodUK%Xo6WzDB&arW}c&N(X>3JXKEnvX0+W;Gum`}@*`Qh zvH|@KiaB|KSrVwS`|*^;dH?i@$}Uf|1^_FSR7(_;B%#<-6vE}&6Dmu+okH%w!l@^F zM_ydDRTvX*K{k3`2bmmN1ZhsJ2%0_teTpJX=qCxcZJM1qR2uh`u?OZW;sn=YMw7L> ztf(ezf3mZyw4tc+7IMrl7ZFVg8aV5+2+~^iT)pN>(e}#}I9v)~YK(m#tYfzy(E!^@ zgqv`Q8EDEmkk5GgnLHzmx~aNkHK{QH7gn<@TY81n1lOn@XBmzY>0`1o8xCS-2~GJD zDW<F!yfV4HI}1W%H42N$;IvZBJ__^NfHW0H-r)?geDn2a1Cn8kvb1=DpN@`i`huSR z<~lPQjXmVp?PG5nr29g2#bxTq(}1^zev1t|hLWMdTwZPdQLLC3pBvUX8iV>~7d{(y z9ktLWdzy6;S53RB2gZ8U1zUP*hH>p|MGQ>IL0)Zay8|1<CP#jRBmA}=6EsNG7+mg_ z!w@d{s3gWK)z6YGFq#dOTr_!PcNc*RZOBM$<qsY*<JWYmzE$OtrSd;>xd4;w_K}+E zB#M>RnrkHY5+rN6tLP&xa<_=ATptW^IV5kQyWi6W$=PSKP71a23T2Y~%*D>1?)JQx zH4h+;{NL$weMvjwel^ih{JYoqo2$P?`RlpYX??YQIoay;sBI8PN;KM0_5^7O<Z(_H zx<U!IP0`<%3uQR*3K&sNm;lL?k-vV5&Bcy_q`E;A|60NzPEbo{B6JOmtq4YR*QqCv zSmXA9_^^>xL|n)I*2we>Y@jQl{~hOx98~o({>OJc?)OGhv$!6jRU&M}l)HI3E_(2t zx<5R7rbn^cq1p2gq(=+qgPwW6jdHrRH@#lc>OhxNt)P^OH*&^Qv^JoqteD+*iORwm z+|A51NsM(_Ig9;AmMH`xOmUJ|tjkBrlZsa3am<BAgr&`D1w8x^$&>uSFb)MyNO9fr zf#bCuk+Rg}M?|-*baJ&vSe}k>Bw*6jOEWrHg<D%cJ?B8Hkzz90ESikvC$RdJVVVk4 zHWpzaZpH>mIj<erR6ii-p-m%SpLYYFhCH@LDFn1?WSnz1jWoL$JXn-I2ec8DoxIcn z<IwdZU^7XzgRYn8#APhlq`mAh9ptDR^40eZaNc@Tvo6&Fu-JoAmQx?jBJ7=ABqs7} zcG4VjQr<TZW%l{9RenEsG;|HqZ*n<Pt3s1GM~#X$*FGRLp#$)=rI`itBg;If#Qj{8 zn_d-b52jfpPgasr(`>)&&?qQ7EUmAaz{gBhO=w*~c9Aa?#f|hB@fS$4yLe~Le5>D8 z-+A)>CSu}6&7YEG95CjF!?75^;DSUFfMlRLl%WCYo{fMV@}<?2bHvS18-6Zp(DyTh z$cTAHcPcyWg#px+KO>8s;IyUZug<U1vKdI-ye<z0F0*&`Y3O&t!c<*&1a)s5u7Stf zB)DR7Rr4<}Bv$eN(ci23)8-+(Pa6E^>9GcalixBJ-vYUIun5dzkZfZVRB`|=Q<R=@ zNeF$aB#U!yD()@biPR5R=$94=8h!dWg$|v1kQMW{$2`CU8ozj$g-1N5ELAM#*cCZ1 zXU{^k!!;5_Rafnki%X&}?v@Je3gT6PYS}j$*(127RTgREkH}d^HgaYl6C5gGc^bHg zNNb@B+PE?!8P#ef4vLrb0pkGuuanwG@*w*+7-)x**-d$gbW~!DhFs3M))L)bcT#+r zip*vqI}*SJZLB=f^_aN*!s$}{q>Me}mbDUZyv0K%@HnyDnNld^;uu|1A~pN?mU*(a zQKIvO*#s*!n>JOOJd9y5BQ9Dz=TLSVCJkI8MUz;*Ta<<tlsp^sHvBVIC<j(31*x+0 z&LCI?lkf=>UJL4FrkY(AQ;i2pj(Vs(q?!`1v@hVN@TQXOs|$4bTwiy|I3K&}Z+30- z9d4X3>nyYt%d*?Fc`CWtQYQ-ynCD}b3ha`c6v1k4|K+6vW(dJyo-&zGekRsDsXmd? zH8K}LJ|m>|q{jhCJ8AZnEzsn=Um8jrwpa+1#o(k&xJ4G6c*2Br+fJp%3>?=Rpso^q zwz2@NxVmGMeW?K^;2Db5QtkL#ltpOzBGQjS=TO?d7L{On$V_V=S+koD37oHN>XST( z82ofo)i5!0wAJJzJ))c$Uy9Y^fn|FNSi)dyG@##!IeIejEaUzW{hxR)!id`Vb8Lga z{BQ|aTLNNY*rCU-K-nM`&F~wx1(20Slw1It0w1EQQ$4Xl2T~=*ps=HqtY#U1Le#Zm zhz8iA$gUd?liaLa;6F7;Hxb?_d@FrQg864f1m+C31>qgYTr`U_^;73X9{mn^u~!y2 zB~oGF-V!0hGaK7Vy;@au-V8x&dJ<El{4ft5gCDx6-I5pGa5<yYXwoIEloFV?R(Swv z6JkL6Cafb<2IIoAP!54;RL8pl)eh`v9WAc|7RC+hQo|Z~WT(WQ7cO-+1)a8LiyKz@ z)g~-ujW1fdZ!UqR$u|V9S82!goYW?qXv$eaM3J|%adU+emQJ!pOEMZXm?C<RreYyI z=~@R>sLc^UOY(UgrTez{h?}4|+zMcf^X#JY$QmHcaEj=5xm%j#dAfB?iOL$ni$S)e zSZiDq;BZw$YJyrzX~dWV-JBm<pF*tOZ+nXd{hN%yAk6=kP5bW9Y<IWtQ%_7c*s-5g zEKC06U5Nzb5}1Wwx)mRqW{iEaQZ?8T7=CbqhpMK=b_yGJG<RO$cWlaNE;GPgP)+6* zum6G1Z?i##Q@b)oqdU1ijmKt9hFa9sf?c+P30BghsYafCGRs@1%)w2Z0FolIY5LCD zx+Xu%!6lVlhMJ-^6OSd)r#iC0CwoC8e4li2Zj6qBB&01}?m$E3^Rx7R6e^EDbVgv{ z1pFnQsS%xI*LV1lsUB)j<Zu5U{27ITs{wq~sEU;y;?W_Cm7a2X`kM^ewF5jF&b11t zVor@icLGnEIYl{q(-qP4h#+4kxeJI9N8#3OFeC%d19r^s%l7p}Fd5^>pnUKVCTxjN z%g;F~N)4L$ML$Rlx(jTV)sI>H+vMCY3nRL=A+=+z-LV-W236h{YY{-UDG5u{JOHB^ zMZx`JHnuMsq(Ll)rzymuWrK)1FJSbA8UM9lf&S1kHKrG0G#S+<#Bi|k7FTJ%l?e(^ zzB!01a&My#q<UX_#IXjC4_g~LO;7e($#xQ9h6oN4Qdu%oqAfw@j~-xly6^ClT*AE) zJvR+(#(>K@4>~CF&KFP^2WE>G{>Q`L2pt4^#TZ=<vBWXS2oS;Z0(ngAsH@hxgl1I9 zWM9gS3Kd>}RW&)LR-TzPQ5~8(b9WPEx>GtiaW`J0P=hZDVgW$R{RyeDdLCTE0$wSL zDyFEpL;7cj^0#29#S0YAzG)0chn#VTRETdFz6UmC%Q}+m>ImSYlmo`CQEpr#Zh?_3 zV47*KeF)YSb%NZv;-zj9F=zt}N#C6ZDdvg-g}@gFZ80q5EmVxB@0D>%9M?^`W;4<Q zs#sL$r;z1bD4CfEN^k+L0-f2=cw8zd3zrA50wovf-kLlT%Psb9qR<T{gwN)f<o&kK zrq`S<X%GJ&=YD{8hLC&hs6*em)WEq^i24f<9T|&5aC4+lS`n~pVGf=KVYldG{2^Ey z79~t%#+gbck|Qhej)h29oL{7r_>W<uO=5?@D<mUFU`-Ia2=o>>B}9u~q$fY-#_Qqo zx+ExuC7KUa&yLi(2c4N@P;gKtc%~1TQjg_mCbP<GN~i$akbJ3QX(>Nxq_k6{wF$K1 zB7{>#HZ^=4OP<xa;II46())S_)z%PHjd|t5u9(nUKn*Zq4l$}{fHkqJTw#{4z&u6V zXV?N+A6;)zc?6!oP;OVz`rQoiS!py_YkUkSf3cb8)nz;lI%(-x!&oBEG|_VoVpD{( z#<np_{CWXgd2w-(!$PVL2~St-?|B~X8;N2d{6>L%5QK;9t$>Z2>GwMvINN~5wLV;Y z7M_qtO=#R=)Bi31kg&c|Abc?^XV^=2@g}JJRa|c<-=rOCGVv6R!dF`6vj*g02Zo(P z&w`o5f{9z1pR{*p;Mivf_cjgTuduEcdTQwzl3W8gId`fHB(ZBwOqX9^IgWP(A^o$e zht|1B-GVx-ZmQ%5`z2v2cSlbOt)K4zu90ve``{JuW&vxwTJ}!QUTYFmJub&HPVSy0 zt7AK^P=g*r3D??`o6NF(MOU$8SD%#kQ4hA)+Frn+FS6MUdV|vtK3?6Zn47QjYL(0F z8vFpjd%}`zHyt53mBtWpo76%qYXhcnDiQBGEB9I)UWTF`qz?l`%c_81-4Qh7i5>gD z^tDBLBsI$5ImuHRvVwGTFlk$#Y!ZTJuuNFLBc<Q1{@O+=^xP-lvSoaXX;Wc^>sNQD zUWI6%X=6oOIrG%&^fuiyqtPZ1O+s|)9~lAgv+<fi%El{E=940|1ohaOQKm*W4B02R z7W5yK>IB?`2aWFAby#f_rkiTHfti4puKW!TaqxoVbKNh)r)NV##UelBUML({;Y1Np z`A+(~`{GZGlgZBlJB(R?%aASwZVL%5Oc1>qGw3eqI`|=ruu12lKvRM8;RBZT@L~aj z-fhrzgY-^}`LKuw-nUHMlBb_!20*iWVYf8L#`T9~w-^qRm<BcwQjFfQh}5kNNbO3+ zQz}#rzlH?X&qi+<lavKv-}h0nPa=FzIs9R>`n{dFKXSg#+vU;p-i_<P+~12Nf)#a! z$lJD|)F_n%-J{=6fjb~b(bVw**R{DMVfAi#J|iHhep}+U;vNocBq659u^i0CkXnbp z-nn%AdHAjdkna52dibBW5>fyX_y4S^Z}V$ej)DRKAwp*`i2!3|%*lYGX8>ftk(;_D z=RyBje@~L_|2LzqxB~glg>weY-~XgwYDxY(#aK7_-^?F_?td@Zw-|x^d(pm&h~!Y@ z@1^u==#+jV{0s+qa6~|}hP^7TI$8h&wA~uruTnA%TBx$6F1BqGYh_VuTbg8?g$*T> z#vZ+&t7e-Gla~xumLEsGKLCY>Z~Xzl^Vk&ypU__*fRMw@_8jsCYM4drl=1G9e167R z*XwrAhe<=hFStFHwrFECw5bs%@nNLjCT%!_?BJ{FR8&8_5QhO=4tt+m<xGqbh-h>_ zxPp?SfwxizBVZ<+_4<l~ql0zf{F}<neO%#^+$^$)b$-G3JJs0f6#r~&p~)rh*&vZG zwp;bK&EhTBgPi!W%BCN_XufHEy7h}b&#~^zeG}QIaag}~g^gdCL%+U{$*6<8y2MDT zd=l}6VpVs`=2RUp%Uml#+%&*+nryk~S%1qe_yFsHR(pQ;HYnahK%^}HTz)tAY$6p^ z&mXbc{eQSR%cweHcimImT{rITt}W2wP~6?!-FETf?(XjHvT=8Z0vj#v6uq4Pxw7v0 zGAlDH$$Xf}o2<Og?+IbS_FfE|Z*v};$_Z4QckFUV<xHX!ALqF#Vb0oorPyAKK#RO% zAx<xw_E<yk0s^0xR2S!bWFYc#@kk&&(ZynBEdDSmlvvjavCje*<WmDKux2yO-naZ} zSTQP(>M=IZ?h|Wzugr`sH1)H>JEU_skGo<e^`e-({`Jkp715sY>34pwH4oE;9pCq- zOpyDd8M#>u#BZZjbmO-UBkWA`3CG}O(?n*FT!+m>%p%7P)5EeEm9_AML$||%<gaVs zMf+fp`Z-D|%c1<55$!-2@U;uMk~|z6w5IrN;U?JRI>kQ|mD%BUfn7UEtC02a_fba> zPE5@%szg~%KCOKs;e&<%NKtBPMZSE4aS~FGM3o3e4Mn!C(ojnrh18}k3<o85N92A5 z!@9@_kjmv7(FqVqR2b>BDcN=L6@~j(SIN5XZF3(`mS|1Zl&Bg4>~;&684}*e*H(!p znyF6LPc*vg73CZR40MWX;4IyldILvr&($Ln5-M%ygYNci-5W~8raLW1)vTOb#ds?} zDm3HmPFO=d&L1(TPWk%Rgb^0Un;4WBCEJW#_1*#`&0)ZIC4kV^yTCD?f@>V@6E4p3 zDf=K{%6vFltFv2RvUHbB;WAI34c`w=&?!Db*2-u{*x|J<MMKE3htwfvcTe5{rXQ71 zD5hVa<vu;ZkJmng?^Z;`c@BzC?9VaJ(#q%GL^0{36JVNuq0u-qILq%5T^6xGmnXXq zm+FnWyaY02=gFWqk(RQH&Y0%eWl&Rn<{uwhFc>XbXM3T5$wt?ksC&q<@l>KFo-qM3 zEFVy?xvy_J7e3D6&j>22fLWcdp^<5W)TSqYzLRi@{9-p2mY$i}kxwvfUKpJvDs)Tv z#*u}jbs(sIB&hDK7$SQ28Xg>ogjnKFE93AliB&AQl%A{+D=vN7ZdWsHaF8V2)*opk z*TfPooMf@V*iNQb<kVO-PkRP)m~qf9=0^9N4Un(jLBy8v;MJFEF4a0xW|w@ac|u(2 z+ea6Z^EL0;b^c3s>&asbYj<QLSEZ@Iw~ua`xev7eAGJE@RyiyCq?L~$QmUcx6MxyD zr%uVkz$f}!Ho*T^0ueI1K`D{X$^dA^wPamkCr@2|T#-i>p$r-FZ5FP02W%?1$lS@v zwg&y8@vN$N1@%gIJ#CKQrsLY5+U_!w<zjw_e0#gnN(zx<Li;N+8Un@EvkfcH{In#D z8EqqYxKeXy%|@*HD<9cn*s~(0`fz=MgT=}B3^d~ewt13MDJr;u!4}p&?*|}#4rYgV zc8PZMA{S?)YK5F>z4{BqcoRQ4m!ym|r`(_`LwYyw@*y4eB>a#N3Z<QC8{2ATX-L^4 z??`H}f@#sHsa1B}2I=bwNu?ZsI>AwOuVHLG-&86$%f)N9*4miz)&w>rf-iPl0PPVz z$Um7XXFroKmlW$l1B?~;=nXWdXeZr}{F_7{8#AQKwF|~HxZi^^_>`KF>`nniNb=2E z(XjGk20Ri?2+V@RmW*|@?<)4ct-kZ3WWMqP$UI*){yN$+Y{G1Y=!REz`XC-L{v^^p ztd9>mwY*u?MR$Z@t_fCaLb_Hl-{Afbv?l$j^r1ZOF?0M@{0^wWd)Bbv&#k{1)@gUN ztJj1-uL9vWSFKGA?7?W1@_q1U-gEL?%nzHrs<U)v-*#&5t{<jHcD9YT@G?~&{C}<k zpP#f6r{)9U6H;&foMPw_l`N?f<7{Ao$trq3u|6FJ7E)OB@O1k2;GUGq)(e~lB3Zcs zD%5L=yQ1O+4hv>wL7k1J?i<0tViBo=@UMNOW?hZU0Be<Fk<E1e_M=U~qv`kOw>R2f zrIsCzb$<<~exSC>wgq*vg!rIZqb?KMvU&C_&o_z3Q*jt++Keb++ZfQ6pGYbLODWXq zw{b-QnOLEFoW*L-S>%5#FF3K+uLDf!Jd#p6-^v)L1m{o?wEhkTz(0m4JqSS}ok;$m z{+qpe*G8tr0*irWO!=?w?}R8<#iDVU>kESr+2{j>>o-V$Fllnyo~Bc=iWTY)GOQlT z=#+dQY*Pl-U(NPoKB|8=rMD-@z*q|m3rEx$x!ofqGbU!Si=XcCZIxh;+k3{f^T%yd z_YMbe55j&+Ly6i*wU;v$>;MSI6PcC8y+On-Prc+~rzZ@~^VkRhCG<X$+lsFRfdp|2 z?6XBlP{g$?d)F{DYpZMgdS9L5r{Da+=`ohS;+-c~b#8_)6F%RqSW<5eTumbrmLR+R z3E-TzN%6QgTCWsG`SDDCo(k7US=9E8R)Mk5&Hfhq49U*9O{P9A0EtfJE)=26Fn$?x z%G&GuwrIdjzyaxKw?*lwM^C|(CFci<OYVnEyOvDI@5uDc@wh;nGIM7Z@)ha2U_v|u zJQ{e7@HP&$Pjd@`BV40Ax(k=IRuR=hD~%$i$Nv!{9wf`R9X=O4<!}%X)c;W%Y%9QE z0khTY96m{S|79oVRsIosx&TagMaV4TopD*KawxmM=#EZuzpdgZsG90mrspQAn_FXc zbUq}2u*zQa)=0T8g%#5^%%6(_WhO5VdwcU73v-L3_ve?bTnPKTl)%Ud^XrAtok%Vk zr=k8Ou282bAzhCvvkc^38L-VJ(GV{kP$13CG1JI7F>E1Qv&^|D#QLVAEJR4Z_67xm z)0A=MiRMvI0RAmw_D5(`y~FQj1sf$EXeb>s{B)FpZz^@5ZJw8<S48m!Q|88GGR&pv z;S9ytCvB;W>ximb$B@cCp~ocYZW8ncofwLInL%ov@6)P9*8ha!;%9hyUprI>fVsIg zt+FzX%u9DT1w0Ie$z-T^4N2?$t=JXT0m2S=7H<X2#=w9<&7Xvw1;rJksk@s2MEEo< zG~aN$sd9*@`fOV@$z!$s<txdoaUNY8L+B*swYucn%C%=B+Ain<y}k3!L~tjt&AK#J zDw>MVak&ZBm{k_K-WMnw9x+K9fho#U+@6`w;rZ>Poz<wM28pin?y7oB(4i9cdAux{ zzZd;I3cTq|BHHNgI&Ar#riX}nJlI34B8fzt*kckRy*BgxH}DpRATK)FEUH{Kqy_0Z zpT0a2tLicp{M`wNq!u#eYyL?psA=AI*K0lO_y9rN=XmoqpB|~&6Pveq9e8Ze+1`&N zU)uF(<QjiG;NW72e<O!gtRt36HI?@JVx?5ye<zEyN*K$HQ%54G?Q2LC=~Rl*4I}R{ z<|T9s{;fH7x>YliF@b%CH4$|xsZ{~9lEqC*TB^5nxcODqZeVL;xB9K18?uT|-xijU z4jpG)Mm8I=fza6%n<Z?45s*$OA;o`C{C3dMAFoYsjzgz}rg)k#=?25WB3@3~eV4}* zmQS8E5ZMnOvpws??w7l@!#wgFkSI))_S?KXRTYg+niq^Z-!)?R03mWWR}G)oDLetB z1Dj~2%>h%)9_Kni446tQoz0_K_(Wej7)bm_Yb5+)BBwCE#4%!d0r-|D;S#z$W0%)R zCI1-oA%_tS^grZIAZ2?6Q1%*DTruL)XQA<ZH{N4bc8GsRDMS$Bp-M;@Oq0LxU>G93 zy7K=e*YlGm)@5T9KbqWSn+DV+Qfd10W_0ijW>T{(vMtzzikQ{~Z|e`0{VudJv>y^) z+%w$&o4x<zRACmnOn*-b0b#B3e}q7vLlX)3ze#CdBwy8~>#IrIgv`y&U@CM&wHygK zNOp?fn2Mt+kmQD#eR;np4r#Pj&q_K8$+WBOs+*fXuQ-1k23su?A$tC_ENKr(>YNrX ze<BVBxTlE@hns#!_B>Y~_mAP9!JgCQa59~f)8cS5@%6QG3k|AI=vg^TuMMaGT5u!N zrH#D@<m8K&-0v|!_SNV<VRyRB1`!_AONWu)$y7^T`TqE|S_xiZ%QHb22RwXkJ!it$ zM270P(G9!(kUM3v`qvR^fz<BeyzBBR!QvZ!G!D)L_%jmrR)b81eufcV2?G2zW_wEe z1qI|rE^R#XQ8|OlPoAJqSb_8T$bJC*s$I0kYfNJEy<)(-RG17s>w-~>+}{y_$^Kl+ zCl%tD<i`m&K80~cuFVpVZ<lhJUNLydGdku7+zik!1yS_3>4#bC(h_av?M4eMN`vJE zxOS!ld_!AJ!PNl!6(joXlt|h=RIkk%ux}S7+~r`Wt8TD|?1peV(35qWwpP&Pim%IH z1n$a$KZ-Zc1P3+Zd#H0;i!4d!fm`e%$znZ#bpFbSSYbrc)fbyI@gf!}pEh_n5g<Zs znzNh_l{-i9V2f%yc;x!Z|1JhHK#V^+xDJfz9l&!Tc(6&Ik_Gf+A$7NDkknc}RiKJu zSmX4hfW&XwYQYY`{p$@~-Cw<l(+-wTKE%@lyk<$Q``0Z(+jCcFVh?)6(?cu!*CV=B z{g0;aK+L|Ec2A*qExz|i2$8sT1MXKv^!JioN#%Dv5K!vhPm~YpgI?}e&GC<8M}mt) zlHC0|x%bTI+`~PKr}j_-_$OJE-UR1fi9YIliK2M%!p||5?I*A)l@2hoDmrmwG_y%6 z1lbkYBvpdWO)Zm3LD^jeNwuIYE{Re|hQ;#V!wog_d|u4UmO>MeG?rXSw6w$pbNK+` zP(!X~U-P`>Q$*M)GO?7EKTBq?=n)yO0X5@!tu7bC<1|Yxzw(iUMSdQChnBQjvks2Z z28M;Rm5c$woS84)JZS+uE3mcY0-bu>OEteFw?WYa`SxhBY(FsEy<U4{Rt|GyTE7~q zFRML1EW^rajItKuXX@$y3}ZB1Zuj=V7?-v44u1BTGBk^{iQHj&^tkhyf-$r4ibGvG z8dY)^#lL)byn6_lR1@W_GX_a45L@48(I;qTd9MOtYhcBZGI%<8IvHNXNE{BFH7)}i ziadgSTYtNSk07@}UR?Np{;N_AY*79{x`23g&~ePI<Zd%dZRZSQpg7-jXvYX1GR(8L zoI#m$_{m>A`-dl}x8`?4x@9HUbJ?7?r$HQ35>RCu%@j7S_NKR9PtEwUVjuWaJFX#Y zz5^&>Pzvl1pB(HO^jgP=Xy$qrD>*K5owT}l{imDX1JU@X+My<>`HmXX-Vl_%kW{Cw z?$gca)%;0;SB}VO=9|p_JzC!dq%W0!V`@Zo5jR+IHtQXA=d9;7)DA-C4BpfCfo1t; z5H65@IVW39hVA$-P-rBlOuUjs9&<ociW%r54jgr5)S4^jJ~RD}yLS+JJv2Q52D?R~ zekEsES*J2^v<w_Pom6lyAfEHTmiS{GVS>MLidaX$7=_Du{AiEME4*bcz=*Ra(;G&O z1@FAFnOC_GmB4b0^0Y;>Vp1r7d~6{hbV29C?MLDeHruKLrc>AH;{v7aC3nbeO9NN! zB#&FXeKXNx6zH!KF|TaiddLJB@>Q|*hWSWtz1~Grt|PY57xrLn_miE%e6EQG!@@bB z1@!D2lRMpb8y(HuDaL8^i|d{xzQIctsWqwCe6L0Qsf$7=)IV;6(!QCQ7UP@@ke|0~ zkWk^usF>pl&XkqpUjO)7*l?I}kOGWO3%QlsT<6r&s4bnx!f>c_PA9lZ-!AJqPb=Hr zEZh-E<wc#GIKJ_gcQDIjYM5ZITsuUx*JhiybZV=jC3mr*L-k8E{t+4}Zi01k8^P_8 zl~DL#WHW}8;1;<uwE*fg9z8(m6_jK38J+YtpzCwj&Z29@Tw;=-j+bzggafi7KGG9} zzp$n7(Opw4^~C)Bmb#wQZjm0nzi8MjUAC!*q&bHqmLEmZ5*=Fiv$b*Qdo5NZ$6k>k zMwMLo54-TPxl!p$#5V6v9sWSW`stRF?TPIXEaY^NR;@M5wQcD+mOTc)t{B->e2@{9 z;NTPq(4ek(2JdE6JDC>qJ{Xu>-cUb$>fzw*(J9o!#-hTA>ejgD&Lsgwl3Lc!RzCQf z$|yn=6I)a_48y8En2WJE4gkI?zyx6Z@hOkYPMWjIU}Sz|#MmGr^p>_=YFkx`9Msh_ z53-8DjXJr4rf8^g3DJcgDUJ7!Tsz@T)~YLiY^PSmOz%v`&j6L+-~i_$dS)Z0z1`>a z-tKj_%pEl4I3>kC!AO#qQS4P_8VAFC&&edl0~G{OA!{wfMKzJu%Gyf7fHPgfUFgZ^ z6rnVf@({m%OLMJDM9-H>-X|<CR_TB#1$q)YYs9AFC|vk5%`S@ME++X}^C4vTW_W+6 zEJ}w{)Th?bLrVWmXJF)~kZL=+@e!<a-q_>`WH})cv|9r#__)*jMYWT^VlQ;Q77G96 z42I{FIJyY?7iCm3yYJyvM+pvAsjiQ9_*u_s&81fe;%1mThL1M4bS;u71|(al*38L8 zn_H>o&ndwt7LE^XZ$w-Hu#5*%jL5Bwt5QXZ8pD<DNgmkEfnm*K^0L2~m-;4UnfN%g z3Z@4~o7R~X%0r0pTr|x<VzIQx+h+*gt2i)>8YO=jFQnIlx`n2ljpz+YA!*`l6wdm; z`*V#O();PU@65{)0QkFWa!`nC684!2_wj={CE_hsVW%l+c;|MhqX-DkPEl&jb&+j| zIR?4$EAppFferddCfx;g#k=7}(j2npx627awata&!@&lokH0e09SHe+(!P@<%ugp2 zVgMxS5?4>@@W=FzA41V;?$m(>eRe#apRXdj*gT;-vF;PUVjHN3bBi%nq-I|UVW**P zF_J*Zkgp<#`Q);HmrGqLw)Rr;(L^3%&+4+oFz0yzVTmP22)S}AxU>yt#RU5Euy(zm zazc)fwW*rJEZHeFkj$ecnqimbjHCaeSmz~rI!Kouiy4LoxM|M3yMAgyzeNk}DHAxQ zf2kd?fln+db%SUg+EhG2d{7N_QFgCl3Z~JvSLCc_v!u?pRYYpOSxnT3z$u*1<Iy&o zwbm{K&e2yT(g&{a^!}+7>rSv#t_ZP#ZC=p+45;vu#ZK%A`k}f#v(}aUc?*`pm$;NQ zO@Lgyi6+e)VIuz3^S@UP*K~dxXMapXZ;#cNn5w4h{!D5(#|?hb3og&gIvJwWc4aiP zw^(&1AY8qHi`U@MBs2{l)4bly6taeCYZ%4>I(taAz)n+(4OB&MXU6ZCA)VUQy2gfb z+6*ucP>tldNNjslzKFe(#8_@I*rL>mJ)2><IfVh#B?MRLYO)m69gNg88nv*l%EpT3 zRghTZ9N0$Xvr0Vxi$Atty(}pQ26pfC0)AC~75K3BikuYI_a`!?ocwxx0c%Hb$@Z8J zZ23!1nUj_}nKE{Vzh7AEL}pzo$$s0Y%QO(k<s3VVh(fZkQNL--Yb+>4)JRwqC+gQU zmp9e)qp%wGejyz;`-cJVv8{k)p##1vi{C<?!(1zhKx|Lp+&@^qR2H^lpQ{+rKcz@0 zo|rit$8c#Dvp@EBw}V@eJMg!;kHcIMfFtCTr;9X=xtu{FiG{20OUmK093f+}+|Ti% zsdKR?&j@dec@rU-jFyD7)F1@333@^w?`~MVu_}&o8|03Z8qQ<y4$(rwq3fm+u8aN2 z`Mk_y^C0{DOzw`QymQw~QNiyttqacE#CBqeK^J-IK^IA)wox2&_2t#_XH;I%z>E1$ zCx?rCAC!(*cYy0xVZ+%O%f98rVrjvU?L1NOt2)t6%T`gJW6@wQ%G%h$>oYg8`<|(s zUw>!^*Zu{|1(8sc+`<c6&zgu`J1J$+n!4p+`*}y?@Ej9}dZk>*xC1H8OJRj2Eu6ji z{z5P1<;sRpN+cHo?vO+kn$M)IfEA*>*k_dI^<iceu)?g$o&o-5pMDEAP!ABYZFGUW z+<(Kly~pxWtyl-x;c`|1cjNGo4P$mF-f9AJKXbPMcNvBXB>bR*qEm<)o#1fxKgsZz z(Z0gQBH?Sb=QMmBuRqG(Y2I3rlIo;t|Ey=I-FQp6Gl~zVpIoBj78BHr0Wa}3Vj=|A z*6o<t1H`z{h%Uze%pO0&c1t^0-L;5b&XOW~r@mXx`rN6T3P!}1p*&-C=XOob377j< zo*ZV2<jv)xJR|v`Z-=Sa>m$?~nYxV2I)=BH43UP@x+)TuOx;A^jRj0GN#wCp;SZ$C zf42rEpgWMz{{-6c5^n@;15G`W{+_yZS;+Y`m~^5$(F(vX%#-YssS?e8QSjyB7$}dn zQ?&4KB>wttiCT`qfOe%S;58@7=PZ%7bGXUs3q4x~>D5X<c52x(Me~O^?yhH|Y5F%+ zKem9(Ba&?5jJNAEqu=Pd>UxqbKT>FGQuVUY@Au(b24c64tOMuBT%b{F63v&`qpHRk zi+}BapPQ*d(Wyn-2N&i@^zuPWls^esn$3A)+Md3UONu~xPsL@OliE`r7NJfK79C4# zc%B#^A*c9+`|Y$y(#fO*Q7^@=g3$ztrCG5EdHa+?OBcZE@fru#_Z0e8UDlTdLrSU! zY}k*?i!@8E&YAYpcEAiFV|wt~JJmx4=a#-gw6UK>>C#j~(XG_<xlCflJn;L`5)+}; z!QOPs_Y9!q710?%#ujfA*PpaTJ}*U|9|Zwgw^I+_c>}Hyc191$Dz?rMc^GMr=+t;) zegB&2LVw*xB-j9-vbEOiAlI)-FqlQdcgTkD+69QTGpbckr2(z83oR&wJX7V2L!&wC z4k#<{7BeKsvoHT1qd$NGeo1^7E(hCJ&ql{8p$jPcYef-Huevwu;lHS4!_ZTKs%lyY zMNW_(zu|lfZ*6b7&ZFBn&GWT1&zG1cZL$nru)fZ{VcLTzQpN0~_P7gp5FOnlWA0*1 zEg_AUq(i<NqXkAKPAbaZy~|2irEmUn`dp+NTY#)u$OMInGS8G21Mziqx8;d4YvI}c z0NxUd@zn;>PhQofRZr5Cb*#^@0r6+MwrcfK566z`gwdzwgOM&pwIB0>EYI~lTQMh! z1$OXHKHn$hY6I==zS+YhMbnHj)QdF4+LhVcR@CbJSp{}=bd=ZI`{}vqxDB*UyHf4v z$j%~bG2>3%V6a9EvL!>`VwsWoO#GgY1Vw+yXnFn8s|u)I2&DNLJL0?{TRo;Zl||w9 zJCIc^1HX>qi+V_pemh+f1(R*KxJiq7u>e4dPgl#>n;KC~<CM>2?=9nAHd~epy6TdX z{%Z(rR4MRcN83G}(S!7R*dljOIz89uJK`B%t6f!fJ0-ce!q36lTmRLupr*CW8JTM} z&i+8urx@u|(QQ!~yAX;-(3y&I@@)OxFqJ&UZqwrSV9sr`yNdij0)Yua5-+i=$s|MO zl)=Le%%!HfR@T)mTl?^pjK6bM@)7JQJlcYqgY`hmI|rT&+y!h0Jdrl>toPn_Y+M-0 z5v;kgv1F=-tFLX+?$%SsEO=4tG_-AElU}Xz<7q_WQLe@GOT>5^8$T1P$fsg>UUU?% zGtBbKkhU@1)*$yedphQN3plNJM=$1o>xMLA>SZRT17cSuhDmy)75lt(EFQey-Uj4? z#{7UBvZ*XMGoP9vNWGHcB_fvz1|4!QTn}w?8A6T4{+=hZ;#{8G0cE|uy(tCccJ_Y? zD-j=&g$><D$tHT~sx=-{zhN=2#o)^F84l<7QNx`d%nAXZ4lczq3S9l#W38U%5oTf% zrQ#j{ZE)7v{-$~14AxCsSJ%_hcA6AgJv1=yBI)`8Ljp0kOxNN_2us1pxa-H|@Y=u7 z_)Dyk+cwJ002f%j%<4o#Cw3BE$$q00^qLO?$J~TesN8$&_F21(%nYmxWoO%kvI{>4 zo{d*T;{Hr`8c<p3N!;I;m_3Hpg$bRcaYs$@re(F}=KJdEtfLHdbaQu^D2$u1AblWX znW=OzXm3(k7I+(!lcQftkg1J98Tw;;@3E5K1s)64Mx)AFr?<e!&(FngLi)Vkl6P}+ z{Zs!K6^7W_vRGT=U<D?}G%?1tY|DR7T7OQqi)PGV{;7KU7iqlSKrECLcwkS|$aB_- zIxwiL<J;^?vGZimuoqtWXHrq=UJ6)B8}2&%y|k&Rr=>dQ$S_!^!bW|RsIh9Lh)U2~ z>NlI+D#p*f%AG93QqjULJeoJZkFa+*?-03zM3)6r%Z;hS9fPra1$7DULH`e9$kPyC z9R!2pvfD6E#O$1E>7&NodmSB~t|@n{=AIA9`Cu!I877m~e}vCn`VJpserLc?LE(5t zAm<tC&ohY$cen2>J-I>^P#w(=?cXE*KswPNe&3Rf{7tnog&G7($y?vF<91?><XWQv ztPVXLF!-Y`#H5X36puOCdcsLPCI5&P;W_?#X8`rH-yKS)eyZ<a)%K;^mJ3e1C70n0 zm7ME&@j*sz{R!8|+g6{%t-K7_Aib5OV;`!UjiI#vFu2Y2oQ2XgcKL^@{4XmQ28+f} zk56as#;uM%Su>5&AXoO74$l@9)e?o@`A@^{tai+)ju%)n&Nr7j1eyCeklR>w!Q6$A z0Ij4S{R|QU)n=biXvMkx9bIU0M)IjTL3W>`(HH$nqy}ARe1>U>4~_%~E;S<W17AU> zc5FH$c;w(*$PswZ-0K2X5_haTLnnyZUq>9~Z`B>Y!KN1b{91?WulH5|H~D3B6)OET zWl6{eqA4AxlbkT^A5mb055Y?Qajb;`ggR##@y#xmzX2SlVGy8pvjE|1m;prDI_s|2 zAvs~m0MZpBiH{I=f8-|c@RtFnFGDKsyR6b{D#4!t@AP~hoR3<z%g^<+(Y8a>m3|M_ z9UPeleAK}}oA(4^@av%#PS`~#JIISw`K`kfL1-s9dh)RZy2l{)=8f=)`8FOYamQnl znf(FZ(**xdEn^RJRcfIodk=8Nw->=d%h!Sl+#LTE|El$doGuN>4;?b)lULSld}tKJ z*1}NOHIVMKiXK%tj?5~iwoF2IIR3;im42}34ubwwah4v%S<gTlSV-D}(ty;^DsCPi z!n%^)j)W?j^N<g(7P_zs6Gm<u6kEiDpaq>+c{7@>!DR+fqK~FM>^|y<+}|Y3*1eT} zGPwa07KxgSeS``e!0<7Ijdk4rRVJm?$!UQ;uI6X%qclWDRQxx&!oR6v#{A4g)lQ+4 z6O=yHm{F-8Yt|>ECKzKgQn~*6z%)DuYt#?HMDz;_c4x4{cSyqD^Xf;`XI4+$rJBZ# zwe(`r<?u348?I`t=j)$zJ+rd_feRIbWL_6Et|r0TwB$q}tf910q2Y+MWt5AYrkG7J zZi-^Sp121tRk(>&=R#aQtdW+CJ)&1JVpa%-y}he>_>CFe0pTx1|A*U+6)ooJP-8y4 zlD<~0=pLxXT}Vy%uff`F*cm$<Jk_~u!v~HCx=<bi&1d2@CXKy0(R;A!lZ3mhj##gR z_Mxo1xLi9xbt0&ZdU$&fLLJn^j(ZlAhat*l_`M?@OF*X+|7Otnr}zi<D~Qt&LYVEh z&JMc}63NKv4uUUo&LG|{y)Qw|u*NRIE3r`ICCKIJ3t=$+XdzpQ)k@#S6)NG|TGc-A zmj`+5HS&`BsA@`C3#5|c6J;i1*}9M17d3e~nRhZ^Kk4qzE0Iho_8wH=VY+D_w(l2_ z9g=j#h@bri?I|#g*P>78@2~(S)7B@9*X(R5=Ixp&Hn@x{*eAFZSIZGn(AQmqxQW}2 z-)1)+Y@Y5%{UbsWBr~iKA4WAd$!jgYzp8AWM>;=Zx>#J_A>5ieHL#A4i5w0B?hlU3 zUcR9LW3LeQ`+@0-5DkwYhS8-R6%-M16p@uYsqU+fA%L&Z(wB;BS$xnHYws^i6dKmy zpSvM%XG2Kyci+`aR!uLEt_ZR)+}RA+HC}#n?HS%lxc|V?+RD;QR;!O(3t@<!I2e6i z&`my{^oJeKCJ;0lf~ayeXWKWe55_fJvi)HJTxO0to%DuS4>er!n?8dxyhpDpA9p$= z`c|7o&UY{l^$iHIUg=7|EO5U!NzT{Iu#(PrS&Gsxz`nn*F)=i_tZ~4PBFb0Dz-Xf1 zQ$U8MLF8vK3}MR_c^lSuZo0BIr!RtKp*H$mu0`d99X=asmfHM7Zt~xG@Wq!aAw<GT zAmXa4*RlM~6{_eA%N0M_o+VOtduJc%HPMghg1RG3TJ8gW@5*6;k&D1uEq&9hMJQ#~ zat3vh;??|3skpI_B&c%hqm-VsTl`BV*BF<WSiacPrzKAa9<+X3f|+tP<i6#KdL}gT ztxJeVy~Ddnwo;2`K_uASQz7?dh^(m^pk)@P1H|-amrKT8|3Wz((6TmIrq46sgRZK{ zpIZ3dZ@>Es?h1}Y()>Q6OzZO8J9F>%Fe7K(5%74<S?pzvjA;FV>n5lVdcjG27LB+c zX*4POf^@&9TE@)2n8>s!tDH|lATB&9_NK%D^GXqc_|c>Yv<+fdApVt$-1pN1IB@Vo zqbyagZt6?Kr($SW4>!6}8O1{h`CbS2FE=7LZQxEcc^gyksq$P!O#ZZq>Ulha9opt9 z%j6apJg=mInx9xfJ3p<EGwGxSEpOO#0Mc5zk7_$kjpBj(U!~$ck2*%2wj{_@<sD^2 zUseaT|1Rmu1Y923$$dTpeimiN93}Z3oRBrBL26@;YKWU~8Vvup4WVxJXSM&TMSr!` zW%l1CM|zld5{Kgd+ZnKLqpGhM`@b9I4lpCK6#wTIc_t1%eYe82je&rLI$Qj2w)-qr z#ilH2xpOawzPNC+UICaaG6GYvdAOQCz7^A5;mazna;K^o5G&5QVz94dV1Cq_N_nKQ zqZpHnc4X;=+S2E18)6-!V#6n9w`X@|8Y8{IPjk;UIa9|>lx=^2a-+MA&AM}bg=pWK zvf*%m;w0!W<8ezvM|j=XRey0Xb@|ve)Pq8Lo6?9@dNs_2Bmx`(?;z<8ZzvMdQtDt_ z3sH4J6MI6L?_{TiKN{j*d`E#}C(P|5@&&m0BHUh~OpbgtB7cXAgs)5En3IO_geQ&& zn^WQoj~Oa=g;Xe1<IC3d<hwUSf~ilQ?#C3si_JrFgk|p?e4R&SDi*AZx)^cx4&2BA zw{-JG>JK4$V*z6eV%yG5>^EX11<@@8ov-l2uylx2>XwvZT!<U5G)Hu^j><q_;wipA z^)W{Fq5tZ}=OU4abe!_RkoKKv8!fxxF_n(-U>e=C)qcB2#|`@`C!ybNF|_sD^{E^F z6LyzIL0~^ySKnW<#H7ZFU=`hqCn60~Q)5{Pk`{ApFt8#jyiFUD3ArD`lY~iZzZpL) z^=cg>Ol^p7bT=x<2JFPhWa4GsuQ^(b!Zc-z8a=!KNGY8*lE;z<=}zA`^R4<oqfCNl zk3~_lsHDaZ3@>m~?(tEVANF{d`ovImw}&J7OkeT#!PvIL^?WguiC{+(T(BqIPP40X z-=h#5(4DnkjSnMRG1#H6*c1MiBL%`cP6J_B=}Q$znvWpjL^o|TsU%Rsqimh4rn|y= zLDVI$f_S=Fi)dZGAG7AZqJ^@}J$cU<c7;UMPSQDA{CiEZ^SL<ceu<-ICBMkgtPNty z?IiW+?z!FoUya(15~ithHD>H#os@OsHU8K<u#E@qNX9>PSC!n7hL2;|CC!H^%zamv zg=xc!Nk`WFg)gg#styfN?e`;w+$&-Qw~zt|%$<d1(5H)1$ye}arbH!g7ZL@}+A}be z*18S8&@-xV*BD_hPVh5XiEGI24>Mv@r!?el;17H8)`eli!}SZ?gG6vn@&1qeLwEcu zFx3yT8K67XTsbqlqs>xVyRZ{#j$*Gh{Pl$&D%(<$;pD3X%whJ~$bqJ^fBnq3&I$VP zG`MAjvR+Z5U8aCarMua$gwqARMYh3Pr<2nL-Cfn{z=<DMGJ99bDsypwUmca-C82Rv zdc!lMJ%|5is%QCTI+lE&D-QqGHjCF4ut>B`JmjYb2dhW<`qp{HzZ@xT9>y#_+9Goz zbp=464?pwtjt~nkrdfM7K|Quz`S{F=vzQ};&AZj)xqt%na-)0Oj;m|q%GpldNbp1* zR?P@YTP{|>mreLiM=YTz{pAW0PXxa!#Amg2R~ntHun+0L0uYEzfm*ixic7~02&L2U zWn!n3!EPMmpTs;(jdVnC1r0rBd4^?e5Ie9qp%{62(&CD0cp_N34ldP@(#DmZ)=PGc z&_<@U+9i605+SzWC7enqVz`Gc`e$0G<-fVOX404tqT=*~2*V=xO4!!&P0ED!<Akm| zO)GdFoMUHnfJkyjLV10NnDh`ja8n~$>tfQ?Tyrmn8bzy$eVQpO_*8IZP@${JrTFre zh{d$V9|DVSnU9D?-C5UoPNkf@ETbrtA{SG)f1XlJXN)^6Fd~UFZQfKUeS;%io#fKy zQVjQYEyB)7WCL_06>`vsN*wsYh#ea{0A-U9yUR07jEPf$jIBB)K(z+|)*x~!{jPLE z;qy#W;rr7PdV+w)S>wCR@<dSY8I|5x)xRjUJg3@Y{N}2jcz+hE>qHr&5@wla%~}EG zs`(*FWD#AK5^J(hA2;<($gi7K68FV90yfm}%D+u_7}S<NWp?CIp*^r**vj#X$P&Ku zxBm7vXQQ*BKu#>)uf*kDm4B`RnZrwL*#dcSuU7>3=3}G)UFcXQ_j$ED)((KPC8?i8 z?k_Cf;Oxpwpup_lwBr<M%{@4M_2c7z9BSgnWEL)n3IP#8n^?z)pD2`w2^`Y)@>N+$ zexFX<)xNn641z-ZBF9D+6pjTiYbv$(1D3!f$c_w^Yg*j91*V}$NSMEELE+P&eYr_G z%~ZfmnoI?=qqV3Uxmaa=Qf?*Py~HNXlw-k+V?j&c{7hEc`yx4tdZNLs^z9yPySw93 z@F>USV(o&{;N`&LE1DtaInX5^s*u`~h!mY~HxTM)JK0qe5~Gvy4#7V#nw}>zsp~Gb zf}m1_g`Ov>1+>WiVBu4pukZJHU=uVcXhe@M<|kkJQzDBmq6JJ&>ZQoJ7>>Rp`yD5w zG~OnD;@4^Ei=%_H#v7;p5ZbGD%<a9{{m7Y#=153`xCUP(5q+k9O5l?T?K?D(;qhOB zudmi#FNK9cvO_P8g+Z#%M>_V?3``GsyT1zNTS%Y5__Yq?pA@hMbPwamC4#R(3@d_e z0@whdi)lndcQvLr1=+x@fEcI})yw>OVvl$L>DbCw+$X_+73lZv5Ms<$tPip)tN@_v zZtm3#<cC-qX$&fI8W1<XLD(8)aRl_nOI+@tVT1DOu0w~};6TnN)ck_*V`?r>n$7|3 z$7IaltkJ?>cGNKWA4F^eFDQ&uz*4-(vwp=a#cF)Qe9NM3e3A;Z@63%o#q7AZ#GFtF zf()(n#(Qk}RTEs4EoR@(>^JJtgDzsf%T@B*4uZ|IER!t)aeyuy#`({RgFMy#l!l)< zzI63L<d+dVK*?&0lc!X7EdFk}{)ikSNyLUZm0<%yR31T+vqDH0MHzHmT|goCT7w1p z5OQQQC+J@ImB$|1R{xBBPm-`ig$8Pa=-|NQle{gPSG7+3a>@Y?uiGMvE$6^MYCsk> zFy%A-K`G9J4>USOMx3x?GEWvwnh!?%!;v1~UKjq&m%(i<OUx>A*@Nb6+MkqkweR<( zW?z(ad7Af@tD{+`++B*$v3ZLTQX%YyxH!3va+<G5dXmkJqI){1#LR+1*emXRv<?Fm z1_@)^LUT8M7&K=)-0U^`Lh*%?oh>b-dMxNbWhz+h2pG`JKG~ppS2a`WNnsooIw^rl zBI`^zqR~vs9vb~zhe1$wNzhdAIO>?(pL0`L1(m};xhm|bF>ph$7hNc1IcOZ*93n}Q z&&A?bN6XOd^V`p1AuCYnkRpa}U>tHF0S9q)1pdA;>L*WLv2k2oN1Y~hl2jrWY>*uO zjC1%VF>ry7#u-IZ!nQ6`g_dxCn4s$9Qpk4@c~!6`jtxqW<~|NX!9uHaa^b`)S=cgN zVwN;~LtX3;LVR3MepF73<MAfV3^lAE+;jcIS;053jh+aF2n89CF9l!>Ipv)aR!>rk zk!)wO%M$CA_TeFL;-quqgq>7hNTR7|8Yccm58O6hl~AxAV^`C_ox&j~lIf4*wjac< zO>eMDT_fdL((-65`yyzR-rx>fB+H1mWjCn$IV{TidJ$jcc8(xFXs#|HKgh3gAwQ_E z{y=_E&Pvj4<HDCR)>jH<)IpltNTsa8q=(#j6D$in6v&Qz?0vv*pO^_*$2j)LrV-6k z0$&cOKx$N(A1l`a{`uh4HYUDX6EKlj>_7CAPxVPDMMmj@FJ@qbn{z@WxQMNEzC749 zTc%vD)VTt^>07BxhfHb6jnWSdB<-RFV`mqP(PZ|Ku~+f>I)#;4O#01c0p?;eBFRQ~ zPU&vlQfuw}1-B;UQh;{xg0;D<%@h16P`p{7y4X!%Y0i7#uszwJ9jUP`=dWAG7rj9H zMJp(~#S@zKq6PEyf(3P}kj$}4m7q=M%t41z9oLRzd+-ipm*(jr9gIUkwC*?}%enx! zY@obL#fCt$)SenlvVFEMX4Z@ijhaKYcBztS1Z2$us&rjRAE0JI!3)*jdcg`0*nE0C zNHQ%1ZnJhFqO)vOAYPPivp-CzT_B+!Y&fYD;iYLT`xiWfh4b|#*|oMixU%FJo(3s? zp$*B{;-rK`YzW$mA<VQQqczDXJlpJi{O*uI5NlxGN)VI9I)sNJ7>Dx#WixE5!eGbQ zKq`?<Yuqa0Xdj=csc$MR^9F|#XpaXg*Etlmk^fsJUIW#=xwNHDzr-g-qvKWR0H!Qz zXuRXqQ@Gis_dO|e4MV&7IF5sGx6EQ$c!hAan<kZ`^)Dt##&R5Z*h@ThI}keMFjb#D zIVy+#Cpq4jwxCJ)lVJz#mOyg1h5d6el`Kl80ZK-HPWi_Ak2MYQ&@wav;7tP80a6bk z@@VIk*s`hfbYgd9a>Sthf`a3`W^(Q2`S>sHG6yzW4@MoL`z3MW-)G;7B`x-0?T1TZ z>yT{^)vEndsKEJD8_;TfOR0H0G2x68FKvv*Q<Pcr?ttt5MLdd{h_VTdlE!JnQi2r! zq3SB3Yt&m3x0LD>@>?wv;I@rq3e2r$^;E4$sHqOiQV*V&H5w0w-Xr0A-rdHNoNHZq z-Gqf`N;0f&Qba!e_|$vXNTN^u#G{qzxZqAlU~MMDC^ILnxl37NsyPbG9-P>Np$i{? zeXTLnd~yX#?`^Q!p(H8$qos|^eqC81$mslJbX%kmIoxwH3fk-lm@HrAs!I|%jA`_c zCP!^1FB7T2$4|=)JWsaGh;*ob9q-IU%c$c(C*HFyczXDbb7W{UiVO#ijwchetXR_- z$lr*M*LceQidi?QDu48SP|IBOua1R!GwV&+sK-e3E|X8OyYlDGKxUn~{+5M<8E)_A zo+*|*FY)dgFR?=rFgW!tjGvgaPOK#=Z(WQ-MxNn5^1^Rws^{XO%Au4qzMR#s!77fC zUc{z!Y6?q=+CCx;C1?8bRtPS0zhgo!nWgcMLI8)2dVHxocjWb~B9pBGcCE~C@y}jH zfZF!R8w6b5rKVI%2UI%sxQb?Y0wE4(q^oRWai0R+qxhj1P<AW4F(@=uhw$yZC#7fF zPuMTJ+OG%IU?oPsXT^-w<B^N!l0vmqBa?7r<3-!o$J_m(`NiYK=h?TF_r;gF2e(BH zFn_RddAdiaul3y8YXUUs%>Cl+onK$B(o-w-kwo-x_arE&sTZ?tPKjaRhG*6+oBk&Y zuc6w-e%aRmcrO>8s$P}$YQZ3Kp6A8Ko~LUb7$F`PTS3m6`;R{SpQ&(rbykH>BCoYs zYqU1yW>$ka*r)bJ?m}dqp5v6&N;AOS6@pBcvKQ-Wvj4XGc+Z0Oqay5e0CrMG5LRFU z3q|-fs}{F*K*awqJ;VbTB35BDCWziRH3oCI?^^m1*mXPm)I!P9H`lq$Jgm6STyj4% z&@2+}WubV+A`<pb{|M%{eruVF;E3XxTYF@JV~3t}TaKhO<};q+U*>Y3FJ`Z`x(!ui zStF1NWw(iCTwBw4j!XPZRh_>owwq5k<t}q*jWlF~iwHplRJcC(NRE#kvcrzitw~*G z`^>2YK-Hyo*+k7fW|M&isv2QX)@Jd=9#x9>gjaRe%HIUd!|J%>N^0|e-X3FKs#pn{ zPO#jV`YZ@FoxhU1Pc%%*u2~%MI`OhV3?A2BRVf2PvE0(0o3~py(u+57<w%Lu)M?UH zU_3cR+O^u$y<E`!IF<AwX_?Du)p!8sx=6|hQ15{$GY+OhsbM?LIJ-a5>!7%HJiph9 z=k9Kc=y|HK0LT&MZ-eR=Dt~jcmJJ#aWaO7|Ic}x*KH~cBNgsBcQqCh!d{-^K`5**f z%v_AUwR9(A1^c+*znB-I*x#$SF|v3wvKU&_Zccv<ZDIVn)Wv~T-FdU8rMipFJx9I? z%p1m9EgROTla5($UM(0VvsVXOag|+TwoJ^`e_^(6nync2sS}TWG)V=-x^(b=e=NU- z?*44RCI8t#I};GJ4zd=i)>8#jj~Md{@bKAd6drhr#bj0U5zke&g&nOp-e?Upu!7f| zr)`(647qtsQ0&E-FS~KYHNzy8!hlM;v88;i*~Za1L+ZX++yo#V-+1mP#KPVHb6-M{ zu0cRoD9<bMc^23`@f&Tj5vyMGr(>i2dK8KE0Np>Qzz)X*Z>?VORY?!yuP{!(#Ey$Q zo#3`c(!%gvve+*zCJ~4VO0Q`Iq6JStZIM`&qO2T;l~Mn2O<33uaiBG8$$)S8G;h#Q zcP_}*010kd1<Pc<@^+_)pL@SskR_V|!Ohy=n+P$M*6|xt**(~X4du{1Y~Jdc*!I@9 zq%0}%mr2Yg!BGTelblif(I)FI_;VtmyfzlsxaUIK6?&$Vsra|@!@jZXsgAV|9NQ&t z5DqK)Ai<{Gz3g?fu8G~jH(<;Vz8ljBLKqw^v8~#}FE}FJUGgwIG(r0z${1qLSg`x> z9lLoP`=^bvXDw(Ld`3J%Oz9ce4VE4W@+8guTLy-T5}ay_<+#A|lt>|{=vNkj3J~|i z!o5ORYs2y*LiQ+t_2}POTd;Odv#71mg21uO7RZ6|TCQD|%Mpg-1<pKsJ#gec>fwKb zk!AdYdNXOB+kr%$6P=f2A?f)*rYj^Efv7c!ZvHhe;|F^3P^<;}=2WrLG}B;uP}Z7M zp43$6+SjA<HTEdSYgd6BQ0BE@m3*=ZwnM*;5^^l~%PY(2;h_2UKmEhK0TxMg2hc`) zl*r$*VLwkeybUEu9N>1iN4@P&eg>znB;N>h%8AhS$dkZEG0C*``*@n<SM~vt+R*Cy z!zN|up0RyCFeEtWkg*6tERI4UM!&V0aGr#}wDw^^{)EJ-H7W1A)G#<i&lKh#8AquV zKRPnAO(T4h_|fY$eV2*-R>HsFpme9h0utnX&cu0}a@p<`odfdf9zyfbKA%VUznCfb zxfFa>VRlLBP0n_Z4C|}Wdv!qQe0r&AN@i4@8Qn5Ad*MHO=K{=Bkhm5!SM6CoLoM0& zka}Ecj&b(?>%hDD8b>VoXm?@o<^KuW{|h^lp7c!yeDX=VpXV}DeBx>UbqKbRm4iL2 zQDW~G_{6LNOrV_x;SX&8P}@}+ruo2v0GZ<2OsNi>dInZ2@^7;8VmJ}F&t?q-$1Ry# zSz9QQ?-9NIXiEzwN;4;tU!Q1SX-O_8OUS9>7Y;#_`_tVsemR0J79a1Au`3YvU}?b7 zwHBc9N$3Yi7htf{LewL-%jlb3V&o5xq?y!0O$>A52BeMVx+tlO;^*I-t1l=VWotY3 zR8TdPN?Rj_tv+6X21$)we=pXmjWEpA&})cF@y+ZM6ms1ENKuN+5g-Stj-jD4T|DIs zp`E6j!wqQb*>lN=)P@@d7_AmBdGD-T!gO6H^>aL`yEpn304U(I*)VgMnQ-(H`GVQ@ z7Ek@rfleODyZ|)MKDN5nfw+or>)X64zD7Mv(N1JezqrD}Q26R-RO|@rj=;KwO}9uE zG;s`>gPAAt){J<twi$~G+L|-#ve@E_AtN09+%;;|0m%EB>en8v6i02uX#O*fA1h)T zaq~j<rIZH6!TwkOj`_h(wR4-&a^_*6o`Q5Mu;7<7>sd86{Ej!Cd1;|cR~jRe)W~l2 z?~NIRie$3xheBomh82=ql``|g$!H7IMaYZ4A|7`r?cqmC3#hiR<I;WnEy4#^Kxz9X zGk|%Scnr9|KfjfffTG)17Q+p7+GepMQsQQd@A=CCIZ1I@iM=j747O*;L!brO791Y{ zB=(&#>|wTn`nppu*Hmc|ySt~A{cXh088dUqVju>8IBD#<I(o|eAAz$2Fz6%{mcpKO z3BR?h{aNiT3U&6J_tB|>S7EF0Wgz*Pfa4~oHo|Y)wQ#k~85=>zEzjKCOhsYQ%LLT8 zRdN<)pF3f$5hUHP#mu`kQ6M&Fvz?CxeA3tRRBct2I<EO`+FWxNzk&0uERko49+PmJ zoI8!lrM|KL(jDtm1Jo$!_k<16T|F~2mqJ#9H>nV9N^x`+@H=HIv2<5S%HjM(XWO5e z<iV!1vCY+N;-QD5T)<-wa<sMkh}uY#qcb;J-!^24u#2&m>Ys<TnVd;*kM&I|5F*h} z7L5PaXlc$x>r&h2pFae#UFxJrFmm-JTH@@NldqHuA}P7JKF1U34PlWN^CRB$f&ND@ zl~26j@HekRYRr+@nE0yKi2hfH*`wwtn(AIK5r|#+cI<1sf7Tp_bOWk<PvCXnFRo-? zM1g2hl?r;~U5@LXQ0cU`2!crrpow$^I_)s@`L}8oW$12*JNcXeY6`w^A`$W}gosR< zB*tJ7$?;lDZOj#V^R9EZpsqdP#D|86v2PJM2+hV%No|v4e=62yk_4r1Z7--pP14O0 z_{BLGy{v2^7mG%HB`Co@4qP-z!%71%hthvAjaaaH)rxIcBxq20lWW#e_-e@a=$1wY z43w=gD>#&+m({U+w(``vEEpbAKE2*Q{zpK`r9p|4`Gg+Rp;IyLpzu?}6=6^l(MuIz zbA1)DqI(-nOYtE8L-(Jojr1jbMxAMlPx$^n^{_wT`%?|<M*{y)J$y~rcpL05Lpr#) zx(?iU(Kt8&CRt2!z#<@Q3qv9$-OU}ByhT1Eqe<R)o~rvGrv%6Q-|VXToOgwAb8Di@ zbF+cZ`p@oHI1-(gE>54IwBMxOhDRPgwKFfT+v&0IFO`)LEk-=SZfp#7xFc#&q3Bn5 zCdoiHCyHPvDl`840fsPQF&JeAh0wfyjPNEz4P;YQxlqL*6$&A0!3BmK%=-oM3tuJd z9x6nGl#zvGPs|zBJ@_7qSPL~@?qKz-_z@R|9F1#k90QGO6dYDM&5l1(76})kTdqEw z_QUMhmVPN_d_D=@0(M0>s+lP^d?`6Z02d&#FuC9x?VYr6hSQX(>#SodV;Me|B=eSr z0q2KnSg+jQWy{6ZI=@oOHgE6O`l$GP3HkWY)}dQFq(}F=jcH3&UOUE&d1B#ou7o*q z$<}%A&4WYqDX-AV^C&^<%;mCNd{FnJqo`X}x?;VLbAi-KYLWfcZxT{cx3j2Z!!qCl zy&u)s72wcf$3dmTHq0vhLII|E7v@(JsUrHO2`zH}cvDos<lygJ<bQ|OFXwGJLV1EE zRgIaV9+ReJl}Pb_6S3yn&hh8M+!&2WzmhbbOu7pl3*nzgiGqDi7Rs~w?{|MBiSF6T z6Hc~3NOTAa+YP0EZVJthS;0sS{CEPg(+QhN2+I(yfF(NGV0~=uf3m;*RKw?@Rh9<P z5;iKyy+4l%w9>)C5MC~qLoSV}`JV0y?zOp-Us~l^&zilNc#ne=hxB1s-$)bx)Jsih zc_X$HN+h`ZX6=`>x|$DM#bP&wy{zNOI)|KTsttyUIQ=zMu8BWRk07MSDA5F#TTw^R zH*AhQYxZiw$+we{HYbkhZ_p<w8HV6lm}9hd`(bb(rW6sG%u}VhXj-+SV8ppM<1-%M z+MF_Sm7sV$o<9#ZT-gzZ=yJ6GFRsohI+Lhr)HAVd+qP}nwv#v3<ek_yC$?>KV%xTz zOisS<T%7a&ebaT>Yp=DtyJ}ZGPd#_tTVn;U!*mkzbX@3bF%DE@?w(BG%Zj@CZ9bR# zrCLk!4VNu%peZ(|@gI_6_WQLIV4CVJitaQP_H?n5)25!87jW2l61t%EfL(lqKW3jj zb0#+%uqRPN{pNOmJp+NfTnj#c({E*I6Pz;8%;`Y(HrZH_hpJCAW#frmQ~FXIqIsJU zLd8L&r7b2(B3Mr81->bwB)2wgm7J4g{0+m5V?o(_bOX2dsLY1)lO1RQ-r|GXVh-@Z zZx7W5$a`!G4bDHa+4Wn%JBEcXDfXD+Yz^w-#6WvH?&HH>@A1Q5kMF;~2n`_KM1&CT z@pl(s-U$zI&t@Pq%Ky<*Q85x0*6v?dO8VSUSe=61i;qW@&<oR{@*upQ=Wi?bs>#-a z3V!B#Z%n^Rmuj0QI7XxfhE2HSyswR#&rjAMN!E{1AkB3}6HCg04}=~pv7v2u3NIU9 z-&Sv?+~{*$3(Uftgb#vb`VFs>ufdGFPwvlejuk#9bnU;dv10RyXD~g%txH9u!11iS zLto~9G3>-mvDvi#@~fc8@{Na34e8@hCv^Do1aY>JJvg-JHj3s3_IU*-J)frK5D?L* z)pzDCoa^c3>^!5dy_~ki7`6!~jb4TZAXLjB?Z$En^lbGKGInqtAMR*<lXq7BK3cZo z9BKlfM?zw_sqo`rsZLu1dT;OoFqOP>Ab<Kvfi}5<RUWy(AMmz*XX*P21vOFcw?B3! z2_6Dgr`m>Rx5E#B1FL<#g#8NJZO(*$`8q6l{le$QsZ^by%lpM&;TCr20)AGLYHrih z9n_jZsB0=Q>)~dI9yaxgPR3Ud=Bmr-`E%e|Z*A~}mXB+x1O!^jB(3TKufy~{*~=Mo zJNHRa{&rGetgOySSA|Ta(3WCfS*43!?xg<+{@Q#s7C6lYmL9o~7FMyLcDUy^hx<EP z*88L_q2k4|WGl<W6yQ?U;m-4U#q39(-2PaLP?2feZCDv9oYy*<1W9luXlz*6ln+yw z8z;CZ(nIvfRI0N}RcVtg>wy+;^H={8?usy&x*7|3<zD-R^=lzX=3lVJkV6gRPQ)au z7I8y6_5$-Y@NHH1E5gk{WWVqG!A+~KZ-Y&?a^b3>2ohHppP>^(3d2Bn;l?|%z#nE; z<nVs~+K2{xG3UTB4NKM~+%J0Korg<ehNRzOrtK>q0VYIq1IZmoVBT-MV(GeGJAGyq z>CkNGOD6s8wq&Wkf+9lhGObj#{W<tV7!6`%NKKJ}{e?-R+-lN;*_d^7EL6ddr2qul zdp&Q`)E7aB_K(4@ayYXh4sW8@{BiUT2%Jkh29s>y!T~<KD>Tk?!Z7Zg72hb=&rGi( ztb#xuGb%s?O9?gm5brZuJeNFJjz#lyZU3~$2)34|z;vV)26~weO{`3WCxWLYW$-5! z84V&($uz9tcc>!$!dWjdVXhlZqke?Z0~dms*NiNDA2(TxHj<Y2mgzVLqVDY@&1d(p z?T7a)I>O;+s8d|i(R%hH-qc#w0aeC1Sl2ii#v5tsEFIRe%Nz+))38=#azo3%wp!cH zaEwlI7sm$2&P0#Z{svL53vR<yH-=O7`~wQ02u;ie5iOcCHEv*iLcR$@@q-84h#zhI z1FWVj+0fK^0~O&(=aw7&UNGkQMDTJttYhvui3W40w%QA(utT4hgwD|{-UqvnDeY-T zW-tN<x_8KwCG{)m@Uto1kmAZupv)K|_N$kB(;wAik|w`CSYQ8C=a_}=q>``CRXtE3 zAH80X7RNbCb?nr6#=jU>d0{jDzYff`zayGxa@4|8@=Ye;!5s4^Myxqkm1L?+Ujshr zWCJK_z_UcV;FjM=|Mi$Lrl%G}0PVM0lTx*YdE7mFyw9bPk=mT~j{jYU4O~+{LN4sY zCcU8|zl0foa{oC*@!pV3xK~}ujCRssnf68Wa!m-)D0OBJU`Drx^JNFxlD)J-iU+hv zaZyPuu>>Xjum7C}t@<*s{{n{;T7Ugts?Y)zEK>G&O7#ers^|`am8zfuj+oF11Ju|3 z&Z7iGiSm$O#AR${?caihk{i|QP$EJYapeLN!jjL1%vi~EQ_}ew8hkwu5$}d+e|65h z|Dz(Do?rl=x{uxQ8Cy)}+~+)Zx*T0}0KeXFf28ajBV&bV4k%&!<G*l#ZWsB9zcXD} z<YxHmKy*{a8Pv5^X+Req9X&(A0jnb+NSO?pxhp&+-6(7uuX4@n>FCZbl&#N?d$~$= znq`S}_p-*^LfaVzMkLLNGreqJb4o@ey=0|QYKSf<1e=R?B%>-q6=AU~no8~R)5ngm z119ZpAyS;sf7Z(zncXj#*ar(aK-?@su~wVA=fWAfV)Ms0>buH2fJ`6%1v;KGPx2;l z-VbY}T~q8gTZv{J_;f+Rt#)1#Uo^o&ffaYy3flfz7Ti4VRH;DMVBV8ibZ{=`3s<Po zv>7Mv$$rzdDC<81FZZ(}>HfArbEcQqsy-&P)Bkc3-=YG@ve4Hlmx>uxG6XrJ|7{$= zPx6{DFW16UQAbyQE2R%r24Xjx{gSVcS?iQG;+|OGd@{#wJ#7wWit*7aDQzm`x()V4 zo|>(DmPfS;-wZf3hz;ts^W)xJMcJJiAu6u5hn}Dy=Zq<7ypmR1=BZrkA@UE8GY94E z433VH96q2e*2&Mi7Lnc#riDx(U=|EXdI0BYT5Owc4c999Yg5JD59Apn$e~TI%?pX; zK6zVRi^30C2kZC{l8SU&ukSnuO!R85jHMh286AlXq=4HqX|Hf_*XRV8CTjm}L1{(a zkMVUy*XkaGJKkyjYOiz0=!6PJN{eV+ey55(hA@RJDuY3kU8{fmsrVR`DUKf<VedSS z%)pUXV+`Rp+Hd>941^_RlG#xZUl8A?J8lj-W^;B+8PL7X^-zdygS)WYh5rcJ`<yNc z!eM707>iKic3&GtwT#w_lx~*V5f0!EbAD)sYu3=rtwIb?r8J}gbdKgA6nfL3Xob0N z+%8P{3u^L`CiQl8yVi0*lGkKtTus~`wk85mneC&8RGSlPfGnFIu?5&y-g>K&m0{S0 z7A@38LfDf04A?{00c|Bi{}PX#dqEC}0txx-&_8bUM+gbn1k3@cBgs2FjW>{S+W^)d zrZ0k3(PmiHdjjXYy<B<&=!n=`qKw~$_J1mCOSoJXASJWKpdUhAnf(_pUA><~)tomT zs9T6>e9&oyKuXAeK?{QXVW`A3rwGZqPQ1A)!zKRJCu&sQG=r3zyNMy#t%28qP{h-{ z`WuIGPd=?3h=?G5gpeO9iK0D^aUJL+sb_IME6A^PUNJML4pecjAcSrpjLQdpg%l0Y z1(u`*f<bLW-md}qyC=a~;>Yq!;B-9DQzmdzlKxwfz+%z&eZI&(FAzoesfH<TgX+I3 zg*1Q2q6}i9{-H6-B;aV6+Gqt#%P$>61D4))e2w?mry#1!w6f>}6Yu#&xWEGoJZM{S zdp{$C-s~jL8Zv6DW#se?A>4j}5B(?!I9H;Zs_xV7#a@VRfH?ozkjYg7!L#D6VNcen z?h}vhz}LSN3^R{B`_7E+XMdyyV}SF8Vi%Z`yS=lJ`GbEFddlPenecxWhmv&{V{LGL z{BROU?CzxpHt|4vs)^63XPbGmc*Tv8LPyC6L8V2CAt8b0OGB~5flUx$DFM>p#(K>t zrhbZbUcV25-nyf^-TojCz3tV#)zRD(^>EX7^Kf(1^w=CXeXZE?aZzNM{n8asnU{3? zzVtUgpFW@KD1RC?dya!R?n7hnAuA@d4ufcWIx_&-3RL%W!WqIZQ8rPQr7w1l6v7F@ z&&eI)PLfy1XDMeH>vZ?1Q9w}wQADG#Qd;AIoYEU~b&`8!C@oTa@_VN!({Y}~gAHUk z^tX87$1;41d#@;rah`dDkz@vRw<zJMGM#dJhbT31o<)NdWCrxNIN{4O8#K2(;mfj} z!h67Iloshu={@&wPT5ZJy($#PM9+>vUh*ySTS}B`3xlip)&Y>nTd?X4+FJqGv>QKI zb-#>IRwqja=v{w4UnD}6pH7IW-t;S>pL#fPFKO72*tY;y0pnrH$(JPT=la1MiyOE= zOL76rzT;c)a7LZpU`Fj5^NkVJPLsaxToRb`>Q>!Nd~1oJRdatw5%`DIGqP`;hUgy+ zV)uw4tEc9k4+hkpW7_663+T1(Hz6u}5RtwX8#ui~8cwKd1nYP}`2v}<6RY=ziujs@ z{WcK*9G)ikGal~IGGhFf{LPVKBIq%>=mDHww=rS%Sll2&d`$$C-WtOFo7@qE>jsVm zzrN51USs)A@ASYup(6GEz=q1Ftar`}>H~vF#7<(SvQ!!~Y9CD?SqJe5%hD^wpn&~p zjcgGwdyaw8Fq$Y**W1-dXN6I4_l-9=n6sh@|KQ`((85Mb3%0>9wGz))0w*5Uxlv-C zBC*^1L5+~KM+Ey2ev4s;P7X)Glnor2G$GMMI7+98R}$|=OPtJ4Dw89YM^bG&MrxpH z=oL_A5Dx}7KubnBFH9RAvT>*}#tF?Ba&1U6Mh*>6w_uJu99yTckY&afu6y^r5(-?# zhHDRmdWDzOO=($Tr!#D*Yy#wcdV*C-VlD1GgNrH{S_RMJx?9GZACVqF@B_OMYIqNx ziRB#bAbD)E2zqLU+v+NY4-s3xwSbpb65A4rz1Qc??;<Y9Fag<9zdQ6ZE61+wJRp%Q zim*>jz!-^Vz}VezJMhK^!ZdQmjmlP6<?T4DJRDq?f<y@%;n|gd)dTapE+PFtIs5kp zx4gU;&&kf*4M#VH!BE?X1c2+GnKmX?vdh&T9U?BrsK${E>!|3d?L09lOEOsE)Y11e zo(ELfcCI&AmP9)jt7uN{9wV2%TF(}#9lT<dWY1AL*LOC_7?!Ecnt*ZVR_)$IRNXzy zGbnJWj5V0y6W<eCXHNw;TaqmP#<-|D>%rlJO0qaIo&65bD~*=e6`&pbyGRa$hYC=- zMGy-Ip^t5P|32u5)GRr<MrhQ5NPiF9$M-75X<5g^sLiD+RcC>sXZ%!u7tXWQfsN!- zUS=x*189{ecwy@pC6&rGjo{|(iORcS1~L`bv6%;LaT`F+rb(1Es0!HA<KK*bEjCNq zIP<pu!Y3}V1vR$X2NrVS?Fwo3%A>CT!VTweNjAcjOeHUKdxeT3pOu4I_VQI=On0Q( zaShHdZ_Kw7<NLaKy=$YKGLYsmFKZm1*OKS31--U{;aJskT@Eu^m4Z6M;9Npouz>~j z8weK@)747p7kQ-#!qeF-D@iDk88F&Kaw$~WbEn|oad;Yn2g)B{%>kyus>9TIOJ}4r z=5`SlhdR>h?^-`??!Z=8##L~Ri}uA>mGh6KTm=fs%80juG05WPv#nH}*T1ISbe2Q- zx7_EjJ2+}0n>$MA0Ii{65K^YL7g4BCE(bGkro?46f-|U`xW<XcvyzoA?Hhd=<ZKI< zO_DfA)iNbLK)zp^EvYNiPleeac}I|UV*hLcIoL%O5qD$r)jE9aTMM-B9i%ei#P8!Z zzd_O<1A}`W4IJXo@R4%Siigz70|)F;CKpIzqm_wb;%l5D*tgBL59|A)%XOqjnykgh z!A$@@`Rtlah4_K}hlKCB(vgdB8a;=FC8R>y03*N<klFH9j9Q9WvR_gKudka-V!0Og z?wFWF6)h@VMXcfS5=xS+<2qhDBAYa4tVSKFIK~c1dUalb<vGY3BOZ2kpL`8jWWe){ z(56rDH!yR2dl4U@D8wV-dT^mO<-&8q)<?wQTjNF2HNQ{k9!_H4y1vTJnfJ@uS84j3 zLhu?Ei2c1P#Se@5oTr%tkx%f<@?1jeU)~u>y>-TGhi(KO9eIca?O6ndc=hayn7SZu zJea&=QFC6|?lOmA0D3E%@-2l@mnOD>nYrK~47eh!?;$!-?E~Uw(CR4y{KTRI7e|}N zchM|Zh!alXzp9crc=ft$-0vzks;^MWbEuWT*<;2-<J0=Vyb$ZewO{8SE?x%IHbq2S zh`WRF6r_SI!s?w<)lHXEDhV2=d8hZGY^2(e0MJkS!YFQe!Ej*CxNTt>MyMd3QyZo? z7a^S-&#T(K5qNh{O(B;G@_ilFZY?3;j2LB)3fvF0r|45*P0kqzjmCYq(9|+7p}Y(% zOse*-*>37vl=imAq5{uexP5#A&p?I<IkA7h?lM*N*4^x@<t*&I-X&#u8N6y$RgCV6 zv4uhnkzi&jw&=BbXxCmDAX$3<G-#Q(RLbImBk>BD(1LFig;+|;tHXF1NV>xHEE3(P zFYH3Zwn_|%Ph(6<Z=mR@A!W5}?->P7q!-K+&dj2{fI6N$fNdOr+UiMZZDHRy?#;)5 z01y1t2PETooP&NAFJM8vfR1&R<l?TU(^ac=5+KZn!o?0|O26V&xQJWGg}x`%>dI14 zSPCswHPC@#h|6Xe@G7Jr#Fkg64zqK<^(RRaq+?Sjw9WkV!fu0Q$+KdcY~=!)X0k}R zA3U(3j<#NgKq@ppsPV1I9ZQ)FEhQkAtzw@|v~s^4ib*sU#UD6$vfWJz{Q9|wjl3Ej zt2dJ&Zn+?X5K}!9hEY8-8&zCTuo${rWlF6c1D8G(MH*LDK#Aq-VXih@Dk+kd{4=pW zrBQ;}=LJBpE%626A)lDy%GLoCopOtJIm|+aUr3d=ICT!mmz>M<D35F|Dc+hF&Rd&5 ze24*Kw-GuVD7c%tUyS#0v1-j;bkcowj(%C1t{1&dPU*I;Gt;Um&0~M*GN?zDKM~E0 z2q|Cn9u=&-k11|uJ_rnzTw;*;&Of0PtP2<iGm}g|rttj4BDay<-Ee@fGFb-ZMj;B_ z1KedNXGk_7k~)}A)*@BfR%mE1L4hv9aKY1YY=1z20g8#(vL0<UVFlvZ0f|oaBxmA* zBp6{5)EG2-Sao3ta*w`J(8}&YM>D66h)0=0n=}~5nLBtDTKQoVvz1{Ob!rjj5nLLL zH2uUWswCM4=rSsD5|qG>(No*!_P_`(1*IziKP|eyR-q@B4JG%sGLC`s=Wd={@o6j{ zA4T$hUm;E{BpMO>=(EE{=)U6GPQ|#^)EcyVU;vZ)j%BcF>D{!T0@`eBx948Sz{EnW zg0RG3)xLpRt%#-wr(Hhqt#9~kR^+`(bLxYJzo06kZ{H3h+&>@;p_aH$H<|-2K}ncf zz9@2be?i#e9#xRF;0a4w`dThj&525oVr5)^O_&1!Cft2=oSpzhNi432ox5NvSs^t5 zjgjuV7}^)n5-wxXaH2d|W)g{3LcQl&N4DxB##o-ht&f|9Kih$KClP!)A%Sce#)=L| z;nGDu4s$`z3I{fz0SJ-@U-v-7^+tC{ATT3k(E!we<5+rAJ1~GEl4=tt_CQ-Ky>TPj zz;i6U=^Y4w>0PxXcC=IL1U^)Pm&;OHR?^hS+*)y1=AgUy23@l>Ad<uZy@h(u9RRtC zckRw@tLsL#j+J=ujZdc1`duCn3^tg(#~}V8wow)40~|yE1BkF^`$_+Jj(8t6y5@}h zLt{djc@DZKOiiFNIAEd~WV6RES3i&U1`~u?r;8jLCW&4r`G9la+HD#NIZOG5&P+tW zx|E#ek~E{Y$fwCZsh;hNQ>HOUbVR!cP;t5q(yF}27+8L8<GE~9tjC-4>?wqW7w02) zm#*Wi0xlJAq~9d`v6z|4IZF>BCwEXc=_eU#i11Y4Q%uQn-q6_5_lz}!LKW3iUXxt{ z<D)N+Qivh4X&>cbcLSU<6eC%{?NUW$a$e<vDyD8*zOVv+SJn_YGh#kNl@xEFRWlhd z>}i_P%6k0^H_*$>y1aBVU?gQPilkV3m)#911s)9>s>Et%dn*SqaY#^<Zi+9RXeO93 z{c|%LG`}r~qMd!Is0b2vq2B|)B?)h~8x;<3=~QLq^Alg4AU=G)8<OcqRggKMoTy~b zNai*l$cOI=kRpq|W;v+1te`$vb}KKWgO8^=((8^{riBVkfe%gBVA5`!m1eX<=AJ`{ z1)d^=Qk_3Cp4@VO%XJ}2DiUi_#^?BA<3h?HIJU1P<6p@aQ7@vp8L+O2l25an0VJ~B zGqpj#t|m7JlQYxwk*4hyCK3ttLn*m6*V_p1p}`qlMIuPcDq7AfIJP)oOlk;XoIG0G zZL)X{))8Zu>e%kYPw8S9D+an5fE||Ez^AzC;}z>C`bmZAooQPGlM45`E*p}Gk6mdU z(9&loJ$Mgm1Ci=`NF3`->K+AK2*BvtKHO>Oky^|vAYXBRAr17?ZFiInquU1RE9DA* z^CBNLbAG_Nxj3g)ZYtLyT9xIMdf(i1^6SYzB_&666~01!5$T>xp<(p0C|7|NsGOCD zWRJZHe~4xlO6nJPz@6;w@nIxQES~CTtgYi{1F+F(;X|Uxx?qXEP9RQrpbCYof%WOy z8aysM^y}zL6ZkWB{^WM^DWjp?mfceZQv;7<Ew7x;PtRzP;V^l(w2w1p$I>~9jam3H zGZBcf#LkTa4^n}nn>uIgWd$M)e1@?j=X0=@El}3kS`qm^V2rV~uIF4&<z&dU9~EM{ zk}<Ak_7*k`>}sn%t;G(1TxF$R7+EZtP8&EIpQo4gw4C<DM~+_&D54GD=VRbtTg$wl z9}zp4Yjm})m~AMnw$9@iD7BI&#H`?~-bp?_r**WpvvTD+(^)6Moo}uL#tTB@^i?tS zbR}EY6fIp9ZU`JAGZ#;djr_<rf%RxfY>DCfP{G0AW))NT75-f8ObQ%sNvcoY6T^?} zvNw;9j3fug-f(XBw0jb}AKnY<B^;yF&g04M!?<(~Zv>(PK?>DT?3rX{QJg${#%!%} zJf?19Jw+`gFmCHtBvnm;O|m`dgzJYJJ4=q=8UeP$s4U2lI87@z$fne;b|bQJT0$OB zS+4?u#Nza<Z;FLZ@YBWwkJ41^LJ_qgY@xzHc|MT2xMc0a965kgA5G?KC+o`EJ8a~T z?<u@c{*nK7n}zJ^9ZG>WE5aR;>Jpm*+%WH#(0l>DIEtX?oS+i0&I*}ibX`<{`0AJ8 zIDS2kXO{R^4s{{_&x|q?DbDEBnB#a02HmFi9BnHF%Hf5_D5&t$Xhm^7rRB}d-uII9 z;M?#)0Gc+6!By<kpp*<qyjtoWx_BqemXZ@U0p-y2%gR5BLk`lmf~oU}MY6g0r9wkX z;!d0zkO4j4BIONW9>ksgdhGc^37(&wmyP2AN^DZOs~P4s=f7)B{MTV)&*_X#knlh! zCbog~A51WIH?S|{L?010Ab#?X9=rTB6(4QCpWX$0g+6=c-vkSLwg)6<-$ovurByx= zr5^<;7Qev$NSyx>{%J`5eN+96EPOLeI0MR;>O%WL8!7|Gw|~@khv1Fo$2`-#{dgB< zQ->4L&^0GCyY|n5xkoj!8CNMPpx0&RU2|U0lgyNE;#w=rI9>sj309wFmS7&X8XmQT zqcuh2TAL_8vuem#U<NDFk(#N*K%P*Kgw|Q81G`@Ui2c=wuTtgtO#`W+RdU)Cn0Dn^ ztlRHO$+QU!mwPsfz9NghGK+R7lkkX;@F<sfMhb|~k#I`VQE+P15oR*1BC}GjfWj!9 zhbp&W?_;)s?-O}c3J^bY4H!Cu3ou<AfQnJIL}@H*fFj79qVUZ1=IRSv;07D1kVK+W zwuM$uI!4JVog($D^(O0+KJyJoTr7Y}DVIde%3lH^XO%CZ@`>3KX%#aNX;pKeR4QSj zRI0@iYZWn&_^()NVzfN=CGgLv-BPiYO?J(Qfcps2;r})V(QrO@;A$q+Aq0i&{XvI0 zgE#`;3-i;**c?up-|xp7$)P&na1n5by7!k-Vx0TI@;o>(D!&VJ2}_(@gKviFseg=@ zsHF{9PPin%4pq(Ja31*N=cE}|UUeQC!#|^~yb7CRp|jzwvk}6-6OG<sB5qiDQd8~; z*Y5SJ1d1p@;O!nkh)Cx&LhWPrFN4aX$Sx9XFZAk-2TNe45lx@AS|+>&7aq(mRQY0` zb}bx3=%FXAUf6RV`3ss}Ow9oL3x;0!wdyO-v=yQ!$o7_Zg=WGjn=0shNNk0HW><q5 z*8$bdslzIOHwg49Z}kd=cGm#_vO&2tpnx0_I*8$^rT3#_aIsc*11>fU|EAOvEC<EM zxW*Gc2jjY*^@bgw@pF}p1M=U`FEUf|{UD$<!!X@e0I-1>wJ32{Pz~h<z6o=_X6qSf z-+1j}^B(gy{Ch=l0t$K(r5iG7XM))O1M>+L#Oudgid_zv1iXmSIyuX~nSq74qOreX zqdyp}e9_OtAUM_X8Qke70`qqP<1NURfly%}4PhvXY=8W`I*V8`OHM;?1~OzLChHj; zFl1dZU@b`Nq9|xAQFG>V8OkQ3+J;$xOLL5|v@<C}M*zgp3;ek3CRJRF%!A2RX_)*( zv|R9el$3sYJrm-i-=l&&_2tTJ*H#nD_*8i26PY8!D%?MC!7t==mV5xLN8*+fqO^>W zVd%Lwbl<awUInYgZ^o+izXfKgPCwG&Wq4Fg!xW5lc%Il?yRlLfkTGywZQyu-b}QLq zV%;&M35h~xbjaAD9HJruXOnr`{lY678rtmLh~730_N!e^<@CG~E^ML1b6DWu;R3P+ z{HlbQ=-R{tzaeez(f5S83{b6zQ+B{`Dt=>A?)5b;V))^)yI!&Mj}>nN0&9c$My)Sm z6>3T^LRoc6U5^^maxm}ptSD%Jwqc=(!s3)@1+*rya-gz+h~W_~(xA+~2-RDp3{h~@ z6y>z3h_!;4M`q*GUw?!(A|O%|W6Q#BB1YzFg4eP`Y8<C>495bzo!u3d>{R2+sE!r1 zti~BwCQFk!R$(opYlv{m+1Cpv@sywZxOC6YLS1ZJD(u?Myt<tH81-_2?>F~yt)^c> ziv!A$X)tYU07!ao{&0IxISIxEBg_d&LArhitAb{Btc9RjD6&&gaE*lQv@(s~>V-XR zOxPB-&7|p%rD0iIlSs<A8i__=^!DUSR=SVT#46c1_F2NB%vN&qgT=8Q6HcbA#6E&( z{z;ytgQqB#M4xf(ak8|)WTX5q$$h{4M10ZQ<RTcoo6M@}&6nT1uTQwUMP>QUoO)@S zkeGnoJ-h;^@G&Yqn&^IWivp)M_BoGW_BpR2_Bo$q_GK!4`zEX++e$BqQ=JNvq-GsS z?4t9^g6O%&CEo(qz(t!RheiynB72QRs!cO|)1+oy*yy>*C6WT5Yh=?3)us);ZIVMn z4696wuDx1$)aq|U1hP6i61NqRbsrqO-zAAMSwGc1DJJ?GcBuGpFFX;rZuGd2rvnlA z2o?HZjbII+j`zjU$QeWIJ)t*(Q~N<Z!5?;$Z`cgLYjzfI5DY*Gcbaaf9fEuI-ER1| zkd85)B}wo;yZ;~oRUc9i3z*I6J|_pYLjrkXo8#;o3T=?CtVoxgj|o~UY<w9%3Y~og zH}u(mJe&3X)t^N#kBVL&>vt?h-vZgEUK*k6Bs;T~fnz8QYPfZZ9zn?pl*Br#@aakb zo3bI!{PX2DRWCpmtZ7xv3wLOv;n4`!-=)?W8{M9dc}4ge_$NWx<}f?{?guo2jf)*O z=nYXnht@<<Xq+>bYSowp$vnk=$sdeNH9`%K$NsC49pv8zo{dX-LpiyaPbZOEKfNv} zslIgDOuw##Pi!kPmLNZyxTS)q-VhctOsEvawF=573$IRsM<9&ixcAQX>&nA7$5GLa zbHrr&b4Uy(fgBBe=j3{Go}0Rvvfan02A)S0T;`m7w&VD}kAs$s>$DAm0vC?UjOw++ zoEST6F%2$)Q*G|ue>crpFe@2WPd21O7#n6zvOqC5o3D;K8Jq@cHWlktm9fY?n|IY< z8m!j|8cAhws~WcGNF%z2D?P&7YFFCSr@!x7BF=}<1N|p!gM(Vpn+K{Ma8oH%NUVn6 zsdcUkmF|DY4ML9L5d@I8!s-AxD@P=aSf3K0`Cv-+3AixdcR+Zg^yMvwt6sqDMoM-y zd{G=mx||RNMEAT>#GdgwL#7ATUr;_^pNaQqlVCd~0MBlXySQ4>jtL}P(UiNqCq>`V z78KAIAcI5S_8$3*fWrVHThS%Cn?rD!jyIVxf7CE3KmdE<6fO75Us8D;C9UAq0(FDH zdO!w7OwttpnDAiz+`1C{xL>X-8qSE7!^$sPjnf1{ZEj8>VUR;Fw<z1olb?g+5B|3m zCo2S2+Cp=lCZ<muO{!&&s&U^W<DU2hmm$yKTjXX=!!fJXU?Fs0rg*H7mmo0EGK<|^ zgzefsDW+Lc$0oDylHbR?3pcv1=+z;uXy5eZL(Y(hCOwwNrcx_)8kf}}b@B;uz?p<K zu%bqGlDJY%Ja(O@LHNYa24KASL_mXP{K7>jm<?IwtqfoUMwwaiI<?DM8<U4_SvL0p z%>?q)B=%}|<6eAKtdnRLIoSt&1!~obJu5-A_!<}6W+3-Zr8E%&leWHf7QQN~0pV}1 z%F}LW)iZN43{`{s(Kpim)tzxc>loZGs-<Y&I6P33X7qwUu&q65W`@3kK`>2gTRr&e znQJnV2Ji>qyBK;ik}7QO&ugZ`h~+`f>7jEYQ3MLe*;f6B7F@zU$)-8p7ub&?s{8@z z+y8YguQ0(x3jGKC#}8Y$|F#zvsDS^cz3@^MR~+k0AEiEZKbM}qvBZ8TW$s6EX;Tw? zSfdgcctjz6%2F!1c0!;N<~0Xed9%*8p#ax@l&Pibu%quHc2Ut3m)}$7SM2=T1-O4) zBhwUDV#oTgC$CTbp3RFt&)1&_J7nD~wTQpqJ|RR-D#THaZEisz7S72-gz-gz5LgBr zH!vaauEvAC#!=<VUZAF)>fcbDLRUSd2u$43%R?PkQ<jjf)_e&~Yy(SA(SfUg^KroL z4L9>J|L!QV!Az~GXUO!unDW{y7<4<^po;s_Zv6=~Jmp`_4%vA=kNWVW2Rq7jb?Q@M z!dZv)I`i0&{abhf6%lv%4g(HgpZ&7*&1;+3+M}2V14hsvtlT`t4vKXK>#&D5>^$cN zd}f`jOq4Bffn-YQf!fTYSJ{n*MPGF+fK=wLJWe!(`%&CJR1M?wFT)p{7(3(!ac$+f zN58sS8x9$wGAr~UYonM}4IK{aBo&GxYq~B6?0D`>62fwnwxG{6TV55QrvSGd-CO@T z=DeD%1%0dG7IrWmIhGh!WAI3qtAn!Wy@r*F1~#+$L=0G~RbtXVJVvxq6nObVt%TL@ zZpxNGO?Dc-3A$37(R6oE2Rw$FiU`so_BwHtFMdCLnY#zNI&P}eU3!QqrlGhcC(`cd zgMTFxAV=4KVba&c29`X5MI+5N3v4P5v$Bytb=?3S+_OB%-Ql34N^y@kkZpfRspO#K zGDk@)`-h`KvNJbPeNZSY;w{rycYE2Dd)F!WUMR>cup%gq0F#3tVd+)o=#bQ0e2NE( z(|468F8GGdk|^UI@g^lI7i2>U2DQBMdLz6^ecH>(-4C9|Vxw8W%v%q!+v=ESxecM3 z@J!F`px_ZX-`~28sLg9u=H%IbGLWsM)ixkz!<o#JW}I$jHOwLUu-9t^2=nz?UHRd6 zThCe?YM?Xr><5w~&IZQ-KsCD2UJI3Mk3KU+Us7lpa%%hav9RF#dDmLC{mHnHn6oZy zMxMDGRLhf@2eK+)&OKjpN*`z+cunb4lV|Uoj!9Yn(ZwFp?5S%fOKh`!P8;)`noUwj z!R@8aVq9_rUX2=J@42RB&Klo-zsLQ3z~2>F3e2)Nb%>`9=ZOMV58>ddAnW?Ge{=KI zq-~cknYEuS7H84Iolk{=#^W2JI84?%qP}90=7Y~JeESB#?}5x&Zh;C8sSMde^8pQL z%!r`5GMe~`UBh6U-O7AN+eLTvFf0KL-Ah(@`g7Z6*6XQ=MI9Mwgd4V5*Z5u7w{=4I z%~cZSe;v`x8qqTAo^ibCoj1BN8S2%Dh^%TrCG~|GzV<YA%wK$e6lXOG%DW3;`<k5# zqW@X(^GSOED}TA}=i^t$kh}>fG$1|A7tAXA#C0pq+!;a@q-gkA^?+V-1~F_4b77xq z35$u=50`5TM~AGpR!b<u1Amhj5Yw*ZGLk-#YMDXD-{@nFRGzTdBu<!&Go>n*Jh;+& z?=YMt)*;9m_QfEzG1KdZrx<B_4^*48ZG<?S$}-UbKMCAHr3WIkn8&sHa+oMm2sUh5 zNP6;bEoGPjEW9a|6|X5WE=BRtmxRq9mAFCGlp@%wm3HU3z*4L8{^Wyi??82n({_(2 z>-?<3F60%#sdA;1&r3Z3oS1pQ*t12%Cx^S^U?1>GD&i@FBgX7f6JfcL2qTrx&U=O! zsTEiP0a?E*2bMi!dSiCLq}NY%Bis^9lIi{!Ht>n3kbm1WJu(VGjSy$Ts0Iu|$V5|z zXC_bDqkCrid%c_9xu%@bLLMpzjbwn7Hh&++6w7A$(N4w76;<seGU`>+IYN0jTB<)W z*;INmMatU*;}y7s)}S7E!vsb^4l-n(Y`NG(AXlt}kJzCWpXUgM(}^CmLGaD6@+n-E zQB7XJ{-D-kFvROo7C?I&y#Igph5rnxC|57GU48eI-$SJ4N`3c~8>oV#Bv$QV0?!q` zO*$iA^RU77hZGp-=#1*)&Z{&5VX0C`ritay;wW-V=?;=wc9!QCDK{|PVSbBZ0keaU zH+fN(#qdgGDG%cl7ppFQ$5~(Lo7+IY4-lqMNd6Q5Y%vtnH@31=>Ml9T9kSVAJ(Lyz zrGE$X1PhsGTcsEhlNn6+nrrJZP}Z{K>?Y&blLhSz-$Q(dj-!)+Y4C4k6Hef-lgY8o z$7ClP5pV+}m{XXNo%?z!L2|>0(|a={XBONF6D=#$Y{sR)j5u?H@-Sanx>@hwjoT!~ z3Xu@FdwV6>Za8$0fy04ZlTlIO-0IMrhyD>b!iJL5&j)-Dow*mB(~g5wAh;;7FLBSj zO9#b9y`q;%?MB}~d;jnJw7e<Z#+{Y>FIo{R3)DfQ!y+`Bvi?A!nfFyx8Xl-wa@DkV zfq9wPz;!gqio!ng5#D8=S_9hoGH|T%Be&%gw2oaD0z3A0ORqZwu{1$<M*?%+9xfjF zzYnhg%Ncj%ZL+8cENyx*z}*4qn<ttk)Po)m?t0nqMDd<#z1xX-^5~CDfOqD#6YM8e ztrLU`hPSb{RNizyQ3%!%Pi9z1G`Z7sKi?H)Z7<Wj@E06tJ6}ovVex%=BfVaR4O_)x zj#Xbeqs(1IgGy?iCVHeRySkKPYVuujPB3$2d3*QrDrZI<x4dvGPzKmS_W5B#1}QCC zB3y-ViA<ldDowSvZ?y2hRAqTuCh~-JIq)Pv@-#@0|8ziL^0Yv(XDF|hvBW0+llDNn zkgsC!mwXg^RNSJ&;69$jPnWsZbna6alA}Tw$uV!_)QoiewI<Dte<3fafol50tqwEr za-HE8tx<IrH!S3fz@4N|RQ>aUcd5pOOg2VNsGFtVbMVK3II6885vL!^PF>*}M1+^g z7HUZEL1GuICbQQkOXW<HF*xpomp>@wWzmiac1sXntP8j&r;-Tka%%k@HhL#fbu348 z85<k@#*s)zGu-en<vgN_nF8-mUSa=de-5;e73?D7cdMa)V&5Sva8m_O9PM8QK9)PS zRsa|oM6?-d>b5_T2*^(i46ugZ^<g2U>XQzT6b!71IodK?y_!VN7%e*J@-sBF%K`zh zCa)e-7cl;%;+OJ@&)g-`&)GdWhW|cpEq`3^MuM#|o9t<TxM79wvqw?fFbro%aZZf~ zg|9=i?J1yAFb^ek06XP4jeoFqP7F(D8gsf-rdf7WhJ&L<g3zDKwB@BZq#>G@f>oL? zt9y5o1qy+KJPK368p7!P<xhB@V+d6kn<^hMXQxJ*=H;!18_%ew&t6tM<m)n(9dWU5 z>#@Y-tGYm~v>SfDzu<Di@N>@C;E?04Mpb|voa@R28Sd011O7If0xa5iIqIImx9O8e z>rT;*&J`~p{HYKj-Ea$gnuN`Bf%D^DsR|K@uMTHTJYcJftjpkoVw1c>Kh+$tLU<JR z)3i}9dfFkkInaEIxopjwLo^o#cQ4jjpn;<Bu@&PqAMzJEBVhsw4>ee&mspnXWSlwi z@k>7SY}ddD2X<;Mfj6Bvf$$i1F%RNf?857zTM1q^M=jkE*>J;Vdu|RU1e^OB?d1gH zY0y=g$Qw06C}0Q-N>VJ*9{?8nAq1Lh!xia#|2kZoPKq$T(b?_#2T#AeaJ$IrgURHw z!{m`|;``XY$I}EY%$6wAt1g+JSo{BDy~=lPtv9Pl1#Zr)s$%1);wQKZ+cqnVO4a6# z^TD0cJ4_w4%@zjm_Vwow5{R)ji-&Zph?QmAeB&xHB```qgl#-MwRQl}qIZlfzD1yv zP|3PVPtM(VFMsKs-x)jD<jE$y>Py*>(@Gy19+j!r%(C~gXgqxiQ-fz#j}LARVD%Jw za9Aj-fV13XLL-9K+^X0)nfln3<u#?wA-KPAsJiOWK0-BRT%#a@8;_;TNn|`T37*}( zAIgwh6HnL0OL+ABg?-L^bULJ2qOmT2FLhfY{SwT(`o}uh9eS&8_DNCoQS-x#b#z;| zv4Q5PRRZgx2XeWhyPHC`IUDJ7yO%A-XfUUs8+c6}Wy%J{Cv0i%2-7LF?=l!0k7T6% z#hh{0n^p(a0Q%VIUXZTT4rLj~G#nps!%Ksu?z>gw6o|D^_zTqJR(n>o#3Qb{-dp(T zg5D=2S*y&c4om|wc9za5?7Y!BymgbgJ*q_N>Ff8B=wL@wGASG0=i_;A3niS-i69gl zGEfv(8V+>;d1IJf&KQjqYXI*PKi!`2&_2$l7Z2V7(9b&#s)(0pwyC(*P8Gnhp!$uk z9TF=D_s8vt*NY3yvtd&_8NZ#*DtwH)^9nLM^rg=K^S+(cGRy0PGm0*Q!z61ysib@i zW#%77zsHbbAoDDj?9K`9mvLn=sB?`xOR^1!)?jHYsKzdq-QZk!S>Q}X{=)r#Z;Uf% z{syEocqq{S1Vnd`VE+whp6)>Y8xX#xf&VvX|2IzYpP(g-{HGQe!gr)_t}$o+A3D)C zL8@c@_XikB4IDRhh3hALDg(_=XrQm2divKNGlG`HOo}%7`C`U7Nc#i|d@6ft>8N69 zSZZmU#JIH(90!V)v6(Tsv^Ht#z3RI-^i50{kbIB`Wvz<eL5(S))vX}yk#qv?!PQsw zSJg0XziK<yf<OPb5T=tQV!YU|vmSSBzjnKHAHR0bTN()d<N9sCm(L`S91BE3>hrld zWfDN`+ibP``LwsiBtUyS9Sr!BB}h0Nl+}YYjCtq_#6lc>t?7SB$chcQHRlGBM?oHb z#D#>CzR(ba-<YZl6yOxI;mteg4J@-|&D^AgjIi_N?MlNNF5kdQzbxZ)WA~}Pq=fXu zclh!Kl-zz(Vr*Vw^uDS?dVq^J9ymGkH~jF^wl6;Lj8gg6wlBh9{4aSQukpf{21#&? z;!Y@EW<opS@Ph1dl(UNahGcK%(4Q3{f231@i6fsrKmUOWF5key_atrCh~3aAh~LnD zl$gCV`TK@`wcH(hpgd=MyfVUn(Mx|(Ms3@^1a2I<VtwHbpM#iZ>jSGUtN5$Bc;7SI z+cTaXuh-@mw-!j1w%m)j9VA9BJV`Q|Ln<o@vWx9?PtCf7W&~Jl*rDm{Ws5@d%lv7p z(tfSrgk_}1TOp=Op(K#;X6PC9K(hG=MM7TO{nkLVy8rC}QnZ=M)^xGD-qUe7_b(ei zwduL#;=%!g>Tg2zdOgr<C*bNuX|8c(E~R%Zy#jv9vR@8CyyMc9k6Ue<O584ta^Jl` z$krTze~sRUi+u%mdhk-Mz1!^y!-ZMB3&j6uA(#9xlCmnMF8(L&rYQnh81tG6GwZBv z@&4VqIK;)Sr$nxBw&SqsSShyvN_5ej#)4eQBx&gU(Fs*c3<q$mrt2z|RA)YyTL!2U z-x&|^U$k8G$H}ZqOWyU*Pqw6-L?&Ox<8S4#GB;^FZ8fu(w@46EZ<hHS4C~FnlN?eZ zB`q@>=2yu{#)+m@MRQ-bxKQfgK0B-I$ZUUypw(R*-d*^v(Iy6(DS@%X;6>E`#al|H z>l?tA3H}X-u?Ez;<p$x(dTis`hO_8d6L90QE&?Z+!#aJTvZqo)npSe9{WTMHD^+JG zB&!qp%#{?9U=&gSj*{%C7=kC^zzSJ8_yPMj29{;s3u$7-VlAbOk1$fGo7|otiW0J_ z0vv+|-hHSl$hN<|#a4uR9A$*m6q<Ky!5Cjd_~O--Ary#eh(^IU@P~<hBR^lsHjI2j zZn7qcE&ggji^t+JLiRp~6=-9ZAgT1;_V-%3E`1hx1Q!IIwce5Ew$imx{gS|90fzX_ z^<mf9fbo9*n#)8tLVV<>bo)QJ!ZNjfmPCd}pXqwQeQmnC^gH=!+kIkl;V_mb!!3G( z=jO*?2MUmS-rZVWy5k6LSjsyI)H1%AJ{iwh+SC#`*M}?lsTn`1F_^hrhuLg3`L<9W z9pkkfDlC^lB*1|RNA2(Hc+^UuN+4a(6!m!nldyLEDeGM?-C+K$nr|)1zVEWy=zO?7 zAL==ttnaB@Cr_u0#XmULsq7*m7Z0U9PZP?@gE%0+o^}zeyp8U9dQ9x#<x8u2o}I?- zVj#?X_)SEb^+*V9M#kC`TT{>wh`yMZe{V78<nm@iiET-YvK~4{d(1$qxBJBKp?xYF zD&1F|=uzC_gzgqcB1grj28u&MP^MHX^?F5SePD+OMNL4`kef1<+qy4=1!K4c07j2V za31K2H@V9L^kZNwJif^i<`Zn)hDrR><0xKodXP2HWbRj8`SHizyOR(&$lI)l2`e7Y z5Fs8e#Qe+L(vF2U)v}n13{IYIPP|O3a(};-o$jw|ofzHHpvn>^P*y(ay!9vNnMpd? z#}9$giuAdM*vxQdgKCpDt-OK$zFMuaNOhn|u#mdB`u2nYC954mF@j`CWFLW>@_2}X z1G?#)7SEG0P%En-5~hxaZt29NN~<K&rAX}D%@>St5dbSt90{MVf~lUblUsOw=a%}6 zzp}F2?(-FMm7+fh=~}0}ptQbfE=lAqktZSN!?l)-Ba86&uD@KlCQ_#^Jo)zW*$xPT zdvu+f0Wq@AJYKRf3G(r^l$~;TwXALLMaVNVeDQZ9i-|S;t^9TUBcj1A;DNN|X~&-X zuV3}%v1?-R?Sbc6+#;^xx~YZ84t|@CJNf*oM`8$-^V7J^A;(eoWBc&;0Oz31U$e@_ z$;Ir`MjOzP1nY6LDkXY@`A(azF?e9vF1ID1z8p!#qIupu%U)TG-6OpV;FESplL}_1 zAgUm)oc%495*zZet9b*{Z9u+{>m1jHIzZ5Fd3($Hd*Dg7&{G7al;>JEB(SQsHVU(m z-fVIk#V&7Iq+9QO`O(cGgR?hKAb-=jc+g6RE$B&hwbjF|C9<R*6+1x}*b4NBV3XR7 zN+r%5*P^k`yd~34nYiCEcaIu%rf#?mz+Ua+?aGAI8B04%r;6;_G%la6bLnXL)8Qh) z9kz!a8;y|BeCWAl4ZKgj<4JZj-XLRTd*S<cR8XB{)-i!)4<K8o!*y<6Z_C25sE;Zi zZg>C3HTwDo#H%dO?t$jrc?I}u$&{aNDs{-5cC5rYd&eEo8Dw6ajE<qubX}!P)U<$2 z%r=3!Fkd?3^2v^RlOOL~zkkH6|8J!W*7S!(df$m2smtiGCY?{D1#A3C%sNK$#uUS+ z{1`>;*@SCpuR$kd<p!0In1<*NuRG@aBhaf41CJ|@W7>^X*W;faF>S#6+>F#Ql*#N+ zGzTnLt`Pt^b12j&5-}JzVJXKQM|CMZB45OZ4JlLb4k?s6t}(dP;xGufSa~!E_{pL~ z9teg8t~d-;HHQqEks59!nv#w^orchA;MoZnkW*RMh!WV6i>6zLvR+GoNfL4S;e(u^ z6uMa|$%#Yt!4MVa9SI~QN|5>qW5O$YOP(YV!YeyEjCF_3ra;x}#?cXz@Ot;X@gBzs z<8Z@BKd4paevEMC<4rqfjWG8C1V*axGNsR@e&|itCGV5R@DKhOZ%e!n&f1>zLOy1g zQmJ;B!k$V6WbK_Cde9z^a6e<q7S}d*wZB!&*itA9_^S@HAOag^`Z~?%o|%Raj<ijK zLz(d=m`#g9soj`RhiMR1kBl7Hr_$Y)9wmk<8>(3}rg<v1wOG$9-Aykm8_uHL8m7uw zrXHDyO_mgm!t0D-k`=Mk?@~|B4ii_XM2QW9`$bE49eH^t6gK2g{i2RK;Q3o4EjnT; z-a%ORs9biHEr0_6h=D}c%lQfaYo@C|mJ?rTw|)uC|5*0l(IsALu-0qFAO)+@;5hP( z#gd4qWteGFc4_J`tm_DxOZ(DRA^>&Sj(HMz$e8;$b-2qB3Y~@*rtiMo0z?oi=LG$3 zB;mNh(V2tnt=f8_vE@#k*?BIku6TXya9tg~C#8t|r$Eu^_nC2lhYUz%5k#FMQFeia z6VYq|ww>#KqbHPrnfEjZkmWm5W|@D#ab@bizyggK?#y@00=dK?!KA#My`U8~qK`E; zYUUzLM0a5~<XY4B8Da|Xc)7f}5S;bw7v|>i=jP+rr0p_#?Ijkz6SX6VeZK#!>)h}w z!LSn(P!lVYU#=|hl!@VnsXfq|so68l1X9)B?vV)=e?kRamzJW=KHxb4QW0{E%MttL zC$dGDy`=#s%LKzPrRVfU-%Lj$@so7OatW5gpW<Hvq77RE>jwrp7Y)7-8=;81_b)D6 zuq`{0uKzyue*O3}kP3xAH1NVgaZ9AYl=WwC8Q5|QWzdV}7`{)VC1EW;9shdrkoS|Y z{@019fNKZh=CvrLZ&=R#b$?>@Ir+oL8AFn=Qdq!c&ND0<!~NwE&lkeh^V}F^3a>$` zg?H5cIiBsNBliKimu)m)m#lf)-<je;ystx9Mc*Y0lkJXDS<wI4YkUa79R?mBmFi%G z7np^38i^veh!fp3a;q@(R1Mv@;OZHrGl`agw(8vOJOV~DU3=7;l5mmcZqK-{|F;`q z`{3{cZ7bL@4|_T{dihnrH#PZ&JH`Kc){b$8X?rC4<>$bgBwP^C2&q#_v`5S%r!WvG zBKa>#!57<bU^?lX!MOL~F6&&)INUfK7N|cSaVFIAj&%85lnS~|K&8Vmt(Mgwt#?@5 zo7X~;n7&qa$NApjH$P7)1e!PNKX^6^0avt$3&A$gN79WO{^W?pmR>tp?uX3Iq{BmQ zdC+PLIH{hIRcupN(t6&MB;;I6NGlk0i1K+=3}o`!m>N&Ch|4LV7?7vo*0dT8z~di& zQ>`!H>~G}ezk<nM)l+s=;kFDL^u8cc_gXqbfhBbFhhayy8^^XABSZwM;BC{4qrY0E z(aoSgp#>&P2#i`tBVEHfFy;wO8yg#EK@PHqZ8A*3Irl5Jn}!PXQXn-J7lOM^w5Zg1 zrp|s!)<mn+2zfaHtWfahGm^`efV=Pqs$BZr5n?}*Nz#hMlWWA26SSyKC^RE!KFTV@ z(f>uI3JD1Vg{ew&dkVLRt7|mhxO8UXOX`AjoN9EjtYc7~<46a+K=E<t5L<1~k|BNh z0~CChQ|7sSEXs&rkda{Pl+cXRnQC)3L%yYSt8<#hf!AU1-J~2ZF)KSfK$2du)IErj zA^oGUcfeLmOsk@kWt+$5Ftsa3wU!qnhV*SjWct9m!ze1MW9Q%Gkai)j-}a*&_`B)n z3zc<zX@aDkOH!hdMNkpWVp?Z1L(VpJDCj2W*~<QrgJ*R1XTJ7lNu46XEoB|_qhw!T zdA=MI3jc?zcMPs9?zX)<>W*#OwmY_M+jg>J+qP|^!yVhU?Q{n>&$;KVd(K<6tJbbs zA6Bg|V~sigbN<HpTn=M;crLpkmXM*~vQG?^Q(4s{`Vogg#T;A6b=VxX2Q1&e`<RK& zve|!(Zc>UN$ZUl&tr?p!yoX8gwimiaQCdhwO{?Ve0$exnYpTC);|~8e{QZdd`(gg{ zFY62Jf7PvRnVcZuQ2$kmdWAco<9wH*4}@uqLLkC`6IXz|)|HPIodm1|p(8<Hib*p& zNZ>FM2?%>DX+63KI6w0`o@9`ldFq;`K<yT5%to>0{*YzTYDdyCQ3Q^aFfr7@YAgyc zHmB(2D7&Zbk$=9K=>x#hw7#|k9RLV#+qtW~!P|M+?tWiao<W>*n--&##z5`FETHa0 zB#Hv`YKX_pr&%#&CL2%-Ex1SqmXWqs&n_VSW#%J!$*n0mIi;b->>(`EPdj9gx=lR1 zgG))*i(gPbyyhUgZ#zF_`r8V=qj>5L{!yF16Quz2(F=X|=R^1}NysOs5I_ikUBLEe z4(+R6Kp<kl_n}ZgK=pWnv@QZbDu{VZbGif6q5icF`4pSo5i1Y+lq=|qQ^@`lwEHj) z_LKQSEl@wpL;ITsX`52^z5;2RR`$-}B^sDU>Y+H>2lE#}@QdoS5B#GoKM(RRf#7en z$G*T%`Pmup9`e&j1LVoTDsXiec&J(=T=Z&4^gwF}aqtzWSZF;$kAhvVez-skC^ZNG z1A4cJL(wjIzcd5`$|h~Q=&e&f4R{@Dx74jwzcqLrTDRP-S3iycogG@Y;+`xtFg-Q; z=6BX&Q><YIpCw(|#R{`mtmR$AcG9{fUGDPaoX0=|oU;qX!uj0>D~szSJFYkf&HR$L zi6>A3h|&p`xgN}9WhJ-0oUxpCeVqxQGe)4)8u5Noq~lUl6gcoOl9ARlf6jW7aqu-% zYjRz1O#9nxQ$eorW5>3H2CZ0qnWB4w5zZVz*|pj>o@&v!#AR%U@#)jYOrtH%V$ia> zyhlw*8o{cS=;YIA6pJN)ds%M+KgOC<Mu#USf`U#vj-sTtsIy`tlh?ExIBo)<eNeOS zZ*)fN8Qod#5#H&&fv+B3H^Zx8SKq9A8DUvAwxU+GA=Xx?b=8Z?K21emRiSs1n=C1T z{*`Pns*OSTkZsQOtGYWQvmY!fysYoBs@XDx@lif~)ZBGLxavWzlL8kDMwOxy36ZNK zhc+0}VR}`|Ewr#d4<cRUgDejqZR_7!;82}h)w-z{@6de6X|uPkw8tJ|6M`Q)Zb~kY zZ^Y#0)4EcbR2l@k$f9Y!Zz0$A>)ulLQ{0NtRW&?4Izc*@-6=FW_SI0EU{<#Lq_UYK zW6_yQlI}nZ_5fCrLr%EVJ8(UjKbzpnIqkK5;7X=JlY8oHnJk0_??DD|7@3S)OBS|X z%tMSNnSMX1c_RzjS~nGNkk0YUn?^~`idLMq4JktdjgvwTt6`|ld{t&gYIt;2?Dr?$ za7QE8GDqoza!Ve8uqP6|0}lHSwW1dWALbE!FOd|Ai+-|P|3Wv{cn)sdeT<KCa@#FV z`tl^kCbWIbJ^MC(2`(1^x{qm`=hX~cz0H)1>sRX4LJkv6N)>{0+HQ{G_Hb8`OK3J_ zPT7pu8O)FFupQFZRLYzr8uJFAcL&cnUpK?}zqcQ&DLg-*9Mvhh;Z5z2Hoyr~UY{v< z`g>$}^z5UlHZhL;_`!?r3`1^2XeZ5%kntL-U7TcCCgHX`Npj`^w&bQ}sC*Wgeyn5J zT2!7IgA`B2&SC_y)*aD6V}TWadQ&(?+dOge49O64!l(lTFC3Yi2gFVWNj2$nU%O~& zM<}RRkvGb|j0#`&9``Fuh&T#p<<c0k(a^Kg){2sYFh_HXBCj~pW85hhhhs9baQwk` zR36qgzC)NxDi2=3IK``7$W6q3&wDDRHSc7zb!%a!tu@N^2YJdCjXCf=<77QK6=DL! zH~8#$MYh%fd5rF$PI*NC#BU=tR9{VJ4Mu!}8RNaCbRT_N*%BLItm;@q%jXlkBnqw` zf<GvCyx?(=4Mk8?0(;<ZYR(GW+0@wNwb~yT({kH$BUl7n=_n2^|M_0G@Im~71<uZo z_;&JT?Fq72nl^eY-Z6S~#u#=uG^T)9!%jGp<s=5xKr%{|IX0B^bXV11K_nDFz+!W{ z^reha0O2vcI{$cizL`brs`l%u-}wFwcMqBp@}DAP<1jj<I^!THXbAs7f=z<bv=SG+ zCUNZBc^nNW7_T<aE`<~9)1+*qrBHNG_+hI$iwMcjlSd~C=>hJTBZiDs_9U#?w+T&H zO9hjxiQx_<RiR8>l+0OLI4*dCiljEPmrpGArw`0CeDpM6%D{pMX@t4cnDk^_9G=4A z2I5RT`6M~j8Fa%I#C`03qi@K7z8mGcu?4c}+5`tMi#uj1k*H~k@4gkVJUVqwAFgTt zQ0dJCc(J~(rwsOspEb#NKIn^P#%aVh@SmP1`#mdcbFt3T36o<YHZWW3L$DcW-c0pE z+x@ZVSr4|GL9+>9JqHq*+<MCaOW4F^ju*}bQ~#+!AcCfe3CSOC!L+t_vB$4l&{B^K zltu#FuQtD{N`~K`FJ(`S>386np-XEy1Xy!dMfJ&iiS%b|%gKAV+`6I2N6DAshf&*9 z`kiv!E;-v97t4V;;k6CZAeLU7gH@SrI)Gp5O3`>uJC3aIWMmv#LHqCd2S|k{lgBwd zXU#Ua<Lf`y0+-WNGbt;?X!{cFY7~;%QeFVMlxG;x=W<4+q650PRly}cCJwraO#r1B z7v(x9ukOlk&IDswf7fUy^UKnoA=pIswarZ^AWZ9mj;4{lJ7bb0v`T}#l>KNSrEV|# znyx&8#WB&ejLA+|v{s|p>0z3Sk8ua_uql_=r;XRKic|)@DQjsY>hfnU$#ewCI?n(h z^-nk|6(x*jMkN)+JPbqeF5Bs;7b`Q|*J)7)jc6_uCArxNSPf4$*h_UdZalR$O;s)) zAv_=PT5LSV-Mv!s2dDxXxvkBM{(jW<tK5Fhc5$s7Cmg%7DU)>GV<Y>P-qp?j>b2*$ z2Cj;?2q`}#_I)F?aA>rtv&JX2So8rHL5tBjr~H(T?m#_26#ZG-w6y+m!X+>L7`jzO z@?+)B3T3oVrQ-Pt$<qV%s?bDIC23(bU6q7ZZixEXiCgNCX20^ivgRb43LWdS7P^Qy z0=|cOTX5M%_SKYYd40b*w5naV9(HM_VMpLE7WO`oH`(dwEbpnsn$88?_fY_{GZ+j1 z7fOP|{U8ehIJdL42sA&_Ne?4R4m8evkU*yqm=7KO6Ah?*xM6PVi0h8}wyFD%m~c~h zN?POYmBx_=k8FG>uVspRE2TFw%!Ka#!D<JtNm0s)VCYllEW%MCHCHH+Uz3J!C9~a~ zZ*0?>9?hw=<H(9|Y!h)jm~4OvdDfw1V0)yTv0tf;U#p28O&{#kBp?=i<YfWO|94yp zr=RA%eIBvjyr?IQ!K$uJss4{>FUUvszoPHU^BZS^29o>AuQdbq2d0(SnT6*(rBsU> z+l~=dmE|8$HY#0{D$pQW@jlR#_sy8t(j&5j=*|9qP=6(vS{^5AOK$*_Yu+c5+dSrS zcQomJRrzA7;NR}`(@7BS>xDRkA%1@2Zu0t^k4D`m6aPk|ql9Kimu;%R5;;kQM>3%{ z(^+K&%?@5(`Ed=VIiJfV0$pt`%rc+r5p@a0ux<p0a+8c+At>&Sa)~1>E5%Z4k!)IV z5vNMG5Rb$$0$qKf0S17YI<YO%sI6o`@heh%UPI{KfGw|!8FHgQtjxAK=-hzEa?sm? zD*ti&qy7YJF>(o0rx6Z)9R%pokKOi1$oH{|RpO@9`;3oO2BH7_25hO$@FnO`&nuWe z5)d~BJmvMxE<Htg<n`zg$#eQ(5<<F#3L5MJ`E~ZggEx4trx}3LjF12ttGoNc*c{$3 zas$+YYp($ZM7ZOh{B^~!hxP*e8MY3oMlhlazI%8iT3NWc4I0-@`1A$;dP#5!NmZ68 z5J&sVcn;YqL_?{QPQ4iTd%!;D9=rNP4()IFD->Oi5T4V4q65_kP@HHj1|hYRO5-b& z0HX(^{0y1S^c>K&hfHTY%NHxivf#Wp->0ctTbo{-B0oo6XsNY@)+u8hT53u6S!$uH zK4q%)dlA<PQF+lTgB@PCR91M3O&QfXqp(%ds!s+_`23ja+y!`7aErRdDaj!_pDO7k ztiD<T|8G&dV2T~Xo=42ST4t`HgX<Ck*g*q^$6VDNehLuBN2+Ex#kI|!nRolkdsPOM zEZQlA_5EhAdU|}e83s_qH_8tXVC>#R{rOAm))%}a`Nz^LmA)S#Z3}W0OxiZ=i64F^ z<j)nqf)6skAJ3IgG;j14+#}Lo)^|<1E6F~=z6gCm`L5g}fAc&yzKL3M45g(Ri-e0& zpdWNcZXAF=$b#orPmi|+Ebb9qL-kf0KtJsFX`kT(>y?Mmv6a^8xF0&cAAH6wn);1f zTq^&ssZl&%d=lOpiA^9=C8!yuTY{JPA@JNf$;-K85#!I@#qQJoMZDLiR*74Rv@Zm1 zAC%rsr(Ff;=Zn3U-((4ipY~uXgYtwzOD78xsuIAREYxaoE25yjlY*(Z++GS6>M3<d zL0Dr8ro4R1U+4;?TJ;4)Z1Kb2^&I3+xfjhY=o9;&kaNL5UQcd+em=qf5w&8-uY3lf zSNjK&Rvz{PXKB!1_B_X5dI{u@sTF9wCLic(AwGmoEj(zgLL5k~$_{M16dtHf4gN2k zqTC+<ovLk!cDWuLhaNA4-Jef-f7G7n{Bcy_9Fn+UF_n}2zk}TCb(cVR7LoqoEGqt) zI>$69v-&Zzl;qD*dEn1caW*8Xgq`eU{Um7(CuNP7wv6pu<I2n=n-SHVKQefoy6Wy_ z4M$`@^;jx<pK|nPusxFJjB-a7J8u9@M>hB0@4Vuh19v>e<%Oz8J{|d>M{0tgEP%|# z8@<*L1=rw#M^WCdZRA;BG@ZWVfi&IMUtb9SyRz(700I7l`_CaK3)-HkCe)7~c4_hI zAc_F8HA>rZs1njWbZm4|M5+AQv1?MS)ya{yH319JE76M<t2&JqRV^j76zbrtl9uI4 z+aC1`{jXP`KEFJH-=doD%;cq6lP3NBZk){Y+q8S{&BwL*&g)AD%#Zckl^+9)QVKD} zuhC<#9zXcgjgp{mjmIQ@DZnb*j8u7^@izcIM5y2olJ*~{u<}xo(10%!9)$kTyqJH4 zU!ytSs|kP8()-k+g1vUX`Us9-NGrxML^LtLe8hz>shjw)`9r*BMvF(?Y5c&(N7#kp z)r>A1`XT}G!IOuX77vcKhg|0{^^q3RbhkIx-`URX?XfP};E;^>0;jVqrd_K4yCee8 zdH}5=DsXWam^T`0Z<WMLQkd(K$5SV<l$IBfIE`1BFGIkoOkrn(iZaiwb!mlK!i;^? zEVYAmzdY9(<{2ibM3T2KlN!j*rpYO-kb69*ilW7VnNvST44W65oKF%n$?0kj{^k== zmX3&p+Yh7N9Vv98SW&kyA4=TTEy4k|w8#wIjqT@gW6On_xcRXo$Ykk|QK}<~vCG0G zPF^i`!mX?vwFMX|GS?DLS_8#6o}_PG7ZwJlItxS>ih?pbWbtI;mA0~?D<b@s7KgXV z{#GO9@JH)F6zedunn(YDlOCv9F^LIY&u}0gVU8!S3j@$2jbEf84=i2ONs|IXio3qz z*kRsMO4((U*_?%2ZMtKg`$3fPa26(?G7B;)Q&`D1J-7gj4ik0#`m|1l;W4@ci6h?_ zGugKgl1d$pXG<pODCyhFbEDRg%gxlSeTses@vFsLvN)a6R4MWiPg*!e{SIl0^g30D z$K(>R#4`~e>ti9=0%CIQyb{33^TL8#>9~iOkik`dIy*USnXat5X&OnDmb|UJWob?b zPc~+K3L4_9$Ss*0wTP|u3AA`*Rcn21#8fGNHICH)SB+HX#`PHvnWHTuU-y_>&Ojj< z6G{XzG{1|)NE5Oh_Hs3DgB)-gLtlMX2q`mDXhriVgckX|KbV^vQyqYFkJ412n(x#M z@i)<urL<;2RJ+~K48{t{2+ap|M{PC|fu<dzZT|giaWlVE43oOQQLi7mzY+d|s!RX| zQRa#^wAN%^N;7(dd<t7~L>bM`rwmz~#)MZLO1J-kv(x~YVf3{&@MaGJVh5=Byxt8- z!x>8;Vey&gRT)UIFP>(i4T24LXHfP9-BW}(xl@L4-zS5>xNQs$@w_dRwAN*|9crqe znFNHGa)H&^F;c_&n7}ye;Vm;K<!CyyU}=LJ!(eHv?1{F;G_SUWE~c06ny-=^s6zY( zT8sFe(ZE6b1+18K*Y2jhrUdH4JlgNdrV8Ub02QlqEHNH6GFnwL^2`Cq=Qt16Ggn!% zA0XW!JBO}?Q$dAmF051J5hpYD8EN3z>G6m?nRa}_J7`*!>M_8F%HLq2OZx0Z6Ayf7 z(%7k$I2Al>fp?9!5>eK|xUY7a7*--(onBthPZ}XFYdDWYd@Sg%u`n2NAvKnR^WrVj zs4%($`qr5PPasR=HY|XJebG5i!k<y@w6Yp)XpCo-y!2iG_U4wD4*1F7?m>L;igLHv zMni;fM+18+Y#1rpf)edbb?JJ?RE9=|q~Q=RHKLggun||UscM@har^#UIWPYu6Riro zPB!8KV>f=;mGB&eHLm^~I$m?5mAU#BDBI-JCzJD;v-}WpTV+6jp{6`itA;yz$38Aq zOw-(2f(FTPq=DnFG8oZlkT%O|NiA&|vtvOh={i>2CXI0x4dy4kqGPQ-xtKXCGe2jL zh&1X~U0r6svfURC?SB4KT=#|OPNv+=I#I8yfT8I+cvIHoSh*XM_mH~qZ4)OzCR_O< zW+=}k>&Rz~4ke(L*F2QdnQm!U=dzu49}8nR>UA9+FD0#SgY{ptwk+vi4HrD-Z9^oT z6h7o*{al2oRqny}xAOz}8B>ennSS(Z-tgNv=d6EmtN)JP4<Ki*H$3b`r{8QT_cDz$ zFW{AOgVUG;APR0RB??T-cA?nv0y$jpdvnSZU7$Z_Ek^+iiNjGT@5_TTH%h1lNFc0t zZ8oE!ta<r%Cz7MzF@Nn~_3Bk~k|{=H{$7_U{->fCD=qXTOQF}<Eq9Xv8@55Z2Z4CS zYM+>5o|N5}ypa)ghpeCrtDvpH?8$Q`0j33mIm1f1!%EjiNRY|2qJ$2QwMPVbT)aqL zFHRJs>LGyX0@PEuoL~-K2vcD5xs$f)FOzHPiudPgOVwJhj9d2nFk%)dym7HwQhpXM zs(^7`=ongZpePl0-ITcM=dEV4rvtd_b?Ii1x&qQu>l?XaBCC98!NS%mY|52rfkXW$ z4s9o6NM(7O6<cPR!{DAd(L?Tx_fNLg?&GHPgJD3d<S8+PRh&T=qdm0im1uuq(`&p) zzk9f<LgC_HH-Lgc`bBws(EUZaJYORR<%XBp_@n&iNr|LNa4BOPe3zm@YG$7J8tx7f zYFM~=@1hUvxF_wZ<PnU%9lzm8`|T-W;JaN3!oSl5-N}P3*i(F3L(bF#d@n^&R7diV zpFx046wH3***BLBAjgrP3{ikO)3~@Ivi-o&M*S^3{H{G`c;A8EJJ`#fE^aGHOA7vt z5wd;Z5V4d-_>|6A@=7#abcV!R)EaANc-&p6N!{Q2DY;np2E31G1EkLgyyCaUv8&Nl z2`u;(-NW^<rk}}v{$$M5#V-v)Vn<eId@6vRI4LZ!FQFChTMwy;35&Mdu%Si=WM_`A z*KAjmhx)Aan!WR^Qilg5v+_}Y7K?h8eUEmpJJ@^VuuHz&sSPdSpV#K<9zR??;!}SQ zP2W4neYToi*_E7xZ1LF2<aTP+-i#g6<&w0;w@~ulz^00cI^b;h7UbJQ%K}TC=}!S! z@Qy88H25Xf_{LNG)A;wnHLvL63Tk)6q3e#X#G%@pn{gfC3CBl0*yMtJ7Y>SQ@JHW} z@L6ULet{m?&*^FnI<(uep-n8ekB$&fQ9SjHPh>Zf$g|)nI)Q(-gz_h>>F2bwRx83r zM?X^1Q#xsSCSugyqM4`C-eqEMoVCT%=sM;}Mrvv5wo*<I!lnt68Olh+V}H{CXT}jB zd#d64e@Fgai%}jRxHW%IU~~WDdLVc&vx|8B!SMfF*7pQ=8;>CfCG>yw?|&umYf66a z<uF437abE|?Bwa-V$UG_Ul&yuOB;G)8$)O36g6#SRPpa=RtPgqwGPbTO&1YSjUm-q zYO8j-dP6}MBnDEVDVr`HW}c?$N5ZVM_n1Q}wIjNeU-yN{l4r)1_lW>hXD>H&jV+Dh z*2)=~7cPCk&e?+ba~(jh=O-g{#hw)`!GRVkV8s{{2*<XmGFT($lpbk_&0-~CnI7Zw z^?aob=d3shL=@K=OjD#CnhlK(6@`<=wgc8j^C)h{5LcnAv2Gf3_GETu<@&bgW@e1Z z)iTKOYv{|EHYGV%JjqyD+L`7Clwifm@U&8N(5ZJE!Gs>;l-iDK!zOI^s)mkOQiwMJ zY_S&5rTrX*+i83T3%^-3#tzmgU8P<K6gZ~+kC@ZuxEy(hrN$I*YD|`#wG!BN9i%yj zE(X_<3)tos4B=PRF0wp*`!Xpd`LifJ85BNI6m)hcNhvLNy)9IM(~e_Htv7T@q1A^Y z$HWXFtXd^3`l4#_*2=z51K6l`QRc^hE0tKKAw)9yd)0+#10m{MMxT}Bqr>d_kck?b z*W7h>5y?b}z{nIG=P2n0!f|X$+ZycwHi9(a&D0-h@dP3N9M8I%1*OM!>SclgA&D+t zYl+Nc$?|P~%ci0J3XS#swA7C5b~t#&Et42JvW0s&@bC|F<Q>q$)!(z0x=KC(NXU0~ zH&dGQk9pcoy^=>harQ#2)FXujJUBiQ^*cNxmJQGP`UjXs@V1d}b?c+HZc2j-e?ymC z7U<uU)K5yZ2c{<i=|_bRd3EQl66~`?`!sa-Q;&F2xVAibJxo1pUe9MwZ_^V~{~^|? z*g2RSj<*mx_DrNKUSt*OKs-SMV(pI};R>S4_}g60PKARUAz>M_EBF?hdxq&)#1(#- zkXre6Co;lwnw>rQH|bqu*W}*hMuh?$4;2kg*R<b%EX)~(!b}L7;tC~25OH69G<Edc ziC`wSa_d|z52@M9L}Lfi^!zI&od#ZPjpoOHz%jsbZfDhc4`$MA2AL%SV)vlfRr{Gk zYl(MI7d>I=m*|)9Md0$ww%wlUyHs60$bi#naMzO3q`s94#r1IK8ntP~@B-jY1CZDn zx@eVs6&Itk`pQ=1TZ}8mYWKDF(oK})eDdwo+Egp9I^%KnQMS~eROM3U@WgefIwu)c zLPKu=fl3us?$Dpox^u{Y!46-Qg>5HxQHid^7EL<|AenpNLbhYJY@X1*WtN=h`aZZ^ z#rqy%6JpUk+;K-vQ?Pq2GUunC)hCTZi4L;s<_hx-y1`UN2;zdvN>eH70rsN!zQQJ^ zobM`vc|L#1k3ovZf615TkIVjQtaB_+64>$!GKsV#8_6CKp*x@ejGU2U17<PC{5tDh zf4gk5jX-Bkf}a5KZ7W;CJBsE}g1H<K^Fs)1fQWSA8AK*JGQ;eqij_~S#y%RW0Z~KL zDLjNopsZX4SGE92Cn8xZ-cWOtM=p_HPjmn0G1zH8L}L#~g2bZ*&s{$KM{c-o^JO<l zVV#%=!e1~X45%i+_u^+mM10TUTBgSbOYD2>>)<pAgX5#@*M-|hnM0xOx5sdwYiROt z!~pIn$Z$BGi<(=+g*+EwyOOZRCN?r<52@a_3*vJ`EY$d?$o2XTog+(HDBueDou4uD zH3&GOCd9h^U2qQZKro^_{NNa7Ps}L?dKZrW3(jo!5Rd?<i$Yp*zFo`<l$$`VIOHU& zTsF+Ar995|3WO%pCMJ?8Ib|i&qCD^%ddBgppUJO(2q*?}#)L(xs8Kwl${KJg6z7Va zRuIiD2ad!(KWmcF3q>f2bNr+z=L;py9Vi`mC6)j!5(pgw{EwW5|5Uqf+VJx2kUzkF zOd|g;Ar>`ZW@BUGk~a1HZy{EH1E2uWzG~^bkDjnmg03(jk=p2RNNr>W=D@Wf6@2pv z4IL~lYD)yQy0=x`)#0t?$)`kbO5MG_f5mj9Ol}1s+u~kr0BL~jKeOge8?4b2lB>yX zPv8sQgV{^X*LFL^F5zblAfJE>g1@^i1phnUDD*;HPY?zD-9u=^6bl042AGt6K?MQL zpPvswaQH4i;UP8>_B!1JeSN@lafiF(nLDGm+l0aOg2w-^KG60R65x~kn}39W;D!*F zf$Ei&e*pxFVeA#x{G&UNVK)j|ZzPR#CC$`F^gmclEd7@QG>9I+YiP8+l){0zIIwVm zrD$QyLMRY5X%@Xf2Dl{SF2!<Rov`>ToB=6D<*tR)h$70l7a%h*y(;qlm~klcE@NXw zij}k1J@=4c!HdPi$HjsqODZNOM&=T&KabuVUyzZZFwo-4;w&-{n|Qf4cc)=Or<j*y zOa=9CMD{rJv0n#3X^P=gi)M7zTom$ZMw!W<iy2|v%08oGMiDNuzLC>Wp3yi@i`TX$ z`q4<RLu_P6cDI47g_RbUiM3^d+`7VnT5}tJ@7nQRT~IcnVc5LOEG{${qFB4NjKP|w zI(lW=yU>)TF`iO?0e^l#X4Y8WTUbTf`A@FU)GpDMzcLHZ`7a#2<7MD9WUiMg(kM8a zocEhW!TkI8#APwNHDfLoO$nBAX3ay3_+y9W)}96(56ZmD1gs^@LqW1>Q{tJ0mxG7L zS3zM<Yj9P`F?n+~V%l9fIGGpMuoHd6e8(&NOkpk)6c)(&LBV|qn|jinF4j)cF2lf~ zOUXXFBMSq7VhUz*nE`zqo=+}i6wS(RyYvcOkZvBw$ytOlEJrEDkRGBn5k<eQesTKD zq`GmgvSi_|!^lwL^jufBNoY1Ni-x`<M~Mrq1jW>{Pub9kTE=SJ>`=F2+3opxOS(mJ z=)AyDgWCSWj)>Kg?7reMJfXoNBZna@YxP51IaD3+Bc_M<O=@6#gUU_lq{FtZ*p5KG zf;iF>P4kpGb|24$_J-)pMTWXR0_*35#YlM0*bK`8op3@ukY?f!E)95gbqdFPKy~XM zP^~}to1JU^u1t)q?QzB)DbU>>Kdwh)9lO$k1pZnHlY5E!&?b7UPW)oJGb1|qNA2fN zP-&13I8yK%BB-kf-{lNBz@MhUOW`h^MhHV#(o!lwqkGpFh7?nM2WO$i?=PbE8#f2O zUQ*Xqc;LlVc!1egY7DWu1}RQB;jJt?p-fI`S(f!+t$b)GHALr)wHNG@n|-`?1pnDO zmn_-1Aj0ZXaD-_yeODT;yS0uz<ER2{Rf`z|yw*hS?7b|e+{`CaRFIXCczPEsYMRuA zs=R{$Nc*T&RnDCP3T_Eu5JF$V28H)-9bOGQEF7M9Z@>Qs^b7tCBiodxU5}h95JZkg ztWF9`@5smUqWYzJ)KQb#F`zz-Qhp?v+$o19ZQdg%2(;t*Eg?6}%o37(-2M~hNOubW z$)t$fio!iyuGrj(9#f(>H~i1Ir>n7|Z58-W{W+i|Qi~9B`AAPUEpSa9&e0@Qi^w#h z6lw$~HIE}P_;d@QT1CzE#NkbCJ3&5+Hlc+w1{x##e*HOBweTn_R=O2!ql=<ySKdQf zXeLuKb(XJgipfh?((;<Ca&kTj=IIu&_2i_;ke_v~cC9IeXsOtE=`?>|8)EBDCjkX! ze{vK&p1bcY<xDLuJutG}$;h!XVjIX6n`#Jrf_0`GttqA;_mNjzey+Rn_Jt_^UC+$Y z0$X@!bq%&f${;fZnKPDE%ReKA8@Be2D`LCRNaf<0q)SertH%edLLzHweF6vEfP$af zmXpTQ9UfZ*ydXm*bjocz;~+25AIx++e&*08M$&KT)E}ppHEL*6G$&iHL129qtH(P| zWKNu%LdRv0H<5&jQC4v4)s7sTQ9hq4XJ)XcH#Jdun6O>8fG36ERt@?Y`j|FVSJ=pl zim*fnUo$)1>z7!c*{zb=^soR3d?jB`y?<E@qQe?6_&Qt`se%bL!`Ert)M+8idqMsh zzcAB6P_Ow|9jYVAv>zB$eH3oy*J<Gog#UCRK6cTKyi4bp7o`^Ktg*Bq%b=8-{YzT& zDUAM>HZJ4<rR0DG7iDqT*TR1by>3@%*;(WXb$=B=9hip_$T7$4i!u&y`C`^eE1)m1 zg}OkWlf${NJm@_GX_#vWhLc`x51%%;m37e*Um@;%YyRNA))$#!YupT1e%usq_rF3N z#$(+O-M%c9xGYd*5Ut9)g*OOor&k1jK%K+<`b$j=<YD?5j`gB}%z_{EGkU=X4Es~O z65I6EL}nJ@@q<$mhu;XmC;P>T{}9L|XABmD17&E8bVSa7)fYSU{ek9E5xvDFP7i~K zt}T?o8WGDzVr-8wTbCx3+^KNPeyT=21zQzAsO=QY&c~Ix#v`2Y4q`Lo306p+%*o%( z8wwIRIt~&w9iSYtkxIW2>KgR`b8OqkT@owuKP+PsjHQS;;VlL5DV&6l7p<Lj$aSm} z>rjv)bhi$?fK$n+mE<>FdY7H><nhrfDd2=WwC`ed3n^L@Hq_Y-%P673|MT+kh7yZo zSk(tB#Ef`J48vwOPMy}1^5;@e(TEkv^Cyg0>jF=wuMBlxu(TlUa3v=1Rb}!-Sjr$J z47E6%mqiV~mLdjZLNL+DREqwb0YnHap<HWH*(@Tdy;jvx_+*RWTd;Y6X;;u!2;u8! z`oo*Ft3s52KY5liX{7lS_B-03u865Vbc90kxdJ-|V*;BcSL`qJJuAOF(}9k5Nki5u z`v-TcJ}}nwQfHvHL49t3?y>`~oBGm@Fs7>~sW-yt2QNT)(LCal_=-2ou3JF-QEJVN zL$f=Wi>OQV5Ne;c?!>$UCQ*i~40ugGo*m$cFvpH0pI=gBkJZmn|B*Q=Np`sf-j6D$ z{hH;VJ)nP!@Q%j!3SP4^H%$@oOm+8u`9JXx|FJ(?&gXWX-<y-X-wl6;|K#4Kt=fTq zXKnr8YV^NNkg}z@ouP}Xlj(n3A=Mpa6m`^3JLFZwpkS4J3u}_XWA!bgVQ5M;;^gGp zqMr?{AN^BiP&8RvlgFV<^1i3P=i2z6m!?m5Er9%PrGVtCjxLxnfqL$wE${0s=icws z^-b@GnXI%rkWEl^MkGN054*>LUwIQNvJgo~*!EPf#L*i_l<AQM*epn!{acJ?B(KOX z`>4M9{egj^p$3MADFH`Vf1O*G5I2#tHf_!Yoz~4xt2ib~3+OKBLIo^y(Xqvr_6yHW zVSC(RClH%pr5aiFGv#fWgPq($dSOvvrS)x>$2*()C&+7tU|InqQ?l2XPS*^fL$z}T zqo_7zl}>}LD!uy3Y#Vc`Q<h61HS{WWGIDY<Xb6(#m}2g@U9EnU*3*@!8<FI^L^mPs zlKV%OS{bD3a{a1FH&NKjJ|fgMiVn8+AuAu%43J$kQ`{W5m6FV47r3+>?wEzDx-O&f z3r}vsUM18k9m{}n<dXWR0VZ1J88YP%pif>Ed0S;=8+XwzO{m129d~B0g#?)}FhoC1 z5H{_LLAfw)Uzk>uDR_@kjBzsF>*RiPRxz}pb)ehwT-9!xYspNuZD;^RwhkuHNaD3W zqR2Om(Y1=&4JEW?Mz)B!{~o$G*67<`(kNCN#f)39$pV!7y-;^{L4<9#TSbLAyL$_x zbi3v;PeqxcV<_<hTWzc9Ol#gXR)#jL;%`nRE<I8zup~VZLlY@*;<=5ipa)e>aKqXx z{^iYGdRg0^fOsMsrDtZ&blF~k+p`7h6&(Og<6#XX+?6~U&F^(n?-O4hU7W6O<53e( z4H?el;Q=gIZtfouti&F{_Lgxe=iUoY%XYA`eg>EocYHau$T3mkp{sn|DwH!Dx>EBc z5?k!osh21LaczBy|E~D9-0xn|JIHOyN_Hu@aUeFft(VUx59y@coyhH3U6Ajr%OD8` zH;|<_E7QG<H(@RdFPs0wsOjG9>v69|E=q>uasZ|ZiCm<<ce-e9ievWnH$EI9KEZzq z@IVS?0}1y{00JCdKTb{yST5E{uFK;^PZD^i7i^-;?ym_X2nge+dBquXd}C(`?EXM_ z0g(z{2zw4rNoJeC-;ioZyBU3=qx`KUv5&P9DFwFk;?u-gr0+nmN$O^sQl@e9U(S$( z>i{@x`-Sk{dc7u=G418P$@}SXPcVj(*ETjhAAdQ%#}gaT5D4;b>RcF8Jpv?%@T7-Y z$21abGOB0LUDiS?R0ySsdIgm@rLcjJJvqwgX}Zsf<I5A4lPgGn+M@=4THZm|{taw} zQW__p|2-J;$O0aOr9Ot$DBKTQ(IK8Rxd1R0?U8a^kq}9gO23x)y@$~f=2O=Jius1y zSaF@*D|LIIs-l}-ezH3_t15e|cZ(VW@|)9x_th*cG`DEvnef1RbYGd^*!(^LNfYXU zW8m)eCS2kl#NU$Y*b+p#o?_h~H<;;8)7*mlZL-TV_%Y2q*G*eR%FgoL?{U#DsSx%^ zS{B(;@gJ2mpDaE%*T|taw<J=FO<b8_mi+Sa*T4T?Q|&(*wiu0qR{gDA`)P7+pjiK( zc1iyKR7*DcH_QMd>d;L~%agpIyLeh>`qUP@kfVabW%3B3k`)ON+Pf9J-;dwPtCgts z9>4=X_nDOs5Q7-b0nUM7M)%`c)D(0c&yB^hA~iPmn3P|&bVL%Vo>Ji{F247^|B#7f z?jM6<;!1TIgzQ|GgchpptEK#of<qc29c`^f>deWbQ)E^*hOtsVVrID0RlC(}^plm^ z;{|SK#}FX-LGz#bXM{y;g)m!J22bJs_X~Q(cJq0E3p6T9nxi!cD<I|jfE9HG%g-#s z)BsO(Rurl3u60wVCAHWf+8<E?9Vb;PzSvFUC#J61@|q^<0()9!@!7{VYfo82t1LsN zn1ZG3jcih@W!B?1ApZmAzF>*}WU^7m?a7?~GK1x$`-Jc2A^XI4=8|TmP7iE09AN1n zq!5ohB{|nl#7bDx4}dAaw45`MVhT_u<HQrfP1-+_fyDEW81xp_2z8-S?~xz;Crk+( z#Kwo(Q{*N8`8$8l1~R30_+a1-#b_^y$Olng)!{2HavK=@0UW-h5PBu$SMA?6Pu*>L z%?_OjtH+Xu0qP@8u@t@)rvFPk_0?q6M`9X1uEOpuC1hni17tYPu*En+;B>~}polFx z71rR|I_etVJB&!1^6uno3Z6L*>YC)-RhuLa&IN^bjcRe&&a_Orq)l-_m_DEn$;!7! zX&1Hbnd&I_ABQw~f2<T6#)+NHHi~DxIEpSQj7Y&R=!PAPz)sjTgl!~>aod)n*~Cq1 zlZa>idJwbv3D8C>x1Fx10&TQuqF~`S>GT~@NF+hqk{gh~XVQ`GHvbhAQS5je9eB@c zF2XoXcnGt{y`6q`JWw&ZqZ2@Wz%eFf+jbBu1~<`AG&1DDy4P;zvMNzBy2UsL&8_XL zO<$#hxng3)-kK8ivZ_sE0ey0H6~_?cEw(Q*DKU104d|`i!-s(L9D=$Cq$l+q_NLpo ze|CKF8L^I~RKV2WEGwxUyedO68z)f`%l9%yAEUzJu(2MJ@rG7om{+9b5k4>t+(M57 z{!YFFFSABpo1U8M<fIEJ+tY-of)+yVPr(`NHKEL*-(c76?CJUu+vdb88Y?^c59a1g z4L2ky7LX8CJM4Bj=14L4LmI`X8i<*OJv9MqrE<sw2e$8n8r4T%+X&7NP?dNV)oar7 z6&?)29nXQmNG*$jQoi0inKXL^MQ+lDYlTLpku!@fW;4q0tN%prH1#gE#*zGDqih5) zfB8VQ>rRH-+-9r&wqqksL5t-1&IfeU5ZW+x1}t78(xZylrAguGa+V+HauyzdC+<IS znJF3;oN<QPTXEJM@Ob2gbyx%C@OFk@Tzm*-E?z<CDK*?D#><U=6*h@+(1a6*M?tHh zj>5n(oW2t2EjFNZd7wM{amPGjRu#l<d1S?cfSe<jvdUbvN7F#3wsFUbZ{bv=pT%$u z0?rzZbm;n{V+r;mV+js2W7BR?@W{nj-&KG!w-3ngLNZ@UgLN+N*bFB~EZ#!I`#Yx| z5OwFTbbe(AFCKD0YY$Bzfyhl<;TuXKtg#$LSXgWpRH^G)p-w)cU1}H1@;~kaCJ~d? z*i1beMI;TJiVjsQ4fm!Fx?cLFYAw@yz)9uPy@s^By0~}u&1%B)w#Uj|?mLWK0!6|L zuMUL!lspQ7pYx}L>?h(AzV+ET*YfffavTdz_)?s$X>qAS;!WgX<auD$vl)a6qDC8` zsTs>R1Gs_Ddeer;L!GjI%N0*gsvz1nSB{C@S))JTt^Mza&Pj4RD>_EAc9+Q_04><{ zj|qtLR7Y1PB#ds{#RJNwJQzzxVv5&-1bK#x+%|`{AGp4++)Cg;Q;onH^&fM{x&yez z+DnPozdCGo*^C>wJcb@0sT?DE>nt{LEK7y7FPRi9=uD59mYT&{^K0v>nijt`r(nlK z^Rkc{qi|CKb6L<7<09n)kY_Sf0lu8`s<)?6$#mnyC8j5KFf{g`5c=wyEPJc?C^U4> zwE4?LQ6uKQD1mZEPw8bnZhgJ#;>Tw<(J7JmElPd?7$g14wMP*fg!?<V1tRm3S!*St z-BF(QaktLIeB{5IIm-+p0Cc0wReLjq8XvO#sT~Hi-UD2QX2$pO)q@fqfLQ4Vz^q~0 zLH1}dDlZ>_pj(1I1rMAz=a|Sd9ZbAOQ^@{CmH*pr4GRB7;gkgVL2;4h;BPdWo|@Hk z)V9_^@=pkh-LD{Gm2mIy9%#q7QHnaa>1)H^{%sFCW6JLs&J_RL9S_-RXJ0qjz)bVq zumd!7<p*2DTFRL);X{|q0g7vg@Yi<KwRP0FLW*B&`A(lBeHUf<db`AEHwM}fgd1>_ z!(y#j^)WMEpHP<j7Ie1k4nby@M<bg*-J5=`ZG?Gas!<WI#-WEUjL*fu0^95-*4mCP z5oUXHKG+~P32Tku@UX9!^6O2_c4bve4c+d68#})$c>hoax1oak1eh!EGQ!b`A(HXI zG2}NBvxbm;NNsT?->KGR4Ja`w@C2E%(sl(ONhNUy-3P2=G<5r6qsg9o(po~RK`<B} zNw=t;lLcGqNFJ!B)oXE5*VLfc=$drPMjs)nnRJ}*?l9!X+QT6dz#j1yTj9@&;VbeX z9LbGrh&<*T9wTN%0+3v@-(lkm4ENi@zvt5vH++Xs4{i`UkD!WQmx(*1BvUhrbrymK zO$u;<w{n}}Zdo3$JtoR2J{QgRR~wC+*TG&v1?7pV^U{e2f8eUc9X{j5(tNZFRL!w& z4!r!VOiiDD<^|sIwYV;n6JuE$J*_;Wu~izn;<2fIBPBT?0M=E3O3Ku1Pk9fNF-y`y z45R5*m_xMI?)kyC42sW&bna`D=5Vp|g?YU2@qPW->&r@fA0h;YG73&8h>nNtYa}h@ z5|1g_4_6zcLEgpJKKRu6dO#<tYL14^6onwo;+F?t9HSFe^HR2Ke)MdNfP!xd|C?cJ zt+*xUuNW=G@9|n=Ut8ZnR`&83>IVIhkt!L~SL|^*3u?8>$eDtGZWXkJ^r6l1*Y}te zhU3I~B<Z!@Fh<x={vlE+0~)b(oubJ%CaRnuPC4WxQip7Gc3%AM{F;Sd@kNwLC6uFY zMaAp_-wE#izo6KJs@#=jsC%wY{DPXf1-P)X>2fJ50F%7b>cSrcm29w<5|`?=#$Ed_ zMagC1c{_t#NDxEfR%#-3PQM*#8@Ttc4WDc5+5>q&J<MX4ux#)x;*({JrIVzx98$PL zY6=IzcgS;<S95i7pz^;8X%n3*@7qN~(M$s)xkbrx$jOpO_hC~W;)~7W2t;CYF{$v9 z35^qz0Y6T7B<~16mJMJc_~jHpJs<<V3KI=pXdd#Z_je$*LGC2^L-?BlmS5wv6fH{? zfZ3<DNnAub7ax<5Nwa90uop*je3Q>ew1Y+2-q}rY4e+@~n<&hF1^1OU^x;n%jS?ck z*q|@cmKcgC``|V{+L6Bng!W(2Cm!@~-QQE-srZNPOmV*$b9_Wv^FIHF8T}8V%Z!I0 z6ZxCb^$hmo2TzhA7+R_q2wGdEI|$?djeAC+Hc9#S9rvt+oP^MZ`F)cRaDw(JmdG~m zVe-eo02h~?XY`u`Qo^iY1B3^i0KWwNiOk$OViwMdT!duMNm^iu-C69tUTnD#)9Fn} z$G!{hE}dDbEwxdW%m4iN*kFGpEZqk%F&;~$dc=LNbMM>fd5CKJa=zmBpL$j1njanI ztoQ?_Ev6QL5F1!B8nKrQh+bfx9>rA5->r^*q4pryW;g(YroIi1ra+?|Qa0i&+hy`$ z3@^JQf|%JIWX^rM$K1c}2E`+Q+AV^P-7^O9(V3iqxQ+P1kM*Z)=z0N~KWGMee<otj zv*6|dy4N3L{Eku&b;L(ekR?~)7P%Mo@Oi?>Lvuu(G}vpZN$ey8AV_kMZYdpz-9$Th zEpfh9osd#bxb1eTA?~hif79^%**mQVr4%iLXGwE$n(8bde+&z?6cqS<#KgpTIdLZG zJj#m9=QWNt=PUHPMBFJhBYNmbrYhK=j?3muZkpU$_wK6ooxMVPFhLK<UbDAMa>X-> zB6U`Jlu0%uRh}&kkmwh7<37qHD&X=d?&;;RMG$x4MxT=}Ar7+K2q%4%8DW0}=OW&+ z<TE67yI}Y9#XAcos%E1rqDaGLyyW*KJhPHQI3EM+w2k~&EMOL(`$siWPqvxWHY_6^ ze9gYGEN-MmelklhEU(xq5{m{Ao6E+MFlctHwkoNld>Y3DNN<ugFTVAg$mvwlPF8SI z+iIk5ZM)s{7Am{SCAmU=Z%vpY!-H~1saL2gLg1gy?jTB-BQsP~BVY!XU6H{z+S?F6 zu~SilL~Q1gc@daK)6+#%=|susAbBEQaDv^KPYS8$;)<CL9Q2<h8yM-HCd`Phzh9x+ zVh)R@J!tX)r1rip!nLGer5eXgk`)}~?2BO@VpdFkcI#ht6c7dE5L%k;mB+VWihnAH z+QKVFxM+OOLh8htL|2ITsE(|d_~?#soX*rlFA#jOFEHoeAzKYLSvlD;EfM5@O*<yp z%-!NGSy*DBwFHg1q?RPgo?HrwISaS@*Wa|~@Y7WT=nw5CxX$4F5A`64a#=3y_dL+^ z+BXRWv(oaIPF+>qGYTDO?Di@|9Y)*p_p>ll>f3`|&~^seG&|eC>LUex#E0=+sxU_! zjhsHY(;Q}OD|H4+zp6%5ecVR^_L<Y}_|SHM2;n_gRc>c%2Qo#BJ}&DWo{SV0dWw!n zX)4`-NP_X#gh;07iT1*M>RL$+_b~LT^gq_rdO+8K`e8a|w4_%i8s<UzLIj+tQnuvW z-Zv;esU+ny5qeDzBEe|}D>8xMnOkQp`*IJIK7~7Uzlwd^cd7JuYpg!SJMDM5VV>6> zPRY^l0|<oIqR4-D{*iR6)P{R9%+K)Ij6XF2y9LC>7zu}hQ=K$hpvO8g7`?%!4+crH zgLLrE`}O{w67r;8&ct5tujH#zQfZNdbSU~9(=g1e<BmLzc{Ndgt!T$bU0|XwXEb9o znAa)S&__?3uUNHX6Pu!0usZ1<`8eZxue%V(ycW#3{S5zc(_YdMuxDq4tA<^i*q28E zbmsT{`i$U(6@hgbH?{0Lr@n8J5pBa>_jTA%G$&}M%!=ptZ;-E+`7z4&`TqvLgcW=% zFeX5uPno7USqw68k^&jRX-9PlQiG+(VKCK*y=Prk!zu^JxS9KK{DghLnwlW*A$E}| zTN5OarESNGgKNl&ljF6=Q@u}~Lw&vgOo>>7r65&h1*AIX((XrzV<vYGiUYnS-IOJ= zNPK1H=M!{IyhaUP{Hj64tVwTH#wo+c5fcIXN(5u~%5>?XO$0LL>1m<JbCD^Wkx-g? z1Ac0krPtEIKG#Sw*#QFL+!=?4pD4YA<_$E=`~xIy!Zn#6bai<xKr8<51<549aFkUw z@TbJ;XwI*RdI?tHYmSb8Q=3f;PFKQ+H$urAHU7!OWqD<e72cq|pP`P)!zH<;G`H>? z&dK76Xit{C_){EMbk6e4jbn<{q#u;5?mD>k*uote5C^1?vg~@n(TLQRiNKr{1wl5e zt+gO>5k!X*Sys`>8imb{B;OrClx^{P92xG)tqUF5qLLUMS@n@5-Wj`+5Z;z9a7j!G zYN6*RcUwXt_SjBzj)fr4OHT2nk;XG4<_47`1|5z0tlREZa!)RRXYM#uyn3C>&Y07! zFsO<?+3|@`9TtJiwPX3FUpRT7jKTc^sa>+khuQ5L&=D2#3pp(8^@RZdzF`W^Z3_NY zHe^z6`j})7h0OGryFvv3DS0;rMf*5-cO)M6<sq-H?<+9FjVk1)(kgu$QNVhV&in#p zo%$U*tf)DE2&2IErb>$?tf~_^o-&~aBLbF4sJBgUbQMk>Cm8j!#lZ9l^V$>Sr#j7u z!UJ)vgKJ&aI&f)xT-6w$fv3uP1idUW!Ip=9YZY)|tkI=eqeR7ah^Az%FJ=KM)rXEN zzEzwL<yo~z(_W`Yvm&J4FdY?C#E~~|Q3sQ8q}X>{_VhVsF}zry#>5p+@)09@0IBaX z_ZQuc5z*+ACCC8`;Aozg^{HIG*B-*SHXbhu6{V(R=Z?}ATVV|_{N8-uZ)Kt5$w;Rs zH~qaZAGhUGcGWNW^Nsa+9~Xbb^K$Uv^l0XQU*``<cc5^)*f@tnaA%y7GYuQT@Xl>G zOv1@4Om9d;L{XRlmpJ96YYnIT1paYl_B?TNM}S&Su-Ew&vB<xK;@BGSN2_S11#cZp zK55@yR3%YP@Q$#6diourE5pBILtTvE@?ZR_RnRqvs^<llee=CV(TgOAGenk=Ru8C; zxu~-n@M{oTMa;2CZZHwtSaBd<Vgf(&`nv4*IjN=M5A8jZzkpF&e`HmNI2o93<vk#c zX6P|Cjxh7l{8p4g*zeF%!`Nu2^`=uU$)4b1)Mp*AV&G%}X>%@PSIWeFDYaivu|MC! zR<lC4R_n%{?_Dr<YwsPAs(f@xixT=ae5(53=v0Pa_;!}s$2SxfZpkaL>x2^1lgB;4 za8lB2sR2$AL;TnTP7!S#*O2|LwN%nuopk{bD792<9@$jWzkdI)&sKy?P9Q(3Y=eX{ zO#HPEFwx>9<c~3T!0P`3{ohS5+F&ZL@}EC`Ou(lV%z|Jh`GX>-%>;tr0-RA*(Le2u z=U9Zr2P{BsiSLjcnTg;{&{n`#;XqR71d>INgRY%e7R)jyUE;QKg4;XkYvp~e&nr(E zx>TuuYm@zKk@{Hc8>OzI?eq2?75*tfmU7MI`!^T(lH)vcmFM^I?ET~4ttIvrXG(ug zIxAy}Agny~RC8yJ+Id;)F2M3#RT9ABKn`JPKpv79&<Mg4V!oKQhDwcPvTJhIRLW`x zt@jh9ylSIK>VSqyVR``dSXo11TomjCmKG|V3@eoyWj?QEP{82gRI`Kddu~9PE_!1x zUchTJ!_}1YW&`x?$^IqI>(iz`i_1D~K-9aGFi6W#WqNg~Ac)n*6%aUjrKrDbL0bxu zXgCbz^$f{dEL(EW0nwpAlgztAzj`9&##d}Lm-f_oFpVCovn0dL>XNm2(=O+suv@W7 z?u(jL?7In1qxY7K!dY;F@RW-j9_L}IjkMqX9PzdueI-AdLCv<NSVvk_R(w-_63Sfb zpD<N;vHmHc`ZHh(2ngdyGn~=ex8V}c6j6lohPdvuoCeK+AUg+zbUCWk#torYj7im8 z(H#bj4Qu=s!(|*ylCK!6>uI#8vLd(g|HIWc2HDYt-Db{MnT&1QMlu=Owr%rdY}>YN z+sfGXWNh4dlkZmD?^bnnb?<$;|D4|Y(R$Vr_r;i_3rsy$Y*!S6*zT9tx@l_F=ef-* zH8#f@hdytkb?2~zB;71QPVc5=l{ZKdQEu-Sc#Cs%VnxY5dRQO>LKhoh!6t#v*eDyM zTqX%CzToFFCYV%CEt6?$`pI8SW63UvDn>zB&S-US%$h1XdsBR?Td(~k7h~OGj_jpw zqTvXd)a@X&PjNgi(aO)fyV0&w8^u*Bh`V?@`$h%dc!k)ccn6@$tz^7R59!)<Lw+DM z$wjA!veZ~#8#0syZbPWhzw7PLty%48pD|q#++(`V^nbV8=qDJqT>YcRw9tNW9uoS8 zRyd>1Vx7%)ZTenIjh1Inept<;-yvjGY4`AlHM|w|*Fg8hKY7_iV;lN8Vi^?{WsHSx zxqm7T#Bl1$oCaz*jCV+1!~83sS+AJ12?qr-(Q9pM@nX;|fl@p3DB8?dTpJ8_AT}^( z?RJ4=8@;P!8$E4g?|?ve8L^K+x7mVWtgj2xP6SPi?Hy7hOm+Z+^y)MmUcfp#CMy8J zdX&9Q>%neMGCPr4yG@KOmvi0F#!8oOBW-nQFKYi*TFd2k3G_irj-#ftyhr6o^zLz^ z6MVaA-B&9#;7+|bVcrlw!?AzDCU{4vJD1<bmRG0c0{u^252?wLw*didH?_JsXI6(r zOQt5_xY!#?-`_KEv6IS);?CW3UI+-4Jrj77p+jYv_f#?utYVI>W`!yf5b<h~iJW_U zvKqI<KAH3Tk!@QOpVC{wsd=^iEA*Jn!!2AVZVoq2K%b~j%mIc_0dL`u{WNk#yB+5u zCKZy+?4*>lbKgL4x$;oPG#=9>w1=BOiyh6nD{dE9o12eLmKy5;p)Ep|`gC6yGQO#W z0y&ryw06g^gAjfJ746RJ`RIdI;IPXbeIDrtuD%Q%u{UOj=NVJ1j=4;mTUS7t52Izz z?ol@f@cAh|>dfx@{TFq4JO9Lc{a=aV&!BkO6|#^r#I<|ach(}EHkiWi)FjWH{H5UN zF{4A+at2)H$|A{z2XpZ<2Hf9$l=dQry-BaX^7wzBed$|~w=5dYqlF>A;E&y0>{jfo zE9>I5CQXbnp57A*R}ER}jv9s|tJ8ppusqeJ0(QZx)LJF#73hyG8I#s6SJ|Bk)vJ%# z*kRj3LsN4mAC%%(+%m1Gh=$JM{u+6~j8!yltveD77_d`|HW3>pV&)1WR{e=MwM~*q zUX9L@Zj!7+PIxv1a<C#b`c<CERHZwMqQEp}FpIJPH4=460mE1_r$7L{w}`>FuT@nc z9LSmto!%>Vf|Q!+-0wYr2%2&POkSLVqyL?<Kydbzj<va-uKe*Zxprsy>qIZ8a(a6R z^^!L@Hw5xi=lWZyq(#K@eNVnJc}b_g*6Bt$mZopXK8KOg`@7BbvhRoz(L+wc!wtzU zY5f)0W{Ap?MpbQ)HZf}z@;&(dP8!%f8nA6gYxa$g$1{6`N37M^UIax<#TU(tC)W(u zLew1nw+co1uG~7OI99>$+=AVBeRZrtAx&fxf_vD&3KaAkN9+3QF~@R@i;X~hu8*L5 z%f+iRT=HDHmGvrjip`erjIv?tB9Key4lnVA51I6=o?i6v$Jx=$QQb1@Uud1IPEw%7 znyo7K7lQ_^XA#y-rq|6I#pErFJw?zwJ(F^)MFC-XT^~CJp2M;xl+<tz?WJA+AL8&Q z02#mLGXoHc{686jf2J+uGZQd!aVAX$#s=03JskiciZ%>UFWVFxh$3zdh|kv*tB__B zU@~+u=4MU<D_@YPaG`B9?`dn$t!c=iC6o<tUvz&$eTT^sx#Be;lc1x9<~bP2d^+IX zd~=>`f5@^}1Uc*f&Qh-@NTZxSCWe|f9}K{n9fp<Ur)hL`(x(`_j0XntHB+@?c!DJA zW6Uu`hG0mCJj`M7g%q)R!epV1G3!5thfcxV9<q4iO)c!ex=KU6=7!RnsfN)HU843p zE7f??238GSMqqSOa%9R(kD`O$o`a#Z7w@!DO;Sy_S*<#@T?ZbfWcsSk)L3phLR%l% zt!;<vaXJrTP#fAuS^$l|Vpp2I1PP=<qL$;ZjaWY($Wr7^)rz1xDsJ7$pi0r-T;e>_ z4c&`_&lTi9MsuoST{s0wf{)sP&#Wg}J$azuSZS?Ab1S*DpBMY4(v_5V4%JTh74&8w z-(&l^=_Bz<PY&a=ewJv0hOvPC`ijm<vPRN-<GC`wn=Men5`nzw%j|xfX!=LG0&*)f z@SOVY(%$2asDgtuQ`e|Cmgg52oMz#eDee3qUY*p<iN2Vs!AKiI_HMhkX5iGuRubzh zLX1N0hua8T@7_g*2nkl?IR^=$dQQRBG_v`+DHagkDFToYfPJ>LpSRElU&_X&lP@+D zIw|T(eL7$iA%NX^cq?-gp4ctvl)ry3#S&kr^)+rMX+m$t=8wc@G1&i<763mKqmxI) z(z0-SBd%n|Be9!b8?P1YJ*!N|k61tEjaOVe#BQ0<&Z8Jn^pp{zHyTa9OMMS!&d;8H z21=%v)0tf6r`!xp^n`DtGWlfY{VX8H62fNcNVh<J8U!ZUpNABq4Wg9m_y+G)AHznB zi%w&BX$t#3T3c(GE?hN+>P?Q+#%N-PO15#9xkiFNCHf+?9Y9<779341U=}iqdPs<f z1o>JPDC>y3G}!4XN;$3}HE9Kxr7n5H)(Pm=^A+tKjK3w)1cKYI1SD}~_9Jsa^v{Bb zi_*az<pA{~>-y$uKsOg_P*)x6iSvkIXS6WiSDaqId{?YCixjAJwj!Q-O2B3flwyqb z?7L5CM?-3a?R7?K^dKILW~Mv;Oxf9dC%HR)MD!lVS<h_LyrlBZ-7Rue>seW~KYGOc z4EeQ(z+W{7%DCuu(7CkxPPx28*irJ)(ong`?0{keX<VPSl#1>&xfc~T$|ufrQER{6 zs4e({WoQ!TKGvia?p8^2?&_=ez+^@puInIJd9oi{D4N|l|Ad08_;~Vf=piX5ybI}l z-#ls~ZtqbllpACINP66^K_~FB2d>DeQPxm?Y_~hPwcu?n+^(K6F7L2Y##iZ{H<$f_ zP~ax@QcJXwaLcCMbqiu&tp?a{Qi2c!Y&HJfk8b*`&5HEWiuE1E>7_!q$;nAi?H*86 z^^RG|(&-nuo08ZVlV!HaL`0O5v%%l`CTt2Mg|bhc(1SgNFl0(BKMXdTJ6*=FMGi0- z`-F%a6|FmoNmrX-jLG9JLJiLxv^XW;Lx4vv5<AT{2`>K6ZUIvYVM@qLt#*hSCeo{| znBN_I`~w%n6{$)9Ez}y+9-6fiAw;_t*VAsMjTiP^L5D*~yoKZAy)<IPeHs}{M>i6F z+qB|fC7#=`ke(O`3k3<W9QUh#&MMJ1;6mGp8)(Hwy0v4P$uI6Hj<s)JQFij&=74x; z1uE++3$*kUe%ecB5oh({Pi550b%9~NlcD<#<WF;#kgsw*Cqrj)tC&rGoQYJUb8Kny z%?b>{1DJ8lo--76(rGKP=cy1uVeM~g+#;uI6n1M=sH<)l!SYw+jNdkQ`JKn8$M774 zC#GAvBj1E08F>nJY~b{xcEb}xVSw7HxS`RM!A~H4c%y0B5<lEtEaq{9?V2?9CRv#) z&_a)xuAlhE*zpywT<)J@EfuOS?j2R9{&cZ)^_IqbQ-SyiZD$cJqs{Udel=zZxiNJ2 z=2<5ffW@a$fE+^|T-c-;n7ZMPS#!HGR@~X2LA6GIkGSoQ@SQELSO_^W4-*)n+Gis# zn-%{{%k@k2G-R<_O!TxCDPc}Ws;agQU|k8Zc)G=falPGGqm~R)Ot^}mlQxIcyeQjb z{v@MjaETL+B$0HU@^@BWUdTQYSD5R><WfM|hh!JLlCHAht~ir7o|PDDO!Ch@7G<(| zGZy29e6|231=WlQogWph8N$G8VWvza`Zb<S4DPT<t9UtHcv5T`VdEdPnCSje%j)|2 zVpr-Os1vNsf@3dw`u;J+F?O8NQ@>D7su9a-Qew=EAXYFo>%)5m!n=uY+q05E+^v{| ziT8L$Oi?jmvt}@cAg@^X{0}en()r0J_2{)pi-ON3ipjGA*YjtFQ3rr{^5$#j1Wx2T zd#zEo04{;DRw%Q0S(#9;ag;ob$uXg`p*|*hbh}@&3I44ka<n-jQ1hZk#QsD`C;aqI zm4mPxqUquvnL~un$bEVjr}1d24-|{U{gxuPo2K$JMdfW`i>#<tp}mVKAKo<s_|0p1 z@31rHC_UJks*zLjDWgCWg;uvRnh<K$xQI@_hNwC|A-D20nxuR(+#{nSgb`U>D2}lU zHZgKkXlU{>5=R=xrjj#jWIl09#L<$zT}jjUST{xlTK51?Uk4p7J9_F<niG=f)ZXV2 zNBurHa{)}JEWrU}+PNjyyeAr><B?ldOF&`GQ{b7T-YeMwY&-CE@QMrT5`x<-wwZmI z*2eCG$-*Qw!A^;*k&599eqK*>^bx7hAa`hLV1F%?ly|M=HQ_8Q`swV%T=B`edjxC9 zlU*DCf=j`p;tx$U9rRH2l*i`a)oTns5Mq?fZH0bCwlNE%9JvrlQ6KjozxZRIm<~r1 zjrw$j3yk7WTR??v^7g)thv<tY3ve16agO4S<kXRqyfHKm@2MEj`gXCjo#QZHo4ld| zx6`==)|lkI!!+rK@3=p}|5xbVR22&%^p``u=^+UO0Qr|h{WTM$_TN=aA&ADmD}+jr zn196TsO;P|BcBx$S(qfBEC5DQwI(=g(@QPL9O&PNXBt7uz!3k#`t(nvS3iCJFzgc! z!<1B03-U>Srvnb2^wtFemDJh>0s~xE##Takmrge9HGHHkEJSNy6K4X0P$xhkY7m^( zjWsV>s(+UXG)QJk(VLL0_5$DR-UoF(S8r5pQ%?F%{$WSRDNEoBr4~}FJEO-vr^$!6 z$sgXE!@A8ZKr-l`fp3XmEv0%G_)zvz1GRw^p;%Txisl5cYgH*-JbdWu8enK_AYg&z z=uuEet?vsJRVh^^Gu)Z#MEz97LEwJ@H?t;%hj-uYiAxKv_ta@2YW+A2LR0%h<S>x{ z&6>UMEsB6Nj7H>P>VouGJ#SW8B(}eI+rauX4O;~PfzbuGwZIW7&LVUYhWBW?aE&6> zW`i?LommUK18=;?`q`Qr&}Uj~#E2auKDTKEI<0p1+PqEa@Lj5GBR^xi)&yBG@lUXm zi2|Z7!x8D?ekWY|oc%QlbYT&6+OSt|Tml#~?J()~h3f0hUM&H$?>JMs8-Fa^6>iLA zXxqqi93mxY=dS1cP10wpU({MTqu{BB=lut$RwH02(cG{xi^?bt$b*A2mVg`|uwlPy z22)@lL;^}s4DrBZ8YDeO!_6;hLL4OgerlZq>r&v?fEv4%)3rA<=26`N^a-1KqIZlK zMoKx?K%jlm9#~0SQ1mR_VTQ0;w|HUGaWY)SX*n0$jwrPlXh_qx{SqFE&9z{sFk~Tm z!r_OAYjV{R30DgTd`jw9TN*xD>PF|X&MDkwyEuA8(m8&_+GGfKN-2%B(K{~gGfoCB zOML5*6*`cf!(@q$Y_dFZ*f?G-wVZ#?V;7Caw~il<CmkC)+F*1Fx6I)shpQMbOXFXr zBeP_^feFgQ_`*2W_{2sSH|fX<dIy29ujG6=ZW!faw#NAj$mX`^iEke(*HoT8`X|W# z+j^nd@#w&CXM6ZfD_mecE27bltVBwzz$WRtn4?y+I+oXGfb?9<O9Z5lOn#B!g5WGO zxxoz&%rbntl+K3U^A8aSNTF50Z6O{S_ig&8^qVN+1E}Kd>`OZ1`?qn?0em9;bbKkw z5>u)~d6$_RVAyX?5f)*tZlQMmTZo;Xu!mP<1e|&qVi&a6crk0>JMJV8UymtcC6Lh* z*4p|9NFMM8DZY{oj%sVt2+vYw%!g}H$b73fCsJk`#OE&{*B;>&rbpfDH!y|!zC*+a z9r!()o?@^{mvW!|c0Z4j6w}N9J?rD?6dJ<Z@!QC`KCs$cG8!``k!UVe(Dw+V0`3_b zlYGlQJ~YHka)y4CCA8UJX?zwZJ<l#7>6l(_3mj&e;E7@cE^g9avU<TsP$YmREnYg7 zMlwdypQe9!!N}K8So|z&Vs;tl9FFiBE{`gg>YD1{gN(=BR%oZ|mk_2c$RC4vj)?dm zlIurwSvUz$Fqki2prF2dZWm3XT_C*>f47jRL6Gu)n+e}22rbCpG3zl90l?pG+=K69 zLh+wfTS$l`T{f`4`w#4Y8w-5Xp9zrszrqkriPIq4f3+f-+~+~){=KVg2?X`uSl=3m z$3JGy;0=?W;?H*#{*Ox2HxvMTQnwyBR#Mo(XZjK>mTVq_Q1EKbk-InAIq{4x`<)sY zz8~TJ3o!m_+)@pW1>}=N@XzEV=Tyu2`_s!Q`!CdTr)@mrxuaf9EM^K*)~bX&B{>~& zN}BxY;b=GnLk1;7)pFPO&c1?AMd(>u%_($g0f>tbIK!hSpE|ii{!49&jc;u;8(1k_ ziQ>S(Q#gEktEHrf2o+uwYgek$uLxL^*ob(p4PnU4ZEy>LD#&nNs3rZYyTVG3-0kW` z89t$h*Uxkh1)Vn5>Rf~nZRPQo@CwuC*Xwy{RC3<9I>jvI<w7BP*MqK@?%`t@RU*0N zfB}YhYL%}~&@LyM=iI9~^z)r!)W^6jWQ;)S{64NUiy;j6h$dRflZN4Sb845S<(XmW zKk=bI1vAJ5TFC&5Ng^m535(IWmPEhR+lqN7P7?Xm0(w8jwSLy{_F8!iby7FxThCqI z`A%7gpbQt<pKAYz%+-}wt*r@1#Wr(Xt-k6VlxVMw=?RWds=(W>i6D0sS6CBH!yE+K zm{JQIPkNUCK9u5fW~^DiHyrKL9@uiZv^7yrXsLkRLQ~A<11%3fi8^({<T}43gbGPc zLzaehDwTKXxFp020kL_rW5^r)1nc9n-k=llsnc_Ixz}B?qbkL?!1rcsDr5G&c)o<s zz9-8eFp8q7!|NsUF!3@;^>WurOs@db@FzaLq;ZSvz%7sxThYLx%(QPy3Kkhi)7S20 zV6uiXa!uS~uys&}*2FE07d-f)5if9ZO+~klQp$P42G*f052Q<ukLa%1)~6CAFqp^2 z3r(3lr=8jKOV6fS4d9WupHO+Fl-BAWoyHhRP7U5#{yoK2&Z87kUV~-gVQx;vF!N^Q zyu)F9&5KOi{D1HZ|E(DQV-UrEwmbRa|9<bDHz3)7zuZ9Ig6o%wKh3Xd9HA;^(!Vd; z030Dn`~K7NN+=-ne|rxEL+T|W1cc^h0}enee+YpIA`0b(sDtN^ns6?MS2r(9ozrE1 z4@hI5RHurE#8&~v8%>cE^OO?cF&!T}0@rmO91jnx=ygECiO_p>iS;o;w>WOU%_4#^ z($EtVYQk*WW2p3E!0(p+jtH=&1q9l;N%}KVCM*g7lVP?Y1pK-z-8;+z9T)799Id$; zCl|$Ax0`>`r)1U`yBGdx&g9!&%AB(Lt|KQI{U_fn?H#30zg_t-+};+3y7zKXi2qPq zYzi8<OnIS7at+smSww-MURy|uSDpydtF;9l#!^#mU`suvrLa*pnw{U9ZDgs@%#8NH z!@n2<#8NoLB{O!iDJG&J4$up92qKe^L)C6HHME>EHjOe-dBj?zwWdEtNGQ_(&7b2r zm&_WVl<u^au@pO_^WwJ2dQJ+c(AixmU3xP>4bO4gI2=61p;tK;Iml32{8m@kUVpB~ zN&-?G>tfkw&-1ftuWFmD5Hrx6Iwp{<-r}emNJ~`T#R6+H_OlPp+JYkr7B1sO3NI1< zgvP1JLd9_IKET33A`4krt@*bvXJDaIQc@A`ylh|%5Vp(_Ib<F8KGAJPZSg_ftl3X3 z@Mwd{qB)Ec9&^p$=sQ@+3I_Z`OhHJ=ogSkj{&jS4g?$wTrufzPyNeyl#N1nHKccoB zQ0LXu4^tiKy?uAK4Za_lg^c#YPz&={$j8KMNPm=V-#5JT0+tEA&VzINZ%?3u7UZMV z;r49@6p;FO3J2z$)BzYDn?Fgek8((X>B@Xz!u!K9iXvHG)3c?#WAnkmOm9HdneL#S zMk;$~!8WMNq`9z%VTN~6GDbpirFY@7fg~YA@s4QgFe-VjhfpyiH5I~-!W1AOvClqR z5YO_Ph_B(#rhB+taV4}YBpNli7U4qrQa-e%Y11RsZlLI~_;MnH6RRAF#AyiEoe_yX zs*bP~X;9QADHcEWSE7Wu@R3(dP#!puTzy!y^^tHmQ7)l`Xm%2IW(D}-Ooiy5Srj*( zzL#J7JH#+W#c4+U$o(!_2(1)NB7cjUj}JL56#L2?PD%a>@xOiL#3zsm=)Y@Ag0?Z@ z>*v~%O0oq7NCMCQ)AZw2fT1-)6huT7EJc+7QF-l-@-0TykINlIlDu8~G$7s1h0D0I zo82PQLku(7@0TE+6uaxo&-@HOEYsFATRcA7YA)8f-`;mm*}v4OiW4qIu+SM!M_j>S z(A^V6g@Cwo;V#n^Y!%w3<cyV%F^K4P=K8TFNmOWd0@rPMaiN+B1#C0|XO9RR$3}y< zNFVxRQM|oU*wf@IDh6f31X;)vSX?m7pBj@#otrAHpr+c9Sd)U_UDT4Dv_AG3Yeu7K zj$3t)%0x-=tXR#7RJU2PXh2cV?SKi8OzKVHBcddJ3d-VOq**~Z+511a-42XJ9d=kz zBNK=jfPK-t3=V<BiEGyhO2rt~hhquwofBGFWvor+02n!*aMIGBN!IR-Uo)OjDZ_sM zxgx#`WgjM2K+i!RCL`^g)?i&*B@wHDF)DSZ4)zxgJM-?P7eXw)4ja9361`W=^~2QE zqiB?qhs3<)u(h4<Vbzq;Tj+sbGd$|5YJisN2Bz7SS69I{%{HHB2XjJ_+l6XxHoA}4 z-v8QQyj87S8lHaK?Cf|Hp+>mc(HzFNW?)*o5fnxCO3|JWIgi*8Og~AC>C`|CtUX#c z(iQbk$D~7*n$vx!*Nd`B90)nY9QT{_XBneSK&M71g%g19q!d?oTjPl{&X6^2X6k3i z1b(QP-6zYd24exU2p<kp6j$99>NWN6NiSl8*(rIl`AbYYTILO1A6&v03Y&uwT1sDt z#s@NAn(P+`6WFEfeq;d?4CsYIUc8mC_)F1BMuJ8o^c;cT!)InTr9^m7|3n#w%KFkl zcHD~C`cK1fMTgS_jL3+h2p76cU-h`(0cSeG+J70{&UJiy*`|mDK@;*E&)vnayvpqU z##n2Lq=C4R8`t@x@e(m)N>tj5-vsa{CgoZDRh14-Mk-7Ef&ew(yjl;^vUmvP9(snC z6j3Ryw;}n+1x78suzNgLFMJ85mP4SpFIVoTSb9UdW$;$AQsMyTv()V14Mz1g624-= zqXRnWTHYG0|J)v<*y3-Q|GjV)ApkRfHN2Y25CGr*Ibr9>0P26v8a5gr@t<qg?B^Fj z|4Fn`i;x6j`WagOHCn%n!O?*k31c>&vEpEt-CDCVSwF2<7A1<P#!%!g&ng+EPrq4c z*c{p#Su|hOc?t8e;b8a>C2I8+AqeabD75M?C8$Um(WX>2KgyqMmWaU89eAB)jma{i zMNKZ&hW@-<WH?lQJRUA?e!&Ss9KhS-93QAgKh*!th2N*98NAhik?RV~5Fqpo<FOCv z;|T))YGdmf*GCjIhEr+VJi3h)bc$mwNI*m(EGs|VNfn-1t6Z*fP+nX3Lrrn|gyy&* z69gR8ZLHGJ-#P`erZtvQtuaw;#-c!}qmVmMYb+g^TD^iwd?meDnbpI0A#+x0Jw=fr zcIjegV8ac3yS#oTW>M;sve>Jjco|)2COdQ8t&AkxP4D#e!Z~oc1i)&Nnwr5XSW!50 zI?a~g0Wsn_z99JIX{Scb2{TDz|727{#T3Xuo#Jb7YHXyhFy*MlK5E#$=;F^tWT~;1 zGNZy$CmcAO5o@sNQfe=gCWvxYzR2Ps%<;45owgfMq0LK9eq{;xRBwct%77Z~u8N?~ zG8*3-YczN7V$W@pf{LEWj9Zbre37r#hZO0d%dD*B4b?)_Mq=5-={1zI;jE`}Qtwc- z1$7`V{fcj4g}_)eQ9G8^<Y?}^(dZ<S=KkuB@HDo^F@%y4S+#VO;}y?NV?|z6pb?K| z?ThQN$AW!)6|4emlIWQc><%%I8Lp^U5QcRhK#^W2vzRGo2q$VGU1evJxi9u<&BIQ? ztWd7Kqnhsl*Hb9GV__`vl(C9E&C{Jfk7X;nsd3~}=6`FCjeV<EV=Bk0NS{k5EyMZr zRs++hzUFESc;m?0L7}1|a}?-9vmgHgT~W+2Na>>m_wWLGmOA<vD~8)+Y%`4O1@{pI zfqZ?#8MKohK*UjE9IQa*5KCHjriTZ=8j{iK*=mf^;{Ujhr<70;biy50Yvs&CV^=}( z8b|~~|KY#Dei5lmX0PN#UaOIyyCTp7<oh~THLgvu6eP<txrv)iP73liESDC$g^A^r zsIKNe{c{Q!X?VgHZ}{NlAB!-3&FmBa1zJBVs0%VBw<h4lf;yE48BHrJ&wtb8=%a&V z4auu8a@zw5MGo|t6(Ot-#NFL?<mn0={^<^Mh37_}y@GzaS5P?nG6E8YLZ64M0tHOy zugpPTEbxheXf-1UFc1~>qfCv9zn8z=6d~g}2Ac)`MlZAz=UUt##WFi<iaNg=Kwtj3 z*&0x^XB$EjKO;TRR~v*a!FKkNV;O`3E9cRzle<Nk6W7lqwgyAPctmfvpwLScLM7IY zs;WP%Lv%<fM;@XS+l;HKzoetTps<4*>zYKe0ZYTUL(jNNdBrE@`2z_bA`#;*Y1m!d zQxXZ772RLtLmK%aA?YFRDU6gA*N^T)dbHz2T6mOEMxGi<j*+PzG_=>P61;T^-}Iv% zI#L{G6JVg-tSz&~?*DQR-E2p{)=qxG-RspwO4tA0o$kWQ9d@p+%;;M7fwy1bYE1I2 zSm^0$^ZV0@Ma<E<{jAo{%{=5z3HDJ0dEY<4_-_%NqK6|UYx5|lz4<7+3lc0?y<@XK zk8J$oyQ-=Agmj!nofvn#=T)zDH~$1z5;k_0MKo1Xt=0rWAqu}iTlGK@zk{)2=q+VU z<ApQq$+%Ks=uKx$BZf0<$pk|Z-@YI_gA<1vtdPQem%KI<2RPe+2eW~Q9I>~9U=*~= z?S)$r!<vP-x>E~kATu7E<}fD*A$R_&{LHNJ5}QoygnM@Srl0YKsa*Ne^naUo+O@&A zlWr3J|1>G2{gZsM1wZSV>_|yDPavd8pL;6;@L#8Jfy$`{jtb_-L@mRZF_fbKR9bYs znOW_bb!`hKalRQeI;eff4>5syyCqy*U7L&)(h&92f))2JixMS^2Kg!}b3pRalCqTr z-=_`~=BMC%lsfQmyxLM%EG~5{<)QtL+n*$d`$1rpHl!^EyRT`FD=U#`C}}}26i}B) zaK-Tpq@QW8<d<UtreTOrubu*55GpEU04a<XDpD1O${740U3D^S-#0W0a^{yz@uT6l z2n>WBJ5TW+pEr(QvJkQOu=@`$4Zmcer_qC5p=5sD344tF)rsTlTLp7u!S9K?DSIV_ zx+$N1XIK|FAG2V!THSJfaeQ*h1N5*jE^9kP1kdR-^HP?{zdJM3U#b~DGfeYMjnrIl zzAa}iErPwhUKPA^iAcxL+=e}*H5eW~!4wd;mLY>*#iPm(2wmt*XHA)&&}7S*GDaFn zHzHvw%t&Xonj2Ab-q~|+|BYK)bgOu=CdSK|Cxg|;!%&cYQk=q?VE_X`4E&>YuP&n@ zjQYrJPbt7}m6gH@0h40ahcz=a7Z@x_S5hS6G=}1VaO_ej>E8Tjy&NMO#93Pg>&>0d zM)mg8=cCVFuPAZmX1y>*8hQrCS!lFbfiCI2oJU9C-n%bl)a{kE?<=w7DbJAd5i$Dp z8ouMiM1%~s*kL+j=JdJbPhj-Qp!z~y@TKU%;GK+OQTSD_VOMwy(;)9%=5!sUUSEto zmzMsjs#*h{oS?``Cym)@#iTV=>x$Wu*w{GpGYnfZ62w%E8VVRQX-(-^IcEo>Mfxed zQ86WvW~S7D7iC(o{js-83Qc5!lmUZMZ($_*xTAP6d=;n18fildDv&sL!&>bb9Go_W zc|SgZpNX05<AI_W<;k$ahRTEL2fk5G8HYd~kCT6}OU{;Ur7mfNwE{_jx01pcDiJGA zzYEJ*pEo?@Hxs?h-=xM1jOFE-(ylkUZVWSp&uL#<6^MHbai{iiR%;R+EEkox+dw>J z`X1~Q`<RpSwngI1IDxW?JZ0OEuF`!$jQQI^k1>JxVdppcPa&#!sQv~Vqovwag)HsA zvdlkAk|-Om>ATyLg#tI;J?vGwV_YMOSFwRQTZ>rUeuqapCkB!W+VEWiV^nnJ&HgIE z9C_O=&E-3xf#6qKzpF_!pLppYZIuLfQv<(4jh10m?nc4fZva!~*_Rn9`g%crEY!hJ z5z*%Hw+Ak&{X`PjWO@!4)sc6RPMK5`BPt6Yf^kpnKA5$(C2ef56=sRg`s|w)>!Y+P zdKO*YKpYY`(P%$A({wQeAHHyE5uZM{nOBgyfsQzen>A}3hnTxS6yomrA%=r%8g^Sg z@5-Ib$oPcNaSEKMd2WocY+GbP#bM4+g<cX{uMJR1a04lJ_1kS0kSQ8kwvZf>Okqf< z$uFNi$W3o#;?xS$kIz`MXDl;jGfKx#S5-nBOVN)u*bnNLB|J^W%*&w19V2WQs})oO za|*$zf!TqG8a@J3^GY-blh+i1zj5B{*jR#TOuO$>TmhO%UTVd}-tw2_GZ39Z#4hFy zA4cmP&equv)^+=r&ypju$|N5i2bw*Q&B?a|vT}zdZrNbUUU3ap&fVsaopmdG5((`| zWd)F~Ur)HQSgrxI)*YJnzvGWFyb3!cwaMG_sNP8mr_*5WH|^voO|@f8jZGYw1Id>% z7}nimHG!V;T@&wTWBbN1Zs1e#soRWqNj%tR#t(ZH+&5&xoP-N_-yKd<9|WOn0x`?j zP=TKfu~y54x<eIHdgMT@`b&`zd<{f_^e*Uo+rvKX3uN|lft>|X93*u5uK9f2e!|K> zyB91|ikBMLTK-58T0ZnxUB00bu}{^8Hx5=it-y?O?rKDr-M9X=-P&y^zAvJp#d0Jh zC{OvR$}$sT7hbOm6~)j>i8FO^ryai?@C#TZc;V@E<YY36im<FWPQ=hXa?~QUg-~s4 zDC)&?4dkN_@OnVCQ6f>7WyB18tO!|Ke|6VQ|C|RT3mDz*gbu?{+UMQA5S;V+yTfQB zb^s@k-?Amc=t(Tyq0Td75n^O)7X%Gpq1b43`;hAA6Zuq;R#J1vSUDoUidFHGx8^sj zKW0;_mgLFs&$?4K70ypN8CZ}mt}K+AE_Hc<I8~T8+e5oFK%^cN^JxmL1-dB)mZ_Ij z5jv)tum{ZB5@<>ldn2Z)q_6WYcSEax=7PvQjvevW&rCugj^4BDoqOlC>l>2}J@Okm zURO!y7OBrGv(u#^h$b(Chm`Si#7B94hS<$E*4RTrmBi!c+;RYZ&vx&GFzXPNPh58N zy{eQ27ei5oh8E5Of3&HhUX9=MTFd)Ul%LmFm~!A+srM&ZnDTT4Um`Vj1`hi0!UC5X zV*|5U9#DXhQxb={bPh8u=NO3-qy+4!5sD1HLC5L7a`vs}Iz)}Jo6Stw{o4JNOLr<W zEV6b>UCa1w?*OcO%X<-nv^PjqNT)uT=Y%Jh_ZkzYC7r_CFn>D6^D14~O}B|x;ipOD zMBLn0F18n9OFcb<c2ff!0}f)VaG(d<^Y9(w4wd0I*2i~0E>wJ__W*_|UUj&I?`}Vb zQ|||U(!}CU3EWNf>D2hQCHSZwWrClfprRnpb2CIu9vMBVGCxf6hq2Ob^X=TbH6L)U z2~`t0qdtI=SYAQ39efM6s~SwKZtFh;t%vo@34F{fA36>HgS`~-9r**@`5Bl-^Vo!O z*kp&s9^Jdi5YUxmXxe)8#*#KLF<pP?#d~9)$g%jMUW|<|{~keG9xoNqkW2ZUY_3WH z1!EdUIsN4@$rY!ow6qY>W+s?{%;@E%SY1ym<c|9PcRsG$khz5*KNXn~zkK2QkH5s; zgkI0gCW%%J0Q)c06E*?IZ~93Nkb(T0>5uehkRAp3@<jvmueZ|FPX{;#`8(5~R}=I0 z>Bo$sBoV0n-#~GT0YC&)z+OasN8?ov21mwTMl1A=#elB9%!{Ocim=KBvo#N1-{G1d zTBs&Cm#_GxdpN^ljm&Nq%O-{UEUL*K*oketvL#kz&GeST{MPUxSv1G;cCkL+C%i<5 z=lO8farpF!I#zqz?ZV%LsK&=%$OX^S^8_)_!(bR-!W#su)gJ+V`Yc6_s$>JF>|hD# z@p-w~`r^@ZRL1@9b^0l&W2C4_12-)USeNap+TX^)^gNO2D+<nS!sh-Iy9y1I!T1I+ z&Y7Ld+h#9LMv*zrH6lGb(#6!=2osrbrI^hz!a8q=@$6$fU6fyNcs=jsn?+<gA;|x> zo30=4)dNRgQ7!|L(1M?D+Yu`yGX^5J#!1{_1~7W$4<mAyb#GclVZbchrY6pOPrzoo z_ItKX<hJlQ56|Q+JCwS*uzD6Ug{iMMaubH{BvU!oKeZNCzoChdpY>g;&|US)D6EpN zYD;Uxk>D?-UFWWpISnbdjv#}8aa$4P9iEK-MPMppOacW2@d(rlnNDJ4a+H{gA?MNO zNNML3Q$hKiBE3R|@f7ABC^8!D8*Mb6?%YsJ@=_qlTY`+Lf5-HzXIZSz3FyW8U}O)o zC-+%rl1UL>2xD(qaSfQglh;^DZ6pszt_vR>Z&rs~#S=Zg2IDQ%AXX!G>hyi8H;lgb zboaiMmh1ou5XtC9hA0e>)N-cCq1O&qTcM}_)Xs9eIZKtDZm)F%$Nqzqds33FhpL); zTFM~;vhrPfJ^G|oc;?y1H#0?df0R2rSEL7`2~boEexus8HFjDzphzb|Ln~X{RA^5w z>AriJDtoHl1aV!r?oP<;%wu6fhQc$aOxmvPwKNP=_IvY&RI0<bg>mo9CTPvYVK!DJ z7Sxsexvg}aUEW`<5#pAT7uo383R6CRzsFi<D3SxK_JCPWQd=prqb9)}J7h;6>ryV3 z*FmhEBD_+1iXeHqOes(uc9`u&XM^TH270f^XOPY$q^zNQB-rLJ?3@^<OlHBo<`Tv~ zpac)x9I*K0QVI*KaMkL2xC{(rze)`Kl=cSMShy5u|8#hKPks~yf<DFuTIkaU_+f1f zr27uG@fsJ<4C&~WZx1~}Q#%weE!si_vEJs&^qKLv1(~x?U`_6%2WIV<U~Tlhe0nX1 zRMw`C;2VT5T6Y)a&^vzLtj&|r>Apu)IamWL8Z)Su?6dAGbU#kw>z0-4y}(=M8@q3m zE(Sp1GJ;m|EHeY|BnNHN7uL0KEOyKAHfmm=L)nfc?JuDC1zc6@*GOrKlx<QJEEhja z(s7A~?kTwYC#m3VDU_|U7Y;*Nj~ZV^N<}LvR5_(tJIA(<PD=JIYQpe~7i2r!7+HXd zw0{cp<ejJBEp1qpb|{pTqId`y@`Uxe(cl*s(RM|oc{i!OY|P8Ba=$rDI4cSn33-(4 zr_2pFrjE3!Ne`<zO?t#V{b77;$(68aoIQ(kBR1d?PMI1`!u@Ih(q%}~0b^w~e2gsI zWMy>Q%UQ=D9O{)mr#w(4$o^E}yHE=}EqvX$B(e>VKE^CfSGw|;^wnO?Oioq2v^Hwb zl-)OUR@a*J*u3KgBV~E81{+|k)f8&VocL-Rov--nqqXJZsZjm2;h9cC`#SK2A|gV+ zKDu=OqhbjHd{qp-7xH_Sdi$G<><(>)M6ueQ2fVW_d@O7_bA{$E9X{Tm^bdL9&n%)6 zwtfo2Vvdy(IYiLWx=R;xL^gzh52$@KPuz>S4@AI-?jbE$T&WAvT~g$4LyAsHtnktq zS(0jh+2w*eC|ju<Xvmpy#Ds1=d6E1u9S8UXO&)(ON68pPfY$7Qz>?G`c?oa+YwSvx z*Y8!q3wnEc(FyKH?kZTSeXL6$Maj22+bwr~_!y0&rB8qnPdVxD#5Z_}vY{G~_1U3{ zO3jg>2vU4@B1$jCevXH;*A(ZUL>p9F;85BenB@?e;+FtGC+Y8X_;t#_<-RZY2Bv1I zo|>&rgRBbn)JmU+FWu*{&%Y>W4Wjz%xlyd!cW;42mr5H+=bJUbDA4NKG+xsZ9TR~D zR)iLO_L$nrT#WUk>ka)kxj^SzLyUUo+AqXA5%UL<KJi*QtalW6hb2vaf>&rP@m`Bq zCs<Q-nh1v_`JUOMDuy3;tXPGm-{u$LHA|Gx__D&<wuA&ETt&MN<1TSUh!^<+m!@pH z#(zL!91%QZ5k;Fk6#yM0ZFGDhs`LX<uh~wou(}*Jb}t~OuLuK4e<wi+rZS1x#?Q}4 z97V;H8c+?EP?K4>*2D)3El4Fv_UD3i2+Nb(%9H8G9G~eC`94PY>yYfE{&&)lycQ$d zkz0|BqwhELYruO<AMB-4S7aIK5=NC#oMm~yye}y}-2A!;>^iC@;sfi*#d&ZSu7fo9 z;0$}anoJ}ur(M+abDZnu(iB9=L(Rx<LD8>+`}|A0&PY!4=d^0$E?4L3Jp8u7pTa5S zKBNSb(7km*yaNX2H)b&RX*E?QGc=xeQL=Oe#%t~Nm+EPbC-qd!MlvuQJcL`34Fab` z`whmoF)_t~k{j~hS^ZN4e44uzg`~#hdPNw$kv&*Hd>23A%|`H$UN7^(D<epJOPF#G zo}q=DXTcD}1NY=?-}x0dQ+>cqcQkR}H09J%fMF2%RP#;WQJ}B(AtD7qW1`E-Sxbd~ z3MuwqtgoDa`|)5lBjvVs4YD^(|I)b)5ydPgse{-9V#fOg$##=TUJXuFAK^fFg#6GP z1m_82<cTVCjS29^bf9TWb5#Sl$4vXH&E_U6`8xiT&+Qsd`jJ_L+SE0EGuk|7kcnO- z;>$C2iX5lJ6m_!(D?9blF3l@=3#VY`6Hn!1BYeaxH!4G+l>;Q+n(?p@Q{V;~x;EU% zoS>!xhgM%!jOJWhGbVP3%WdFvhVJzV$133W?K=Y^ykM6!3@bV!GpOr?zZbH9Bf5$t zwWQi@(HeY*G4M>dvqeRGpIr@F44yv4*rw+}wowWXyh0M*bq(Z!*&kfui@bhnPTyqx zh;d=&{}s3|G$srGX#DM2M6_aG8+HxYHTY+V1Upv}VGqtD<)iBLf2+nwhvmGFM1LmN zr{Uv3bpCH{-4q-*Ntp|P1B}oGiMSOqE0GsmFO+B_wW&)(dnD$19%$0L1V=UL=i=IZ zoWT74*pszw^JD06W8E46uP~fU<}$_mkm-<lxaJ`95CsIjqjqCZ$vNtMvhrXVI}^b5 zPe&{oxPqeZF;Evd$hT4ixp8_a^l302HIuNy$~&liwHezE4Rm2?2a;zWjv38mQeZ_I zZ~?Ys<tkDee;C*5(-*;)$$v5{7C7eUrya-cqb$%4l`2`1^IFAA7MY`rQyMYSebZt> zUra2bk|_M8nq7rnUaK?)jU&TXQWwzU=CkE*KhJ2EFmc>4n`<_O875@jN=6Ny$6>RS znC>yEYc)XRgS;Bh3OsUkG@>YL=lMhT4Jdk!m@Iv86IV9a8bgP*zkh2vanN`*G^4pu z8Y@`uH0K-~Y&OD7E1aXt<&Q-Z*I-yT5ZvNezF#Y+Q71)N#YjG9?l4ZZDEUIgECjhO zwZo{s+<sxbKsoW5b>f&{PyVCmSv0;PZX}i+|B5eGCQwXM2}})GadKd^;wY%cF3LP# ziakcLEE~lnKZx*Et6&s~47-u+%T3R+QbmYg07r^K{)xzAk|K27wm{VdF%b^47@rwB zt12OEcb?OCSFSb#vpB9kQ6}gUH?mMYass1NnY14f!63z4xx}Wy46~@Zbz*I*x)!e1 zbp}{QGdM7s2AT@xx|~v(i&fplrj^him+rmJCu=;vrAP|vdahPp7MF1iA0Rcyl)P{g zy;U?`{2B4kvTUq1Mjv-bwB+rGvh`FwkRHN2oBvF`p#|Giq@|eqXid%_itzWvennPs zqA?H>!AIhvDL-i@a|((v_OOW1|3JjdF3uxakJxs@1*(Ogas<o)3@V;}%UYY4HBKr~ z4fKZUCA<`{5Rw)=Sc2d4{M>x9?WmC<QyRhbt`*QH-utL+NmIg`&LJ!4WLPS9JIS0d zTV=C`fg?+EYzpWp^lFMIw1%_~^U4;RE@UjwGw5oYwI@7mvI=F2iOn-tp_}YHkNgJR z>Ed|93nb|#4@;3xDmURH)|K1EjO0z%I*7#eybyN+Xa6P+zwRe`w;{0QVYF5T=ZBNE z4=ai9JZndOZ-CG0RY|Cl2^voV3Qam93o_b{pq>3a=jsh|xX}9-#`d>I#O-o2ldI_i zmfS);mr|p=lFW@}vYi-LN6;9q+zu!^9)D+&5TN0=zE|!L#DJ4h>;aJk(iKn`CA7DA zfH+q$NC2M%J!q&-VkboWCJk@laASscQ1RT63;sSxp>^^KVx{nLOw#J^J-W+Sam#kr zw~0V(j6poZ;ut*{o^lQ=T@$uv*jtJ5_Ps^t2SRU=qdN-cT@1M>JLg;<oyo%_g>wH` z5@6Y{SSGx#^eK7~1j=XlxhO-_!b6ld32(^Z9Sjr13$s4Ea4K(0PP{IJ7VcgX-`AC< z%NwWwwrRPd>B`<NnLY%JBVzD3-_!n}%*z~d&m(Aj_uimPg1tBx_b7yp2|qeRg<IYC z!F8;xJfy8@V=3_Y9DiZ&ATsN`U69LdMIa9b!ayJPQT%*E9^ngqTrWA?E#=KMro(kh z?qw-<cM(G<I+Or~SzK?iNKOWrwjzpiTyJz~C5p|ooDktq1}@aApEf-Gl}F}xE`67W zrPqmbCY=_@CI9|QT1T<@>Y~<&?BT$HVOmpS@df!<dCk>6*p4!3i(f%7Yh>m0OF+!d zy@too=q|E+Zkl}?*V|2`3m16uc~wJ5-c-)^GxTMD9m8uJzuWXHQ#`{U&OFmmU#>D~ zsv>#p_NX^Q_t37FU^jUau*1vHAq{OROfjt#Zg2j81g6TZ;|qvPi0v7UsKr!_u(nlf zhqrbtayQZ1dGO{7@760iIKj_A8SDRr!bwxcDD=^NGJPh){%boTOrrY%zz13>e3n4H zt)bLvX=r`*{VL=mSfS$su4fm1Q0L3$L92`AAvS0yQd>$nGpE7Rzd^r$HF;gb)Zk*u z+kE4E|8kcuRS1eD6>LamGS%_W&U={2YXW@PJ=FO!-WRG5<&wNM&lrx7Ok<|H5Iuv~ z!;9PT+57WL>EOcRQC8%8Ef$b(mho4Vll-{Z@ad;!EEeDV<&Ul2Oq!vlb+4iW4{>F% z$^%WCA;%^>>ak4~jly!J@%C$%_48b<I<G|uSix7VHnc3HWLW=#bFRX*vQNoN%DU3_ zRIQr?14@UIi#F8b)depZWfvI?)oR?;F5*jpTx!^5%Zr+|IAb*~?gL=ppXEvE*0_ub zw4di&WMP2q@U{vtx&up*ig{{{QR?9ergOM`O^L*?**drNH8=-%u<-I-3VMaahSc2s z3G3*=B1(-MQ1OOgCgzDfh?h6WZ+t0wOk;-9y3OF~KApUc?K_sRw<H)Edm1vz9NqoK zVVbf^6bf9mo}xafWi&u>VV2Ff1(#9eWOG0BKJp&Ge%|ynfe}Ats@bV|NDBF=24}V6 zJoyVwo24~OJ{Q{$cj6+O<LZ@WO88TN+}BEqF{jYcV+K%Is~;ws-WexoS_B$XT%FgY znM7E?V_L5uK$&y+&YI>|F!tD_dE=TBZ0q&9-=z~_Gl@bRBSt{M`K}#Rqb6aa->&>D zmKYRUIX;N()>}&L##>0C5V4|itWKelTgpw-OFE$=CJ=lgBi-mrD9Jn;LbdNnfSZqs zggBY64wY-(A)P7M3-6=di>@RLUI(cQmk}T#v?uvmtdQo5A3Jr3d+O7Ti66k@Y3x;@ zUw}9MCNnT%SutR`&Ze|8L(u}#AqPdXiZtm;XEpgOdhyY0tztysJ__rVrOPhea%qgk zgfYb>#mtQAi-uNyT*kx8Rip{10A2m)>~S0C>2D|qRFm2h*#6a3lcy^MKFdz@(0R=& z?oKT^36=z2Z%7rMC~BoD?vreB2vpRQjyO!(GxrNV_A#LGgp(;#1mp7-Rv~@U^cI1O z$gj-bRBl1<N#8)7G<Q0K0x5V!6n3Ww<ZC)jpbF@8LIMmrYD6Wue@@N)IE2qdoCQ&~ zOF80tR)tl$MEhoIsRg!%Q3;_fe8pzR3-a)F2MY2NW_t&*#=1iU@_>hg60~)(9)9W) ztM?>%0<{e^xJ`^iq{Q_BoQy>jdHcHhn4WOnzA`>bAd4_8$PZyq%!nO5Bhv8<4d5eA z$h3D;ckz+N^)*Xv7Wx^+`e?!un3yFL#X+K6a<zDiGLgno>Bn$AGqE~Ca7p-t%e4+N z@@K~y?sAbq|0X(5ONRMnoN9*gS;e-F?!r`$m{#K#aL8;PT$bW$Llq5tCs84U7r??q z!h-M^IXWj;{6j1?#gIY2BefG~Y6BIqNn>RBb*Agf{{~_N2?F;2AqH*40U`g8wJ#C? z;(rKPLK%QE0Q5iY8+SPX=0BK5q7uOK-z=&cKpOh*tg${I;NRGV5di$(6Za;7Y_Pwx zDVBg7(7#u_wt&cgW0Q^m2ax~9k_4Rr_x~nc0J#4i)p7%%|D9}daR;b_eEpA_>16m+ zsO&RGc>3kP1RqHfoGmy(60s+s;V&3?)e}$y_4h4N0f44|WKCiC6iIeyU%t$7{ztrt zkrZtQ{xzvP008wr_00wiXit=R%#Thfwz}nT5<w_S4x#vHKXAF8U!ojDJtX=_*}=45 z+u{uM>e?nRAgU{rOL#lfH8jrk=nGtOSfP_)R$i(f^yoFBHX0r%=Vw12jPEyaWKW|$ zbs=sx9y)hFaQ{p^Cw%ZYrU%nGA8V(MZrF?42WUD1Tha@zQ0PCajp8keW<2wK^V;s@ zS+rAg?TRCF9h!a2ESZ!Dd=VBMJYgmlJR-v5^RAhLH>`XjyWm(^Wmi8Vrs6OUE!u}A z9G1%LGLjCicFc+HB`nOq;jfrlGzp@{I#_zqm8bnYV+N_)6^E#)kF-3+z;XADTgqeQ z%2+#rio2}Z*EdGm>b(~Gv$<DX^t4H=3m(TrEjE|4FdG!i>iZM>vnwRj^&>E8M)TD9 z^K|rFR7Z>#@nZ>CHZhrHd$aoihU?nT*bk=dN*Ajv8*~};SF_0NePb#&(h*fxml7Sn zN9f}wwXk$f3+UKbHfS?I=qL8F2Md$f9ID2F=g4n&W2AcpcpG_F+7W%)E0Z7*knK3z z#ilHUS5Ed#?Kp@2^|4GI(|s?77v9I51iXtLkNdMXFcCV(1yGx^QseBO3fp`X2HF)@ zwA!IJ7A)ShaT{nM$qUcbh*4>Fg|)W7p;FQ2!tZ$pv$qkjWB9t<S1Yy4^=APkJxce$ zWJ4m9`=+2B=ysYvU+;v%1!tL3CZ{|)xU%+6o>|KlbZ2jiQBOX_;cSBtLyOrn2ERZl znn)c<tLBK}LwI-y7;`jlK;0VjU*8wcAF}_P3EsrOxrEG3JdR3H#wkmQ`E~q<$)nGh zKo3G=F78Ke?k3z2PEJbUL0tgiTxbmp&`51Wl!!&wIW5F6{J4E>ULeNbj2{N5p|Zdr zS+<ND32_hcOqg*qsMhL9K*}(O6LjDDS`+F-rh_JtNSmnBfs_H2@s)W7f1rDX2=v?q zgHvdky%_CS@+-rYaWx`~3;7FIF25-XRAKFWjwnS<;PGrLqinRz<4*yt4p(di;IX?< zT`eyPW6g^7+zF#|P=Zk&2yy#F_0Q`wM`h!b0f;ZVHtuNW5ig*|G$39Av9p3eDD$$c zFCd}YTY-tmOoA7+koqMBQzz0GJ{`EilDrnLToF1|SJ*7x#0n222@DYJL$VX?sw|0c z*wEN8s(gqr{TdZVKU|2+F}=3_4^`(FoLLjC?Qp`0ZQC{{ww+9DW8xhr6HaW~wr$(C zG0`NMlk?8^<2zNmYIi-=yI0rqr@QX8)^(o(U2QPmter!e(vC1_ahUdRsZ!FuPbIcL zfgmO8h*uaCrI%wUOa<Q%Tu_4Vtr;zXES!pywS0KCh}mO6$zo=~Y=bx&q)``{7Slt0 zzbH|4Z*dQJQFJG>&~(#XiAyUsLD@}F&uZ0v_~ufLt(6QPtVWg@9~{4B@@?|Andd7! zkd4!?N{ZLST+=Iwg<8F33)Z(g^V_UueBhx4J9acY;|oQem7%R@S>rAIDD_Va9upy6 z<}9jmN*1l;RVa3yOrj>Mijh`w)?A4OXsEeo^(g!KXSb;9nP+*37C+PQeQ4)OrSh+} zup7KRsm7_bHOM25<(IkN{=Pb}k#Jk@0FfP5MwI?zbfiY$EVzq@wUNdZT*fqAEzBvT z$_i}wlw0?I!%&U;{ygl%1!Pj9{p{ZbE2A1AM!Mz<nDzOPjchoEix!^ly&W2f2rv$X zx6mkj_9tl8$qh=a0z+%4mQIs`q4KoS{6SUs171R(FlrzkJiLrE>##TZU|xrx8|W>Z zSmD|di7(2(O=t6ErQs&^KUgwQ)L^nL#~uF)_vP5D$6XZpn^$HUq&OD5So|$X#Bw%* zu>1iA82`JvOLl=3Xm}#Ysj!m<J6Ty^+aT|9ZXC8lc%IYv%ChND{O&j5RWr>b$M1w7 zaAYsb_>w8~bD({z9)gF6fk%m0MSxJEUL+fO>8C|NU<rqbP>kX4)SnaXQP*=P+c@2j zDN;`)950^v>OC%d*oqq{2*q&saXgL%;fk;1LE66z99_gwD`!o}QYP0jF2Dxh$v~gL zSFbVA%d6K&uo)_%k_rQ6B7F}gYFgP*nBsiiu{~1Eme3oc94>}-T2wXtYXHk{NL86* zo$9c&U@E+gH><7@5om{M>PtlJLJO4{9_+#Q7k%$^T9u?KHrf0{M+kON>FVm$i0UXw zuMr8`^-hw<7&|!KYWwZAutz^k)b{ElrlgklSOZ4=H4*oG@Z1Q6lze;St^7Ro3>QoG zPVC@eN{AUCuPs=RWx(bNnE}#S#01eYH*@e~Mikn`DLGZpfRChJ|Ba~4g_cx{>2-pH zCEKLn;z#r@Fp}CIC2+Xl?SVsmFk`F1GO0}pDJ6BZLCoOt!aqTpYgkZIaTLt4TfYBb zgK4(@TFNhJ)?M>WHfKKbp+WwSMYlZEiD#z16lV?(|D|5BJm*}w%V&Q13WP2ku1V_B z7wMei^o_%}U7mAxl4raB$?6Rub9?j}?1#tB(#xhdAs(sVtMwZ}=JxotG0zVaUe!!} zxn`!xX+=vF55$jb)MKa6pnkJ#WIGs1{#bSs;a#M1L=W0Hd`293Qf;I11nf=hOZhLa zwuoampEV#z$^EXkRHeZEpBM0z=8VxKP@O2&@&Z3P0f_a=2ys+LRUc-ZGIvvKpe9@} zXg#!irSe|lIiClJ6s)?Ze6E?2c#h}!B)%<pgoU~i?B?ST8f*V0L3{2)J`ouBoR&to z^um=r^MdS~aGJ+Tqnnx(DX3tDQ;0@M4-J(aJ8tg!fye(Y2!tmx;1-+OFOYgo=GZJN z`O7{YeBWi+J-z1v{JTr;lLx>IlEyWj%XtKcBwRnzsIJcEUcUe;$M!!TyCx}9FyxCg z=U;f=^fJ3CV~-dgG%~xV2}MjYZF~yu_us$|8c3)$i-8$I*AHSu>o?L&U_#LU9=+JQ zzq%*e`oO{x=+YCPjQNIoe@UXTx97|w*ef_0!#p@noe7A?<UgdTRuT^YmG|*)Xk+nv z8yINBJc5%7rw_EL&+vbBj}2}ZT|W|kX)0zw+!<&qlt|QB@#|D-NsmN5nVpGAkCB8g zV|l-Y0Oj-6Cr92Gk(wh07Gj?Cn-#a$F1|@X_53+-@|)*9I!o+|o{N9s1@TQHb4$p6 z;Z%WT0KBYyK#}L~&%cpQ4-LxQQ^0OF$=2IsKd}5l_@)r`xA5NCTe(>0l%oi4=m}LS z|AkxY?Qr`AiD0;2@eYmO*f=fDYa}WD;$n;n>P1UD-S5(<-AGlgxV2O6Fl$eO1eGGR z77PneaSgH25pl!o(=*-5s(U^w%_KRyt1VNw=%;LM7Ea_5UmR!S6l5&_s3<)Q;4E$u zsMeIyDHzEP*vJM4F))<NuF@QaHjPei_<n3cZAn&=GsCJX5knqMTd6W!W7R5)VWh3! zT<=$Iq~^UuwrUc^3j%31u(6yZ0Iro#A5sq(8tAS-CdDH=1G0;8vbaMlrcp{%n2TVK zG}GV`wl886tf-4egIxs~yV!Gm9gCEFDxQv0t<4@UkROP@3^=I|$CDdWR=P<BLJ@PI z<$4_^oHw+EMn_G+X1?B-4Y$%Wzoh(0z3?y9OX1nV3lk%ShIYuZ9(sdd#!lP6qn8JA z{O=hOa%Cr9Y_g_C@7Wx>3UBDV7QfBl#24JCQ+wSha?vSX4`DT4+lXq`$daaLQ5=s; zS?Z<B>irf(i*YhbMQCT|Zc+#~=Jq_}+{--r2r@deqQg@{oIeZaaaLn8MyI5?;!@c> z$eO?urwcRS#jrRy45ifO>SoI>sLuxEy{WF2Jq$2{S64wvDQ5G@Nwj~>`>fwM1-CBC zlF2M__5`Ttf40R=vOIUElDp36eot~l{FOL~GFvMXVCf_FUOv&mU>UUUQ7m5C^F?)P zlTu<s$5_~cmX-M&E&xN}<u4w41G~u}s+h!*Ha2uKL?E%@1oXIWg<!Xx$V&sD=)EsQ z=O`p!k+)kD?uO7R>uFP&zJdG{RDRm|d12X#k>o)_Aq@U-h#GfQLe@i{%|eP5?X+N8 zzzukJeuO!V8}p!L?&p53Gydf*jVSg-35iut`O|$-9n`6+g&pi$Fk*>kGtM+*F&V2< zfczt9tArKaA2fB6P9LW<-26G9yK2TWKZ$>5js$Xj(O4{jBse2$T)!Uzp>Ux|zy>FY z>h4041<y`uS2sAJ1x)<o8bMx=>Ze@9g$&x{*E?Ql<nHlEiT=Q|A53{(el8KnD{z=A zNa&asUoWQv6<_ClnG3@~Gtycu16a8JCgUFl2o#$b=$Kp`gfBkC-m^&nIP5NZuS~5) zSQ@;`9(o5xH$A@kC^e+}k)Xzm(x?|*sAuI?3j)*w>4{<%k&M>bg3iNCp*|!uf^5o$ zQqNIO-!22!uw8kV(FMs2Jhg`>tT1O@_t5In#tYk}bxnDa>vWtgS7h7yxFz!3*lwE3 zS)DZpPuR)&NeI;8O|sa4WX#xnew<C(ZWd2EomvFY^_=vDr<7+AFB4?j7<MJ!7bi2u zOY-6D_LiH<F)b$I$j+Lb!pVxSWlISHTOWUu+rr?`fA+ngSX6~)I9E8jkFSzo;z}GV z#ucH&MxP6;#i%!Wn-LWy=U?V&?7Sqp1fF%r>K9&po-wCSP|VlBqDzETH%0L_`LGoC zbano6`6KR0)R`~sX3@!}{Erge&%=x2p*uCqJ|Bff)V{(usHMv=O!s$`sH*Zd+qc5s z`3GCfBBr7RiwcbRBnpkqt9k~1iCVp{49l=c@NIokrsArgBx(7-6_QI>hQrUw5EP9W zmi5`5F{ufPm?O>t4Wg>E7muwma~G%*lKH`C1p;YT8`c?r;Ex67TmNKuRXS&j6}n(@ z*%eW@*bS)ACe9zauYEID!JRCe#99@0U68_@O>}{@?d>3#=m-~CkW*Tdw(p&g+sTPm zpV!HC<=je`-F)C^$bs|Z-4rd*HlkIhuT7F^9FQk3Bgdf!3cV8bRPy!xm9B-nxD&I$ zbWCx`**%NJKQBO=k%D=R-mtoD&iY9Do5kGX-k<Q(<gL*16e`fAj+1WFgrgojXI=@* zErb$HYby)9+?UBhL{Pnj3p>>$YWyNdfhT63<^-IWS&@r}G4H~zIzygn;i_Z8d@%-` z!Z!S_I%I8Nqry;ml(}q}D`wg`5(g`9I!>K*o>X;JH+nU#+B+o;A=7hA!xJlV<+U4x z1r_@s7sO^!=00b)upv}^PQNX(uci-#4L@JF4I!+_Z?<Lb4|~lL(Z^OymJ>2<<)zLE zxJ&okKK%m-egFyI%x|bu(V0hC#Xn3w5Z2TE+JuEbOs_Jn;bF`?WlmP5<_nV#3I@8) z!uO)iK4IO<+w4B~+A$KR%slH-UFyHWQ6X$;`LldcA?!GyL~K0LE-0|3JV|vR5On>0 z-&n(Sc4}DCP6o=6oJgO$q(-&v6=Y<%Na=SY8-fy}gJcnV86E@PH5HMXz9%GIV3Bvz z9pU5xg>`0unl?d6u51YNkWs0P#U<Fyvo<+S0uI#Y)6Jy?%LY3Yg18TYa)`zh^Nu{b zAeRO^>V9tC2}z<8aOpA<rzC6)c5X3q;rPD=o}epj7{-aj?=2CG%w)!eBa4xL-9oQ0 zaJ!sIftonie=*Zw_gXn_&7K;Ru}x9mmWlcb%z=<Gx+=vhYVHRpTPb%Yn|k5~Z)o?r z!u1a|&S_JQSK2e!KO~N?e+`xAk5c0xkRo-#HG=CsJ9KEXYC{xw0{P_u&Ab=5fI#O8 zj>+p+px2`a{VX+uymBQxFiCu}kJa#KDYRqw{etVOsLb#QtNUxkoJ}N+(0IAxMK3fW z;O?Qi`{J&>K0Lo9P*roTOke8-BaYy|cHCph-tx@n1Nxi+H~g2x2d3#X1kndu*K%iC z73+!0B!d4^4KZ&x>m~7FqS~nFyEk=WX5Qd|l%aQ4z>8B9!TgCeLJww#5GK_?jB5LF z;BfUkC*H+o6gxy?eJrvzFOEkF0}#XkxN<o4Gc~f~Us)vFR2HNmD6B`$=ACONigkhO zcfpbQ#uUvy-99?*yoE{|VD3fXa$ls6$T39T%ru{@r|^PzZujENPF^$|nFaPW*M>-{ zh<C{K^T3D4@M_V9qy}8@@7#vFF)jHf@LUzvi<de}{7xioIQ#~_o_bs+QBocV;9gTQ z*33ph42G`b67@ccHWcWEa7NB7p^6x-=Qq3)q-irw8hVaGUkbG;D_Gnal5abWFpsw` z^E?r_cB6TWbuE|tISkWmGUMo%|JEIp(H&6z7iZsB;&{G(L8w~-{vCWs0FDOuR`ts2 zv<8=RqoFMdfCmFa67LahC)^M~1wcxXp`4}*F28Y5Oi0wuN+stX0zh=?25q(HN)3fM zHTXs@Q4k&3z5c9QB9Lh35aFASSIDQ#Do(|5tz0}QE}n_POGp?$lEZngkjqa!`#llg zvl~BFuDQBP?YUOl?~0(_cbYP@weu|~^J}Rek^kA5^3U%QmgeThD(Z#6jH)Ido`}0` z-B&jL#Tnjn<~tnb(b6aU-ZbYP2`D<^C)=8!jUT-zP21J}SwH#jfGj`qO!XVBhE$)0 zUX_yg@)F!jTV1(z4;;O~M~13gYY&Rw&h(duT9)+7qF&QI-7<<&VRk_DlH1}dr1mmc z|BwEvzDaZ%1n7|d=p+@uW2DHDiT^A|1;2vm`u-r8wTp<m80GKMohmi_hHBp1BURe~ zM*a5)?YG|<WYbczQ&@fE+d{UYL?($Phwoow=47skfAqt!>ANAG<~QgOb;)E^PkLZ1 zN>?-}wSmkbWy|0&4sbXit2yGowD=!6lhT_B((jr|GqOLjSStJm=IzmrzERZZ>uaTg zCvJn$xM^CdkA|pDYLa+dP+?0rp;A`o!0MMK)s9ndY7LQZOZ1k^a0gI2z8~h)wrAoJ z<h-7NfGiPnfz8njB*W1tqcyeTvNVqC5~aDbx4tOf-Qh@jVHfDZ^)$_}=x0x5s!>7s z##l`kdas!c$;s>hjmnP2X9DfVIr+gfG*YV%o7UXxKg_rfHwkZ_iW`JTqlJ?Fj&o2X z=Q^Gvj;T^n!_n!G&Yi*<VT>A;zS<Xgcth&-Tsj0yHE6OUoli$=CKn90&8$kA48&#O z)h5Z3Eg2wn*nHdC4P%Gmd!nf=BG!CzWmA7bR4LKZyq+`y^bQfPz|J?ncZSk<(5`HY z*FTe6HqrJj5#kA^$D4T-tzDa}G)WY`O^CknP0k@HWUDHWe}B5xoFwl~cF^n`AEh|< zTjUR4O#SZ4sQGohJq2{|Dhtf58nm4evYipUy}cx<I=JbFd96+&0^D)IYG!4V>FR~L zX)xp<!`*LGK<nbQ28R@x@7R5g6$_46B|T>sIozcn(B3>A)jOG^u)KEe^I};=P-Icx zF3e6Sa!b+<)?9r-rT61x;j<_`{%a1Chr8d@F|;?awDm7rPLer$BU*gyWa94ZZ}Lw2 zQu)WAg)Am{9@4s_P`0PfpRVU_;8;(JaPLsq6Nwlb00DrsFLpzH;OSq+9!_+Z`HjI? z{cl2#HQt|u#c5m;T0sjk!=T=Z71bpDcG*ks;9n6kc-p>{@U6LQ=f;-GrgNiEbw)Ak z6e@>_qy*-7!LZkw?g@LmXG%eo2*=7j>ZGE*<EC_OG{Q7}R7timHZU8|ACG{mH}jqr zH~sKCb3JtX6SyE^LSnnL-XQ}gm@@yU+h#D6ZeP^KfXuTv^nKa?&(r;1`Q0rWtoWZI zPLvB4@^4g{2Nv_?Kh2xB6s+i<bbn9=mibRFXPiyvYx&g6(Lw(s@FpYGe~RWa&X7>a za}{8H|9zHR38n)1-{`UqtmU6BU(yUFMg5=Pejxp-Q2j~5kcCV}egOY052*)3NRD)c z#BAo<1#A5$)IaZo!T;L~)FIgFzrZQ4O`yV`RV6J5$=MfR{K;2tkhsm_$6zx5LM2>) zRsRbmehbD!_+O~Xd}C1g&+H~Fbh4w-=Qew=z_FUgkihl-O{7H!H~yDDh{Xce`1jj6 z9=P|v$W{d4CI3d~MBt16=3SD45B}SY3#bJe<>%6oU;izw?*&QF3`POY{4bfoK?5H6 zFLBX9503ON5i-pJPWdm$3kNvTzY#ny_@94E!wQ1Cg8oM{g0Z1VZ~v@b-a`Tb;rTR+ zph=#T1cy$R^M!<Ko)iKf{D(<Olms98he_(n>Q{#U%tw4MCEM41{-I31pQScga^N_C zv$>s_(>;YhF$DAsN}SNatPpArnZFQGP#QduV(|Ec91ByblsScv_G{6iRk>|pW2LPc z+N}1EKSX>%M%EhTddrPnOPj9CuCB{^=T+r;8tb+ZfA2SWV}cS29*$$SH~ws1`)<EX z-2+ptyM@hNPSIgF&%&9$$N7m5W%7T4b5hLCGxDPC@x#9dcTkM)vHIirxTmg}TQ{*W zTM6SjcUOE2kx+#7kxn(?dOX$wG=|L+?gx3}<d`Nav~b=Y4|K;bOy(mI-h`MAxiJwf znn?*G5z3rg`9+}yB$<>i78uDd7BI=<L!(ZqdG*^8&K_k`=mKfO^Db2nO7xvTIGjTA zIDG5G!yI>2+<U#rvJK(M^y~`@v0JO`bfQcn6O%%8B{lILq=)YG^{huObgks^pn!g# z#>qy`y>e#qsdwR2RG(eEL$@vit;=g^jK-ZBPc;h9q=4P7dTRv+!qUO4GoQvWM^T^a zrS<aVh1HdAyP3I+moq17wfPueA0>t=#hSKZB>siIC}48a+O0}|9@%26wW`$%AH(*C zFrlKWJ{MY;>yXHjxHHHC&tuA;iPv5eF6Ee?6cqM9S~#sJp!Rd>Ob^x%AB_hhAD4a% z<aNs7*SgzxVn>UePx`$7?$z8VBdpo`=|EyB7O4KNZ*7rXqte}rqwGBj)XLF1hVjNm zj)J;k!xDW*2s-bdEkM6!4pawg-&3NU%S!9v*n|(>Qzz+VJ2t{izf@aML~2AJTTe6# zQ1^mH-F*<1S%Dz79|<R$bT!LE<gqfp3(em(S&4KRG#X{F263sdn3LTjDl*+4S+^am zW%bTP$7w~4e15Hb-U>qp!gPMEH5x<7kHH1+kLDtbZ<zbXWZ<u-h6fl8K7U7Avt$qS z(fpI;IAw$12yUJzZq&J(gRS)y0WV}vHaUu{U;&+-^!MfJc+I-x(4+QjQm+FU**Z<V zZjLz<4w}c=dIo3`CF7&-3)e*R53SBXWY(&;z02ozo_b=U3Tmi3fDV(l(&Vhlmy&@E zxjMBuSjrkw0tJ7!WRZKNdp#U#ViS2H!DS$4m7W4E#N1pNQL8s)E~$%$on<XwiFONz z85M^4T5Z2op2aTpeb?f8@zy~Q&mo%}58{~Xfv7g=0xodIou-rMh~sk07&6{^|41nN zo8rbs_nK_lo&&uf5Or06y%8~^9(VFd6LICiA86C3wLBLp&!VJ_Kvl2nmkp@=@Yh7k z`l?@5It2znZY42+W=DBoh3QJiETYdO8fSU8o-wE@Fh?N@PTkITZ@zy$eYhfRQ{tPS z$Y#rmE_9u_^f3|C?kN%BH2cdFO%z6*nO%Ll-Nkzjm6}BkSWK1{b41tp*5PE0EggQI ze1$>h53j{zXjNr5&4RE&*tN};1wy(2+X5fz;LgQOc4O;hZHIDn&GtvFL|=%I3vWvL zOQGR9v54)hCcufbaDg5gLnE!BtL$W)mN%g1407&p0G+Bc6zB2Ii`J(3>5k%+?%?YB zr$tqn2abmqVCA8$p_cYuIjE}Wf|%!e=%lf*g**8D?F*`p&c5LxRF#z@5`H;;g>F2K z9t{@{1lcrY-#a)mEh#F3X!zvm*D_+*f;mkxG^YKqqzk7UHml2Shb8o8PX@^pds^nu z)Ukpozlh=z2%}A5RCG~0Dm>*o852$ZnZ83_3dyBpK-GIyO<HNlcD)bk3lx(YiNqN7 zYQ~>{z*{aOFaa1{*&SO|?%fd{3Oxw&HN}|k>wULp9ZJ)*=`ME59-Z?2JodjzB3AAC z*kGvG#5Z07nGENoZ`U&MHa0Pw`|`};e|S+cGmo*O`j}BQbqk2jP5Bv7<EbaPE~SAB zTlawW0DB4Y+VyXkmgHb|HXP?$S{ZF>_=9ZreK*z4p;Nf7S3##FJ>*caImHQU5bw+7 zE__nxGjYh`9rEhc-Kr11oF&<~5uslFzhIU4x%>vm<kh~5p`MTw99#|wl#;EXgGo-5 z^0a_3x2`b+<abJ;?X<cVQV>~i;nX3VEaao=18CwC=OG0TShCt~l~)m(W-;)Wd)U2M zxRJlF#XB+Lj0zb!ut1BMz9VtOKGUw@hep5hRQRY5aFa4x!)`ziUM#DXeF>-d79E5K zsTPA!_#>9Sw{EG}ggPuH?<{_UAZ#@bXAdqeol}f-r_7>z2S$2LsKT_SSb~^ZI>4<^ z2bi`qA45Gq6_(b>wd^M=aU1@!FI-Mf*EpNNeAW+%f<r~d(Amdk;TJ9P7n=(i8-)6c zZQ*B!LSPm1$@>FnCMUKxJ3iZU=SCrG+6wx0nLv`Wppk=L7Lx^~D|Q1M=9X*>uF0HT zpq(L=c`xsIflz!i4q6fam&?L1;deClXTYcLfn&?=27$+FruroR3nX>HJ^PpLY~mI9 zz#p}iH+FOdKt5ou?F|zggh#-}HIUTHzhUi=U67gvBe{bHnHNjQJc(~EL329p;l;2D zD^J4V&L{zYWqv{}-i*U1)c?f*F5e*o^$X=`yTfB_>%fk%M>k^8-unZ4AmYt0VIUio zk;ck@zMt#}q-?uOlS=7i_uT2lGZ69aE6=vYja*(rE9!top#71KkgJQ)40guWP-x1L zELjZG<b>?%NIHCZi(GMhtPBZq`En#1RIDAUmW-KR3)vR$3L?^owraXo&>eBWzT<&F z1Ml4$gYI1U*RITKrjlf&U<`+NLBQfB^m$5^jpvP`QXeK+<7Al!SlNlW(fhJJ>9E&c za9K`BIcOT}+58SkxgWRhw*AZF?Gcy%G)z0FbaxIU$?_2bwi5Z{SDE1uO#LIWn9nKJ zZ;1Ikn4ymNedJdX4=?lYavJ8&!&8fs{$?N?6X|v`Rt&GXq`z;$I&%}CGyq*?JefTu zcN+gvfv8dN3rhT<Ac4%0Rh8|k^CSrNQdbr8obp!o8fUrVq{k|`Ox$IcFAfYSUfOeO zFw#~}y;nqc94uRLKf>yw2MESTqsJNf<0BRtjq??kiDq)Qt>nonTKdA=`M#L>6s18A zK&k09=s}FyUs_yUZLQ*4AOH=`B&1)4j8F2iJ%`TN7Ma`D>S4ahQ(KMBCi15drUY1O zH6dq$z4oPgV9NUoRWINt#|VF)I5ah8Pv4n0#~pGTOgeL1Ke$c*RxPLW;ynJ0fBy{e z+hpQpXcwGE$8nXykgqIrLUcTnZPuWqs=|hD&?ec8yZzUbI8pOGeK(+bqIXkhth_|6 z#UC+@pj`xhJ?4X?lZ=BP@PV}*h+zg_$Ev|Rf8guPsERFyvW_GFSQwrZ<ka@lZ$k6k zbWluS36ylgAG9>Hm<PR6<tR66dTwKnx!Y4W#jhTEXJ;&e!A~ncwHmBMt?Y*^6P<V9 z$u$&HlCPO7hqZr~yA9Coy@R*;tdHlCCSKG=oKj2ZYc{lhq{zg<RKPL;8QBoST*woZ z7ulB{3^9?sp6vDsA5_N4$Z~+FOf2>scJqswdXm0$!%9>SvBLU2p+BzFa*1JEbYdrY z3{}%eim}cc#<J6EbCn_Irq?nR8A76N6x~^885sj(U#}#99s|^R<DqhsX-`TP;AUd2 zJt{EiaS(5%6d8Eqt0B)zzf~21rAc2GGDNojqzW~Q%LZ%Ue(wmDE-3E1=lI=$KW$OX z#1M-hGntF7{G>O9-}$R8-%#mC!q2nlG^Nei^Ag@Qd{>F#_#(pmQ7pun#+IR<T-my0 zfbnG|w-h(K`3TT>NoLu9P*sXgp&+-(bjm)=%q9OipWv0&!Eko)B2oGX|I#!mDCH5Q z9c92PJj8TZ`~}iVkxN!=xTIi+(!PV`yl+B<YfKDAS3{Y&N=7oDmdPqdssXLcvH-^_ z&r@4oV-D7;x9sZze6a0cx2@v18#M5BU{g-2aXrcvoehwL5`q$$LDHiHn1&=1My$xJ zCO|jQUHI__PC36fWjIX*78IeLyX4Drp1QF1YRWdxR5n>zV7tKt2PAxvS8+t(FI}KQ zabH*`82o8i?J{NF7KLD#sMrdc0b?JZyAfyGNLlKVLh;jR-0BEX#Y3E!+-j8|j-wGP zi8gdI$OVY%<fmUVY0JD)9F|kZm{KCGt&I>!F%vU0N~x{<AfDJv#i)R}uPfp*UK<^2 z$Tw~0kZIa$upump;R;#`WasW8i%`6@Ov{^9v6L2QG$MSw)>s(3#J6;mE-(e<FKR!{ zN7!_YJ{lLE_epyLVQ)EJFf8l&CKfUd7S|m@NCq%&LcemFI_hK{JsP{ji2k;8pgreY z$jY<CZM~v8U1OfpEiPf{|NFo*C^#-GVOU~2Tv{SdAU_wWv)l9dt@1vhcFSeXksl(o z1D|A5xIXt<yTponKt~}I-J11se?HGX#|ydFPs=L*50QAZ(<Gl$r!eHru$pdYGU2dU z`d{E$v5Kbv{NkV)n)F3LCGWiD6<szj^=MauMWhvH!`x!2DJ{IMjO9rxZIg<mtz3Q< zultxy?<lX*cRD^F!X0{oFVg*uY_f28$1_{R+b3=9lvUa<@dhp<8zU$1N`kBohhHe$ z55!Je1`(`CcZC;jfukIn9&%E_s5&fJh8wV-v9V3md_(qJh`o#?T@Oc#C_cTz^NcoQ zZ(F_o;L|H&cc1HX|AzHL8>^W#l9zNsb?`(<bMKxlI2x3s3nZiq+WkdY`xk6lE>=P! z?Hoy^WJE(BvXpp}Bza8<^qw2UE@z2SXbQZV&lqw6;kn85MffmFQVA6-{wQB&ZY!|9 zBfp64W-I6m+w})tQ!opivVJ~tG6X+}`Huzt4|H7V(Cl2&sae{L{7!R=ED*WL69<7z zv6Xa+i^6FUdE(#P784Y%F{m_A<|P;~xmEe<85a<f+cbdz3ppnc^eft40cV_M<UiY# zK)&-X-G6_zhMvPD+TkRccq&QjLR<!(h1E^ic=!(INLDW4b_#KLy7k4r^n1qeZY<K; z{TzS{&%z=<h^bfX$rSfzoJD;!vt>cDL@DyUK$k)gl+_>jO1H0fXze&>kiXu(08V8w zp~g)MF4B`D&iJQOg8Tcv-}X6(e|him6G8D4+fge{Bujtxk1Oc+fou&kI;#ob=~z&q zo*)nN{bg?&(znEZcTVJb&ZxV+L$UQW5AS&~tE_NPO`v`-m7Y<(dZ1oqjGhrKX;P&@ zGuPFa`j1S#(1d~KRV^HGWrusn#Z59zJzoCksfwB(J-4t19X$gh9izY-(&Q|Tbg(75 zWuJMVZc-9vaw5q=TjQ5);=mXnGh6M*3;Jb(yC?hn2}}|F#VKZ5_l){U6*RPLod3+k zg1K|<XuaeXx7b1}{|U>Zl{uN&{Tyan*`QUS173$wf%RHO>f#fOnZ45w(`9@Nkja!+ z6n3T2`1fEdf6vIF9rrN^>101d+!pl_dT{32Wb_m2F0g!n75P|Z3-tv=?@yW?>b8l8 zyJ4B0`E3gXb2R^uN(U?>nO5fqd43kR$St7T6IW*x>+H%uIU(SiVe*gQ@K4!!`tv`q zjD==9F}!CTc2~Tt?09Dl1oNdvxFciUqd(wqI$~GvIU(GjZB6-dv<5uguytk8zHnqs z{kW0siZ6d=$coA;_a_8)3%$Xfs+~x?!#1~%4&PX+cL?vK57(!K(=dBFA;hT_#Qk6* z69bmb(fQE*_E8^9h17|aKUg0yvCu{H?B@+g`j7KNUa|X}KW~EkBA1qm%qLd*(TVBE zxH`S}jyOepT`@<-dYDH@^zvlr_Ypbthc?uJ9(oZ&+K~AvnKYouIk_|M0_#kW);|o& z2_+0*18H3WY{!FBMmrrdGKtiWQ}qZrTSDZ)<qh{Ed^}?;UmI8JpEoP!xdYJ4a!#x7 zJO&(gOPbGu<)=ZzO<&DOf}^Tc#7;ek!t5&!A#mG^>Aa&hM#Q<-&dEw2J%|#bm}KyR zke}we!M@|@;Q)@-7@syFO!r{#iK4rdNYZC#<T;86k~VSQb+&#MOMy(FUzuATa$shS zwKZGbw=2x3P0yB5;<9(Ug)e!dBk20+s)WuhcQtc8sULUo@LJxB)9lDGjV!ugcPCph zSM4)s_xQWSc7W&t_$}q7$7pwqxtQp8<0t74^dlNV$-unK!?JuVUvSPfPqE}jlRS{0 zpV526yK>+BpeHf+HHZEr^GgsmsiIZGUXv+#?nU0C@S&P4BdMKjA7zbboO1+~X`1vi z>8Z?7vi8?^$=ayldkIxb-^&0NIrJ}YzH=|&H|nUqZRr?}VQH;`m2?>?N9BXNGUD5z znC%NEPym*tqECF8Uj3NA*q$%<gXnAQy#8-q7QV7E5T-4+D^?f6E9+^?_N+ozTs>Oa z#d+kia+UlsvEvONbDzz}6}8Z8R-f=S#8!}Q#^uG#d6BdVVcc4EWIe4HEDzdE9}wWO zBBTgDo}v4wY=Gj#FlCJfcf-`K@iC0%ZGCJ*8`$}U`=CEsQv8)z+MC`oBgdPdfVv~b z?A}*<1ghlgK7!DYnSkW_3&q+Xr%I%K?)i&)Td;j`%pLv%$W_curgY+YPEM&GwLcCf z%|5l&!O%O>JN_EQsd+M~-uOCJ&e8R3XE-2`rv8!tfOr~bj^MFXP|EX8y63rzs|xeY z2Z%2^c|dJBF&UdZ{5uey|E^Ldy<@EC&2S|D^Q`fZ<ZcL7Vmb6|s+H8dQC=fZy=ZcH zV2kHR>QeKwA&nxq?ebn|hANGfS6EeC6uP#BQhqVjlZ{=z{{FI_Z%l=rLmY=mR)gM| zmO3NN{M#$_*$P{?EUhyXg5}KbUD1hqPJn<{umIjAoP#EWKs4V`R|~}sS^Gen2ud)v zX$*>p_;#~qjQ?QQ3!P12FGdBDP<&t;vBk@8>4wq*l@$4=AMHRvIe;)&O+burAg%oc zdb2;f@Xxceiy)9iaQJB&@0%filYBxpWK_v;Vuf1qd-_4;#Fn-1!(eBPr&2`tFTkoc zq!5}o5P(B-{Qgi~lLXOEJ*-VY7MD$HeMtQK4b1-1t|;8wKEM#`OpXTGS<bamB&XrV zI^l`ulvSz!Xk=RhKVNN^h_UJSLS70_@zd^-hu!k3?!eCpXrU}?hjtu3(=3)JWbK{h zF}?^K*T-`S>g^Z=AJq6dZ96$VQ$UMB)^Vf||4woNnmQ;xFRY{_(3FKnAEfDLb^(cR zxb(B7-!W?g1h)-2A~>$RB5!uky`Y@lhyz8Pdf88lp)LcF3th#Os=IQuc799DtK7ee z^!muQxqnrIZD1f1bk>pDfBm{KD+>x;fOt?`g{gwal#teeyk>9&(d<+c_yEdX^7(cW z-kj@jG=tmlb<;Pu^7i#O$~Je7e@$c;pT0HH31!7g-&0Xuso1`JrRgS?l2^m0R__cz z854MCz>+adShE~->8i6B`%L}gmSh%PDl3RR#;^O1HD>dj!Su@liVVNTw}rUxNaPOf zmDzy^jC&caZGk%0C4johgzsTn(I^_{UA%N&>~Frs2gVk)&^6fhUxhzA@{KJrr5=Ra zF(tRom0y?V^zYvU|F0uabB{5&&p!!U!5Td9pVa(72fHxy$;pt0_^-2b${!Lnxzg`b zx+ie}U;78l&<(6@wfl7FLqhr|@BcRk(;pHA=+w}1o>xcvKx3?n62a(UgRz`cc989% z^B3u9D2+#JCYHZMN}GuryOX1<AtxO*JQaWPdEMx`f#VT1haF8wb$eYSTs&=EuG=RA z6Crs0etN?2sXhO1U+8N#@b?+D$MTJ8AD=Kl2*M}jtLP9OpnDke!?XjlmwiIwMnDy? zLw}~C4>E#)hhxDYU5Q4qUoh2;$${e(p^{Ma?+r~SR(<(HL1;KyLwe#*YAn@v^>$cV zYGdbV<08P%95+2Z9eH?{dQkvDO`6MZi_?^kGZm>_#Y|{UlUr<6i^sYEE0e6YPB6=6 zw%)3px{4A)DQnGKdR7#xnoJ`#*`Nv3<*l#9*_}sQu8x*cPV!Uc1~#v&sTn6PSS4zq zMDxShIbFho!C-3THo1?|vA`P2i`;bBolHT(h<l%JWF-%6^h81c+P_XxX1;Q0m7}Gw zS7MAU%<&sqS$T*zx_n%6yk^G9gJnH9nAV=lI=ib>8N!6Hx;~+~2r<PnVNU}l6jkLi zR&^CP5u+W(r$c+KlMT9?D+9*Al2c6M1?QB|J&}~EkPwafAyBrLFuyLAYDKQI#~CTZ zN1ufBS3shKfw;Lp=c$hKmbnE!IR9Ru5E)Bnm&vady`6;U#fGx|AuU8E0v?8vH)P1@ z3F0BiL6p=>=g?|jE27*;JIVtLl%3#%xeqYOO1K7z_Y>?m>noPWtM8JCnOP_47cy4y zHp1b%K{L+(D63eUPjnvwPl`!X&WsCXOD`hkg@`kTLlKFFqcohJ&yZTt*XSeG2u>3% zFm`0LkMgaRH*|G%|EnSq&jtqQnjhu`;oj00xy#6?x7_KP?iRY5-;M#z9Gjb~C<(zy zy=cGdcQ;H-<VOn>X>`60S~q)|R@}_T8C^6Dm3ens;x?(3yj^cjRJ>B)Eqm}hdM4%M zXCP$4<M!66MU~}L?hsv3g&9@{_^ed#faoVVytP_qMz+HW^x$I?(mX>G(mq3&qvQ1| z!HCYU&R#gsag3ELsg?jILl40wqaEnZ6Pi-&;ieenVf1*kb&$SP4)`V;DmT;u@eZCU zCLA`{1gpoeTYUp&`<8Mr0)ODvtOg|L&wr#wGNOA2SEG9e+LOCP7tG(ktd2em>bkM8 za&naI@Dg0Fk4SOVQrlar$G88{A41HUh0QFMtFK5@-Q?=2WNQYre<#<R!(2)aF{O~N zxyL>YvwA1n2awW+OBqKk6!1z-=G5m~X-9RX4iUSUl~Z!5S<A6AtWS(B!{S^QNb;5a z2HC`N)1D||oT8hKVfyjB%icBUFK(SQu?R0wU4sm}zV|Rp7coAo&-x(UI6)KnWlo*& z)`{a5?^gq|k0S$c>RY|mRy1*=lzHAcJ33;wv~c&@gIo9uY`gQjHe{!!iQSyZ=}ohX zZ|H48c!$gBmqm$AO+rC^_0dTM{kNs1gugE&7+&gC#*CYFe@@)aB^V!b2vPJ4syRA> z?DXqLu8vG)I!c{pd1Z|e_K^7ZA53R71v~S9UNxCMql6Lyec4#vVM2L-i37S}DnPel zK=_|#`2oQuWMxXjOOgzTMv60Nu1H}a&5e38x~q0M&5iaL?b5y0E`a=sr4x!r;b}8; zqd7ov@5i$jSPaq!<Sr?{Kl(QmAFpUhe*df;mL^9Y0pa50zq>@C!cN%4JydaMA5sDM zC<zQp{^<$8my&J+MVubyy0suzJjxx^C8@f%J{%HSq6(FVV~Q}1pcdO$hBTiq=6Yw< zuoQj`Wr~VD+2~iszo&}4+P))KkX1OFG|v9vQ)XBp>ukOM-dq>(*u6wtq|JuFfgBjn zK~^CyBn*2OH1iCGTwxref_RML8F^Vhe&Sw(z`xlCa4vj9+-1k)4?@=FoO*J_(HYLj zzwS|CVg++95h#LmUeF6b8iJ?}AvBne$$s6=87%+9P42+|Z3-4@*3&#<1iM2@;U}Rb z!vKF8am2$F?tx`P<kaqtvCHJ0_nAI90kS5mqC+7$psn(nA|pT(R9);6=-rSisHB^B z2#+KW1Zm2{bh#X~+I+aJ7PS3<7wDVHCPu9Ko?Okk^mOYJ1l68TyRu343P!mm`t2nv z$H33`Tia7_o18<G+XLTP?yN7JGCkGo){;(uBzxfhd59HC35hbOiX1&$Q5%})Gq8!* z&^IvZqz-CBbfEu2{nwiI332*={twgc;0FINz+qM&i~66CC}WyrCXvrpuFu1YBH7am z9FBxFKHZ$xve=TCzG1T7Um1)nc&<PWS8{$PL0lWjj-q+AA-UKKoI`Q+05t6Ve)~TA zZ^y@V#^1O5hthq@u##ZGyjeuu19DhJ#`hV8^-~XQU$Xuf_E#xCHcw)|m~7boX_IG8 z><_(=?D3ljkWR_&gk8a)YYot3dT(%S0HjmCJ8}01%iEU1U(vs~Ih-exAZGS2C4xc* zx$&Sp@_#XNnokjZdBloyL!`b8fnh?f4BEjzK&5&F9W83Y)x^BxK!GjDGFs?Hc2|s7 zp`<3l>ZHOSvZ#*iEVaxH!+Ccoas>!f-QAK9GCn#&bKIf`YRp>-GA0_P!kdpc07dZZ zEOg0+keO9vdun;UjWO1{Aj_kQ)sBHfQL=}-C9$%0@S&3?P7*v;6sg+n`#*EdNKs<g zaiPT1`fr0)`B0=n5WmI6s`AC!T*;;IPNXm!xgA{E8q=V9IxRq_f0kt_vG%%5>F#+J zUHKvX&Eft<u!cd%mg>$K|HFq21CY||dnnZ=rxU1|hA&1)1D_OkD)2~8#fe6|svBtR z^xEGp&1^q+V{YY5NHpDyO<9d6z-B__jq<0WO5D7yLEbbaBe}Ch-7MXOCh4g1D+{6) zfU%S?<5*;-nB)kye918UUZ%W(Yh3F-hGdyO(J9W8W~8mBqw(D<iJ3N!4p4xoQCjbu z=K9Vi5w)V|Y(Rtj^D8mJJsKt!+h-SwiE67Nx8ASI-0%gk!cSvAB~}d|z*YlPuW!-1 zAY^?j&{i8(#4W`Ow1t7fU<_ll!G$AOa|#9-{+b;{NNP;hWni9pU`=Jc@VhckT62x& zrSpn&rcGe{^%ipXXLB|EKA?=m{7as=j6L{nSj!$&=3P9=XSHHuNA0fNNJ{gv>N4(f z>)ArnG{~-cbsRcKdwEY1pOhoTP$)nXz6$&c9fM=^W}Z5Fc<>l6cWz{8CNe9Jg{=jB zgP{N#^4U+qNU4ntWkQyl=DV>t{TquTQ6-|Aui{^tu}hg0)$pW9*Z?yl#xa<VD;ezF zcPd)9)S5g8El-mL<@O4qi}ol`R;yL~L~0ca@hVx7pB=gt^mTigTpPr0hgF(AS;_-W z$%5Qo6j!OUaa!tIa2Nce39&-@^r>0`OlGh*e6cGz9$}>xh%B8>ZRxVJU}|>y38|{n zEjdno*^%&rk*Ykq-vI`%bdX3TZ<8?(jSC^~?$%Q#5F8ktofU3D<6@l}bW1FDM<T7G z%Wz>s8ZM-m;<(ZvW-D^P!6=YvIv6#2`}v^(MbClALWg5|svJtx5BQI@{M#GfqH|ca zQf@Hrdy!t*<&eZtXnRNZ75M9IJB`ZVv-8j1zB=GOS5i5$8#vJe%d=}%?MP{b!OSiR zA4bY>(bV%ZKdPae=v30&+K4q3a_Ar(gZ}yAqCVckL9Kp74AM;!D$&THNu%+*!hQuS zl2Dax!_L7;Ou$-!Z}F!!%vH#w(kSf(JwYskxkI}eGWe24Oj*R?(r{76dWV%&b0Ew2 z<T~_vuhy;)6yQWV#s1eXcDp(EsG&)m?7$8zNr^laRei;F^R)4KXxHF2cnw_?8ZYvQ zR%p3H7Y_$MLlS0V-bLGdYEm+STuZ0*N^%<*`S|>eT*}x}pH<mz_qO*wjWG3@h@ReW zWTtDwYi#&A*nvgE-=w3h{#5QieE04DYO!$4D2h!kW+cy)#g9VfHP^MJK22*t#_N5Q ztMt7up{>FRF-kau3~W%9Sm$2IfG-m|Jrmhq-E~*oBkH6VMvf6$J&w}#?AY10Ex_$$ zL4wz{EpA78JQ>3uoDwO(f9OOwigD&e<dbi#Za1GS=MPTB<F=LK7&?{iUO$w{V~@P2 zel_>P(xrQBw^igAGqv8Tu}|x=C2TNvYV9IYY<|j}`NzF@NaL&{;*V=`wLdrkAeJ|& z^($vzE)6DgdwTio!PZmz_<R5%5XuE1f;o_{r@<;bBg{pbnRIO<f?*VkMeUtwq0$Kh zYZ-bUhpCDg1!n)IH^?)q(o!&kg%eiT?$vG9Vrp!g*@g`71A$FXMU$1I;<WCHp{Txi zB}G-NiTiK(ya%KGg1h9tVH7D2a6RwdJ){&R!8Ww1w<YkqOE$@a`)Xe)v)UYjdUU4W zm8bZ{Zwd-qZhY;>htq%>>##L)$N?cWjU53ING!jzzSd%}W=6S{X>XC^S-;v?c)9uz z-1emmK_Q>bKo!IkU7oFPjgV3t3R)PM<L<ibeONVjiF@Y2ub>e&_>r_KAa=HdS}tax z;m7AC?=NJndj){S-i0d{R_XGEu}6;?NzCmVx^+>y*8P<4Y!75;`exfFq~&H4@;Wyz z7T&Y++(eBS+<Yy?E0;0b&K7ZF1&<d=YQPvpX_00)B+pDb&sW{wK<SP{XdIh0p`g+X zje#_0oVAqx=sNO8QU2@+uuD59&Q`}6cj`Wt=V*-NquR$ElIZN7dP>-F^cyJ=uuB!d zJ<(w^DdI?N2@rGRNUkv!v)$Z)X|_@S(gU{Vb&r!7tO98+OFtRsHb@Fm?RBiynI0?4 zjj?7bomBuC<*e*;D{BpceV}C+ObqD{8iLPlyBx445{q8d#WuwR*knqI?(!N-@Fayt zrr^d?LB&<3qr&`@A;KSbBS?3eHam78@l{M)Zfd*wL8LsTT_>rZB4f?_3B<QW@+oyM z8nKEk@Hx&o0{wdEP(~`i5{|&0RM){6P3DCf8!JiU)I+y&f<97JQFZ85yzgP&K?`-K zf}qbJ2f;_V2GWTPsCQEwZXl=cSuSoKZy;w%B720GQ0JLQzQZRLx(?r0C3Wq(8=Z0J z{#2R|gM4Pnr`D^Zk4wm?9{q6BBo%ybSYmQAA6Q}B6k3t|Euq}c?bDm^ka0+@m=R4b zOUh@%ug-x1f5i*#U*xN|()A-mdXt=;j{jG~bhorGM*36{a0^uc*#e^{?na-t^}!Kw z=vqvy<*T``<JWcN?hSaNy*q2b+jAT?nEZ*=WwpU>Rd_vET^d@wE~LhVOo3d1dyiw% z`xc5E^|-4Z7*9APJUPyhaJ%|q!EIZtPvWFh=<=ANChc7Pk=H%EO+K)oaNX~Y-em_; z3<Y2=YhbVgvT@~Ix6Dq<oc!WhuQ`91?*zgdN!FrjrEe?AqSRyM%S!{SERh#y2^5#L zSWc|kLL?`*6qZE-CkDbv*im_PttgUOYJKssoA9lBN#DALrP{6HykodLJHxxgPiN@~ zbP(r3f-m1QciblHfXp=m<6ADbSBqb=I!F2|WE}9oYz@6LXBU6{h|T0JzlK@R<0{LO zit3BqsUx@Nz!P%#swYRueSl!h5p@gOu!S2M)1WQn1s1>b@_L`=?Pb>B$)vPd=U}|C zOCPhgEWMMgtAAQS0{y!;d8J=zGHtEel2LT2C68b1p`1m;3K+pIO;vRkSvS0YMUC>| zxmys>kPPN0uWnIG+K_@ktE07*(lmp=Ldol@b22AIGnMQ~UU1cS^^s9>7OlFOL{pDw zM4{B__}PWp^~V1TU_UTmEq+5>y}M<}687CEOFS6oBmzM0`klwR#WIP;^VgFId-eS- zwZwf@M);))UuZ&l*djg1pjJ(z!&xN6ur~p#z=BA-`dGapKhlvQ-YxY%tU03n+f}cO z@$HTQW6{dSGtQjsE8B+>rA1qHg?WRx4_k7eFFfLj#s%yI`H<X4U-;u2Zj=&f+R+2R zu_TaUl*+!mYJm0r7CLM7+u-TB6v6UhMyf+Mr<$?}CZ<JBj{ybOXC8GdAOG*c|KFFJ zj2RHwN$|>bMBtRELsohV)8=zT3V$@5tRD(a22>s+xqH{2w5pu08s1Li9#5Z4+w%V~ zG)rvi&gpKjMWgMAM#pg@%*}5?Z{F8I!hs;^lql`!60X6xgXoM`O%zLa^_J-IOu(4q zN64$0Wu`A->bFWN%@uB2M-WC5-^!JR>l@)suoT8>MJ)2sJ>3mn@s0^BwzlC*)K1b0 z1GE$D=L~worj=kevja<%hYOTn8}KWY3%jWD%OH^6;C+_VT{HFY!IGZh8@S*${O*h+ z-yXhC^+MsHgASkj?YsJ21sgK3|H0cVM)>f3eUn`id08U}X=0qpk=v+g*et}0Tg7Cm zZ`jOpYLxU1ZD{<ux;%LY>sj-vudOSL2<$3|LGlL+`oq48vx{i@L?#_bJSu5R4<pZ3 zAL)vW<BKM(x6<4O^%XKy#1Mazwv0_t2}z8v!r+XaCjC9dE4)}qfYw-8sh`WP)nHn= z#+8YDhnLSvSyHs9(TeBlIH+_6`$BP2p$?1ydE`4*)Cd?*;7_yM#6XR^ZMi-nFh_ZE z;i%0mRjw^_CaPUx5~tqW5_pw=Fle}`Jz-p&jhnAIvV4Zv1}cK^(k-+c@`kFqjsp!T z-+<Px{g6XI*A>GN1SafbYb`Z`OOu+zQ|N2jIk7lH#3L;xajJ^X<HARbf~)gxTr9{h zK3LiH>l&-_6tRs^oJ@Ug`~im@AQnJb^f}@C*%dv)_jBu(`XPE7WSDdI#V2Cz#L}f; znn<ua(CI@j;l|w2Php=60C9guT0Hdt*^}UkAq;#)ebw*;H4q$p=6etDMd0raDKeNl zf1vdY)K{i-gZOl-*Si+=`vz06JYz7wol_@ZJ1<RKi>>&FMBN@-_9LkafLN&vj+S7$ z7f!;&!qWJCT;O~~xrp9t<(B^SDc&v9S@>yKHM2ZtduH4J$Um8JE?MneOKQ7#m}%ak zZ%TEX!Ud)2!#MiGc!p-=tYB~61^X1ocKL*M>F;W)rHgL<@a;!_5$CAN14pY6w-|9$ z*H{NZLG#FVl~Vi{+HUdB+`qI)Mu6F^<@o+WWSf_~f{Mo_MCGNug=^>rr8XZ!Ygk~` z9}DCw-Rm{=$}Z{xxjBz!Psa^L_QYivXCKt?Sr0kXhAZSm5T_p6gq39Gu7H{D@Z(-< z=$rBrY=h$84?0%G+uWa^bS7_n!z9?YL6|EHPq3Zu>|U_tPQo@Ifb0hWg?WJ4C7;=~ zcfd#QuAo_Rv#F{Jj^@1T%!d+WDK_fQ`Sct>1wGw<ce~ji5<Nc8nFy;e75A6dF=1g@ z$Q}Qf%!{3WzHtXgJ=|3h^oQ<>%IaFd|8Vur!IACV8}CePI}_WsZFD%XZ95%X6Wg|( zOl%ty+ng9T=bT&hyYKz4t9sY2?y9xEd)Kqp^L*k93-ju20+t?Q0SX6$2q!3!3u<k> z#Ht*LZWZ6ub*X9K4{UDP#ASfY_r*qy4r61WdkdRS-;9%5w1PT@%KC|&e+Fp{TO^3B z7$e8;HLemnYX<o{oHjz~VC+OVtQjKHH$bJt%&SGtm?5VRmyHvjH$$1__WTUe!N@DT z;oJ_B)Z6?lb-Hw$TgBSy3GWOq^n@ki55IMXnI4#5h|1Lq8fkGtc4+`cRvUBZOya{d zq=>IbP(92?|H#$SDB-cE!P`-4PZ>WZqA!tX4Ty0f;~RxN#@3G6+9T}hQN1T-$X9n~ zde0Vdj!3But!Rr%aYSd>!+BGmrpe@(qUIINsk7C$9odFs=|!tpbwis1(`gqOC8QEP zkl8F|iL*>%qbMsc<-G$6{Ep7Q+TCin&%8@^tvD+$?%hxfFW6ug;^T|H9Gd7{j6Oq% zb5AXH4(?2Mt}a44>MpX%EG*3f*3-ztzc-X~a%;w2QI!Zoz5FhvnGQ~;RQj@oD`67d zK|Y&yBP&fSWg*_JKDFF_{;z;9ulRRFdl2j!#J9^Y^d<AxA~||WTo5=+3V#4NMhZ(b zC;>H~oUqxvN~V5yZPK<Kx2Kt<Y47)T!u!zCRHOcS+K1mq!IU$%&(tc}P>OdnC>C(A zjZ`Uh!Ctq2*g=-e;XCHRY!T_yHfozJ1z^aeP5T!ni(?ygiWQD2n>RI*6>sgEQ3+Ux znYLY<@y}{yW;Ls#h_f`ZPk(^RGObw~s|jwmKucHFwpR(=uw^ECD}QYU=D75#j;FaA zj@6;k=(SZ*h3THZmQJmA#f~RxAS@8%7gdWT%z0MbkGnN#o6-BjXtE3nJ{H?AiW1y( z{OdI%u@2JAc#M(fSoY|}sLl*GyL2o!J`+qnZ7{&UpoUD^w?YX{emv$1N)|5B3?HXL ze3aeXxOzt_$IY>xa$^d+`Jy^0E97K@gZdD@!S)Wok_8b?2jk3hsJUvl2@523XVj`G z)gLVC9D*95><XF=YRfZRfNvy@0qn7Bu)Rl^EuO^p;8&n`TU8sVbhleB^b5LC0grx{ zW%#$eu6tD6F6SaVQ@_h3TZHal1Lv<LXXv@z^Mb8ng@Jj5VCViEEHmWmEJN`YzhClc zxyZb&R+(^UvZE@t7<o;pAAw`)VBB&g(sJWv>9JTZTyBFEsVTIhosWa@yi`>61N7YJ z>wi6v64OG?xNY_2C2;t-J(&~&8P;JV0(9)JgUM@+ZSnf)<uY7#mi$te?3IT>Y)?3& zNVxg?kGL@$wEFSz^tP*G7#tLaq2y0Fg8_I~yL?j-8;!|5JIjsnf<T8OKiawEX4KLy z3%LzjWm6vWmpxFP41==I{08vJ8g;k~jnPK6lS+_uEaO0mXz9Uwt{4ReG`UO5T7>4a zXPZtyxSLms`)7$Zf@CPFbcl&L=BaERX4svjJlY!Tg`rmHOW$LFeT#e`stx0s;5O9n z3@Tq?F={fcFr~!LF5n7eaqt{3H}Z}$1HwB9fS0pAI3}m{s>ipQ$@07U%y1LdF;OI_ zUp#5Hq7pvjb4cQ59}zLks*XRnLmH*jCZGJC#)rg@<8TB!;^r7<CIQ~~NaK918Fob} zBbrY%O=HksQ*}RY%)-pVAk%MTtwCpHon^<(&m@cLq%uWuegl;_1TYYtQTx+Y$m93I z4au`*qJl!(M#vwGS<~a>U8qYoiRA1d3hg6}uk^9pn5Mb~LcLP^P)q^6aw=T7mquy8 zt$wBNtqL3=Tv0LkIrMS{Aa=@grEVa@Sbm%?ulAW8LC!7MrNflhYLpg!EqKy6g$rec z#6@B8Az_$B@IXfU8<A0|8;*zYJ8iGQ!ly_^VfkW4rwWy%Z|~2nzQBlXLth@Gd#*Nc zA5iF!)IO(Wuf#kYDV_<9tZMhMCX(+;368)7M*-=GdQx;D3H~u<grT^8ZZ30g3{AOM z39Uc@e40b2*<ae$usYMQG=%jLww1FpEvz?L#LRd-Y`}1|bWxbe74-BvnxMKfarr?$ z=?%V#D`?f$9~(rI6rxG3tGhGo0A{0`gM94UQ{O2XY||1%fyV1m_GjZHo4i895sJu1 z5q^yirFTJ(<iXIyeCe04O2bY9bv1H(&EQ+OsraVGk(%tjq2K=#CnoQGE0}2C0v`|B z1cmSgB$dCX>|Z$jvfqRuru<U>Cxa^p95#hM7Zjo8dj{y+zaE_CT+r|TO)XK!Kf~(2 zEK9K8Q*tH03^HFA6Qqb2g7*I#@dZ}|$^-rHC+TWXOt62ciyd}!aARK{rGXUNdQfSg z!avZ(`D9xGHOeQATo_Fkt;7SZyf9&K8zpxnB?528`3o?NHJ$nnm3xXOJK1jqh}}<z zvCNxk?8t@WYuAi!(-WIsmuWn1hr`n~Ti+(~&?Hc}EN-EXu@a7jP!O52{lcNSvJy?D z<x5b5Oj#!^Q;ovdJ^0qQ;ezYWY%+j<T=5b(aElK-`|d$)FTnA!*H_s&4$j#SEAe)! zBER?`bjwe^gq<L>U_8w^S^7Vp7}5>1UO%Xk#n$Tr$wIEgA6bi(JW@E?G>*o`Nsjt{ zS9YFkFZf6~qK8P%RzZh!*I$H12a2n@YkiS@ll<$gZajm%nloE4N$P{(JV${JYdk2d zI_K?3t5W6W$8l3ZNu;S?zm{B}Md8^@GacK)6r0|S5<;ZX;?=eGbOEa`<d44LJl{2K zovF-w+HYv=6cv>a)#36Een1m-(ax`!;nd~X(8L&-<Hgxna6E8YOZR*uXazRj`7cs0 zo*3MzJN+c}l5=N|%xTl5Oh1s3I&kB(x;DTq-_r570Yo;Esmz}c?zZ6#a(&5i&8877 zoS{KBlWK2W4;lrrV&?;~HAebtjFE&(_|8YMw&|pHs|$c$v8FGizYfQEGlAmVW&_Q_ zZ1=lRscs%b=G6;5faisHg`3XL7IhYvjEZh^JM@(QS2>o!poksxU?uQQ1fu^~u~r}^ z9Uoo_{UDj_LCT|9d196Lh#D&|RHX!xGf{f<0sL5|i&owI-sl@?){KOC*t{E~fGb?G z!f*`o7^2zSFJ}er^d)|hH>p8wBHSXZZHVf4pWxJ}KDKlbqR}Zd>3JwBR#B458Aj$X zO6LT{Sm72ZF?5_>$`cL!)@U9Z9lKGj`h!~u(qpJ!495nXLMoG^=B$PkVKP6sg?3ve zz9*T7?ZcruKtL2F#{b1I&~n7mKzG1=JjU(yx$Tgr6w;MyB6|b*pT25|Z32b)Z^)n7 z3VQpW>+jcZ(7gXlZmnd{F0o&2Kn#)4tB;lPMI?kz$qoUBY_aYIP5aNO2Q>g{2KKMn z&J)572m5O`02d@dU*X@2aaw9dKuP|)6%l`(_wCoWaNz$n?AN`}THwY&q5r!L)&!`} zzfG8ux(IsyZ;QWp=j-4?f5nob{zu=g2m{9l7O7h}<Eo>7WVg&R$Kdf~)U!cL(bh-{ ztFIO|;dRs*t=br$bK5c+Sy;!?SuD&_hKtCI>n7JTn9wOouVf?-*rJ$A`zwtpErFP9 zY5CgAe_n%5Nj+^!*6&Hm|9E10v~hdh^m*KTJWO*cA%L`}``8iUkmZn4is$KEfs_#f zjs+^M;A;mqW^{?f|9CJ&k>E}3YYZsBFO`(J#o@Rd(4p^K3hZ!*{n07kdxqZG8_eOU z7|kJdYbv!#JlX}bsTn2R6WHXQ5zyjQ`~50zpTF!PtNxKbe0M{)6Y}aWamb_8tuHu% z;<XTB^PY%m&<6m~vhWh8tV8^?y0=RScxeQ~T^JuLa|;cQ=w1t(leolRv^@`Ukhioh z-mdi++X(;HEeKo`^Jr~SbZiOb!4E^`3`Cl=xJb!UIyc`;iKh^Jt{E9NsL{B=kH<QS zf|bYSm)&~bV)6uIG1(BP!8$X_-ekW`N0F>H3(AF3iJ&Tfg5UC7lX;2BPzRd~P(Kvd zTGc$<I0Tw*Kn}8&=T2y->DiMle_AG}&TFgLjImxomO#Xk9&@6iIA<&F0=+DG+;#F) zYWvr!fJrBvg+$X@umO&1N*+T&nnkSNCJ2M2sTAMk9HSn)2pu|x6O@lj4M)34k>Qt# zOG>B>9kZAnrx7?J^;48z$)2PrP+?6aOX)gJ@Glle#=4Z()Vj|{<e#jo3Yz=KOT9Vd z_bt~k&8Q%QD~y%)AUe3~&U5HMT-#P=n%++j4%U;H>mN27Tqi=God#=*@!~y_EA4E> zV&)xeI8FB|<<CmI@5#xM7RVm?viMp8h8)Xd?$nE@&1`hFhaiPf&EF<nfg|Xb`H8D8 z_mn=SR!gn6u-Zw}>nY6%78%Dbbn?hz<AV9X6cBAwdx%8AZ5THpiB)lJGp;ZS@Mhy& zGB_4S(}8<G)A_st3oS%v#}x%}nATm*KCFVT-8_+1bjPHlR_<Fq=LJa!1gz1_&@mgn z>_SZD*BmD#p+>oN&k7R@;5_V)!tDl_7^<htfN7THKGU^`W0c&U(>FoO_%Rjl?|LdX zWMHb^*d40g;E1pbbbLQl|3tjU|4?@bT)2fPJ3!_w->u;61p%xeQ_CBQ_f|&CbM{L& zHdurP)RaAO<EuNU$yZUIWB4a|0OD77BNQcoRz~V}W`qlC>x-+%z)L$A7ir@!5*|dh z%8i^<cQ6GwyY^hH(&0K+@nAlHnXhP<$6a&K&EyLE#Q$pN6~;GrH`HBiF!s7Of?$sV zBni{ZFk8>jBX76l2^)M(c3yrpIT`NcAqZPeHF4*Np0{Kd^QkC8Z^y&p?=rP-;9h0^ z-RUa@?OknUE46PVaI5Tz@1wTId+WFm-@tE)wr+q03NtZ1lj)_p!mc`%MTkBp1|(WN zF#sfkKE=6n`Eb?+kYbt<be`N2!K-QE^h?IM4%Vj1>4>M#X1=AkDV53ecvhE=J}GHh zXWKF0U{KO!Ab~xwenro#C$F-0Tw`+0(A2mb*zIRultD!W^bd~uDU0!L^KrRWl!#tN zFWOPnCN@_6xU8#UaHyVfC+y~HV+G+RRSf7rf!;{(aHVnbU}2uA6Zo^ZoS_%bFej>P zg<-Uvokk0?{OU!I@cZyBZS9S!on~Qek!Nmm9_J9$rs(&zk6PB;1wNfg!w)Wrr0__L z&jTH;4)+5cV7BTSj)8SCGc^t8GX0hO5Wuu}V2cd->CMT-Lx?NciaN9l?$G)|^GI)^ z*qENr8k_yECovOCk(t<7@%>3{Cw>Y8r;|JzgQR@uu*n2jZ#npR+kTweZcy^6%WVbe z4lB(YcvsC&Pv)UNiREkSR(D-jy#zNDx8cQ-hjJa<z-qaZiOn!mM4SiFOSZ|Ri(rJB z8Q9hp4`^!!Bo=U{<gs&n`%RR4toL3>VNN=h3?e)eXWdfsYIg3ahqXiPzbO)<W|08` z;~=jb{0xUhQ3FIy($mBf*3}6vV~tO*cObDP!>lE96W6Z{tV!r&XjfgD#5hJn#+<Oc z;~1!k!08i^^ZqZRikeslMr1HHf;W2Sh+zY%H3%-lWa#7@XlKZ$&fwdxc8q2J=Scwa zl0jSN2bZnu5Z#YEvL0XPKds0d4%JBo2|rS5t7^R9@=HFzB7{i?yYg>{DlIsJQT$_b zcj2T%ad6B4u2HDNcw{z(xOs6)NC7#Y(M_d-KtA9l&We>N-s^>S)p+SJzH=?z0nm`b zmYfzbPsUFyQS(ZWW=Pku)X9oY`)pShJz^?PO{gWn2c~(eP*El}P>66~@;;xTBTb7_ zePD^?W@!=i%3+ud#R|L4KMSE)6`OvTKQp_L;qcsSNpr<C(+yUjyS!elcm<=Osx%`Q zsH6@=scsKRuoqx9D<50E{aeU^^u>276;IztL>9q%k6Dd4haSxaQ*{3B2Lma1`L+JK z`3-{QTSRsVX!3VM#OYy@Ypl*Lvs{G9eBPhCG-<u{5&(cISKvY5DnNAuy7k}*t~|6{ zZOTx!c0qlOGjK6bzJYxHskPg+)N+FsxCA*qrQaV{V~v3u7y;Gp9Z};9q5drGJqE`v zRiDPKQ&KCojix)KN7yp$1EU4b9~D^NBDW1~jd)9H8RDHiRy@`N_f~?9ohqg?b9OhU z`<^?`YMeeKgmWWJ5}|T&mnp1_oTO+UYIMEw4C!56QmcvJiZ8+=*Y5piX*_lZ*nX}p z*hi@OxSH!pbQPh`<uQ(@o5FIWJpD3eYX8^a<yP+ramoOb{3gD1toiJCi!NV$!62m+ z1$Up*t36?Ywf4wBP@N@}gC*0ASy5|dQh8HitzZA7{{k*mD*GOfw~K_gtAjBJ(!uZQ z%zz(gH$2Z7W_Cl(8;aZ+roNX0oVi9357!r{ijLR_;{>pX%ak~<*%oxB;5-0^ZKt9e zm=Tm?iAjhsmCx}C<#+DIOPj)wQ3?+M`860kWh`T@*2wh73)<4pn57m0Cq&8@`0i7C z{O+lSFmT`rFvV!4Fy-i#s<lGB^x#a}TVeOLS6ykw48QN9hHqt{08NFG!efb<6qH4T zEUSm89avvj08}`}5~ywYKa=`ydkjxJA20-$N1d?D`#%dmVE^w`@1LOKuw%az>@Sl1 z->>CNrhnBS=x}fXU{ccAKTP@I>HB8qGD6k32x*~(X6>a{cq7`dx<Vn6AkmxgTD&o7 zp7jZY=+0n<ZQNr>)aY>XmyJ;753{nO_%Sj$x2bHdqn1ynL+97C#})!n=M4N6<JrHY zWXB<59A3xCa)5T5S-i+gP3KrZEQ(!Fg;+4M*C6FhPeL}(*L>DTyzWbN(NCn7qFV5d zhV+NR>Y7gSQFUoOk};E^q_Uif*k#_X4zcRnpSTobJwvc_kD+K%#QlTruUx5;2o+E; z*tsKjxzM)heAsCT{arQhV^1eTOM9Y5_;074|LI3P#o(N)k4`Ciw{9SX&V!Dw*1;Id zBb}M7ohK#G(o+FG^vAM#-)t$VJ)EwT?pdkBQekivwm>I(*oLBS*6aWP#uROqzo7NL z2=+tqQ3-iwzsZK=cORR?+}e+?v@N6Z%fzns5{J}Cn`x&5?4Rw_&Y_(i0)jtTQzZ2D zvMQ<!uk-m}><f~bA8TOmnBYd7s)No&`3`!?8~1O4;zQ7)v{sk4d>HUu*k388S)nbY zG`klK(e!vG#s<LA<v0)Sl&z}vA(KUorLhE66>AcGp>|F!^SYFGn}Z07Y_7f?`W|c% zNdUv&5=trdfQLA-i8^K>Sn218*(Nf9NKlDS-0sS4i&eu01Vs7VyoTK>%wxiior+5A zr1Kcy3=0IDRKjp7wm1o76ifjRB%B1M_Q@4}!MOp+h&ki(?5x?@p{Uti1#3u4fS5b@ zHZGG*A1(<ru1^17(#y@>%fWPdS$R1#!!&ntCS!QA-zcwOc0myKO?4>wkk?ZEp70RN z$7osPFjYc0Ak#n1%sHdvl_Atv8>i-=28GXnKPuhLvr1rROe+Cn!Uc6h`q(ae&UHn$ zFtq*Aqyo*0q{7qsrKC+A>q-H7ggTZco7N`eFm()Qt9s)arC29~q-#Or&P8JOwdq~% zJ1k=S=U#8-5{{i&nFZdjH?#KBAM&#)CoAd^pMNA$j|o?7g#F25WmC_kQ@J5_EY<sH zStoy_*;ml?a)}dMx)F)yPH5Kv^o|Dw(8Tu#3ML5tr-xdw_dqYdBmdJ)<x#nCQD5D} z{1wze^KUoN9f0Bjb?k7(zVa&VI-43SQ05@w1=nX9>y{moeHG0U(hWH*jDCbJsgjP& z8+S=@tms;G(WgFsKyUi1$JJ^Io+56mU(5Kh?6Hn4{Gz>aJetZ%^SW#!xEt2{-34L~ ztDq#g*N?{BnpF7k3xy!If5-;aZJ|9`803sfcGosuQ#V`znAln}P$G>civ}*+LceZP zL&vDWA3t3<AO6<fK&NE|GFlE?Nd!>bgH^+2S3kD6tnxaS1CBWmmmgp}{uUFxn*yHE z+|MRuK#o|qMYdo~j`=TaA@Y`Ez~&AJd7TRhSn-}KcUoC_28lRMde^SRvvC(X%RlKz zP|1ci&siUUUG_)0PwA8H$D?beSgg{_Xd}gsFlj_kfR=-%tl`_U*ZRdZ1k0>;iEjmx zN7O{V^A+a93j4kr%0VFNOKv8|74LOSzJ*AJUf}UHkoAX)tow|uH~<yxi6CWPpCrHV zz&+Xt1Q5n{qquNO(v6x__b!e)^WE-nc+?DXe5gVod$&WEnrLlvc&KyhCC57@gQC=D zNx5CJ@xnlgbehf~x#uZM*Ek79Lo$Au(}$gQndJ<$j@|sS3l5Z_YFl${aD4EzVsmY@ z1uS}IOKsrv>KX~4?iwnVuCfpCmEA1u!Bx^Zv1<c+r9aijdk9wiQB>am+;6KqyAA6i zw08lrE}e(@S0dy-pi(jx$oXztUu51xX59NJ1Gb^+qS}-XcN@jC94(?dec>$I!OHu| z-JV_zBugfqIzPyq6N4`cVk%!DyVWa3LLxkO_hSJN9|pc&1+x+DnS;)UG7sijK)65F z7HyGYA8rY6T*Hi2rx49hZcG!3X3%8{71Y4fq~L|KPF*8O!|zv)KCvAFZMb{sEqcO< z4GhTZQy&5D!F{bpVO)D&v<JR}sXcB#pYXyN9G!+D-cPw~ihJ%bIqyi|ZczEKS_Pl^ zrxd5=k3f_}(@4iWv<Fb=4YB}RMZUi{Y##s?It#S6cLyf*k~hZT4#Q7#vlF}CIkUh_ zIPD#s$d(r~7TykN*tR#hBO~%;xk5Q(*%k8o9=-$(&Dpv-mg9sj--)mPsrpN|UXRei z%u8~WhA(Qp4WHI)>DZtzTkhWe!E7_!$`n&y>G0SpiOb0zgMcx>YU0F&xjj$gAY3Ac zw++I)N)NY)3WBMstbL%VnA*r(ZV{i{p|9rC@vyLbxol%*Z4&tZp%VNPcXV|Oius@5 z0DT7P1M;ux7k&Y{3Hq<%kQ9IYApRwH6oLI0?C<vk3Yo$X362hoR-Uy*`O*ME6Tu6W z%9@v!ER&l3#$NVehak(M$rZyF(ich$uv4hU5mxcS-cjE{^!|<(BKrCfm~?_-iPjo$ zq<I}p{c)OZW4XIq9z^`6aLpM^_GcpgcM^Dia<C1o8`iEu5yM}W5c6*PKBS7$Wc?A9 zB8Hfkjbf-6kNE<iWOMnC%dBg`^k+`2c^hnIv<hl_8~P->JE3J)6~goG<ZK_pbf3pC zw?SNFYCFOJ=Kc_x`*lSymrLhX8XSDY^}mANaRk^8cZ)3i+s(s1%eKdYFSgH(jJO43 z{CPNPPD}B0%FXO(ee&4JxapRQ?sg+<eL5k3El?n(Kl9>%mZiSiVImgyRg+MZX`U7X z227Q_>e8Xy+g<51Hj6MU4(Fuy4AHM6WVPdQdW{TV?8vw69^^mx&~Yqe3=m)8DwIoo z;tg2h7Y^_MfbRakJZ0<q!RfE*F=lr1U-3DM-@dR5;eIj@tpl|C(b=JJZBilkkmV#p z;RWfTe`cwHoGps^JgTbY^H)rTde7ZZ)XoEWgv2=?<jh}bwwGj&TG>!e1p~LVZJ%WL zIgbOG7grUZ%}&xcSowW?HAGF6TD91&n7c!@KF|E;gyIU5-T+T?XJvEQnF;do5qk9B zfj%S@0i@A403<4$d81DlkK9EQZ9IzQQSlK|c7c9%YG0vxcY{N{P*rlqaH5GdbDDP= z5qxazK#%*1eXTH3-Jh-UQXKSRu~Ydam=dvb>;?9Ch$F@3ZGIAzhJn6{8&I|YYbVal z5BUFko_xvJEfDvh{r?4QP5cFo`L7;zo<M2;qteDz#P7L(iQM4O|H^M)<p`eA90iUD zj8@Z9{{r-U$V)m3W5uE57K({A;^eaGT2_3oAku~@_9GRGv&<q@#!8<smV?mrcD+%5 z(ex3VQ9VmQ{GhzyNkEE^uS`!mXEDq1d4Je6yPf|1`xAN_W6S_<TRB|I;LnhmPT01S z7)pe!lx!%`P4GaLymxp_qAYV1^n_#~u<v-t8p|ma(dfHdM{QUUy?1ou0AILCIJ*CK zqea>!o`1~BO1qzc2_~FB$6T=q?NyRvNW!9%nB&Wwt1d(JSxngjTk+Ng6Aeg##rkT@ zS+ULF-*+WmJ05ZGX|~84j|THUM<OQDf;3lc8a!5sV-3Y5jCkwmQdg(1o}Hyy!1bG` zv(%-B1+l50NhWEoZ^%`H>Mm}fOWws~jEa1Fv-x6Y3yk+Dh@fqt%lg{gRoMQuCcJsW zCSCg`4!!<@m}Dx2s=w^>cIi)IZb@dg*1X(?7Vl!OPD~IX!vW!{;dZ6GkF0iyI>dlq zKS^TNLwIsD8f;RCCe|msc$dbEzc|J`<Jt}E{dpEHaa^UX;!@jVG`DwuSuBHD9<mcS z&=hh@bOUjv#rg^9VYRK)mURq&tvgkO#GCPiM}^Q{TKybu!C|j0+u^Z>fy>$97s*YC zDIj1)6Kq>S!LoGyRX+|%gU^hD$m$(Sz{)!ii{p&0u-{3$iZ|?=ufG^40@TgV?d>56 zw}IzjBy(ls6$b?P(yq{+i0y-k>n_KEbRE&FJ9da~+8C;4?9tX9B3SKLFTnZ^*_1=S zi{36cL~HKN$6ow^svM$s-Bo)5sut-qNDh~-4EikbF7>(N7U3_;AC`)DsP01;U3kvc zXT|HX0`mjEg{p60kW9vB0%0n0d4lgWQRkc57M+>d9_?+7m=bifbrqKf&lZlSa*pKn ziIyCPw1S_gz2OgslChk+&DF8IT68ImAI41bRheMU=f!JJCpn7dBAaoUlbbqREi&cy zg~!`$8MCNQlcd*{U_+epu;li57A~W@rp^jo)7+zt%ONV%C!89NfjBWKJ*&p74&cJY zp3aM^D(d&*Ms-6qH0%x<NaOBf*e7<kYFU|7oq@jHg$pI1!a7WaB7X+kV5<FZwg`Q9 z0ucdoxx?qZ=t@=8eic}UYvCL>7)7PD2vo~bn>(Vzuun0w4)0*T1oB8oq(c4&0)!kE zT#46_qJ*b8_rC>?fTqJWo!?_;n~>q(qQ?QB$c<)_WC62+tnNYCX01AVgQKuY3{T8N zxF)ZtL}MYmt8v4TV<`#K*n6z;sRuHDeY#CJEGwp<+!Te=Ug7t<d_q$WIQt5_CU#Jq zF80p2g?$B@AWAtTPkf~jcg^~W;##E;uj236#kbWxP?rFRzy*=ptvh?}0XBXrG?~V` z?|g$JK7KP)ps><WQc+%8VFN+Nk^zNQ{`>U6k|>e|;Zpizfg&4V0a%sZ(X_%<!o)s= z%&975g|?4&=+Ge3Cxh&dyhpGU6_XdR58-`}t~CRNOX>=)^8>D*i~XTddVhrp`1^(% zftM27s0y!#2*Ce6#gM_kR3QGHn`LC;t5JWw6`v;b=wqk+b6^1_4V=tXoh_`L=#8um zoSc$Ww3O!s(LR!sLz=(CP!pqzwXMJ+mHA;_3kwx0#nqMWJvGX?&8@JWkd}=_2}tV+ z4)(eM6Zcjd(T1_nU+o`Ur`%TgP96#LV!qMlWA%TV;~N=>8*GGQVV<C#LDP;E>eYth z`V(e^|0TPP3c~@ZF-%&i`su)1(Ek{O(PlKCY#Bin%}rzXRmh7m-ebyXS#zA3>|EZ~ zi}3`CNgORXU8Xy3Y{I>&&KtFAI%|vU6&qQ-$?44Fshi7fH8#Rus^-iNXb}_Fj5)zZ zvPyN0mFOK<C^5nBJi`?XF2BJq^X75r%x}#%2OT+xRDuJCRmQ@o|7uM&D_fo}qL15~ zYg7q%mqdmrz)<q3Ji>+0OU-KCw&||#MmOshS>L3OBw>?(7Pd*JH+j=U_vEwXopoYR z=D=Hah8W>`aX)&?n74N<U*lCB49v3!?#Eo$f*FM+WfoUNx+qH@pJ|U(Iu{xunMwY% zFfs{|M56_k{T$Lkf3(*VLqWtUmOsigAeC%vwJ}%7XtrAtJ5rphq#UVm4|4&J#VY<B zj-=g+ONXJxH%c%#*JGH9;{@ClzG)uc&y4==WD)s;<lFn;ZF}ad;B<kB_)WUoYHdjA zg&-Qmf}Q$L$*%3HQGJ%OPzn5JM3a_FR!cozvavYu5Y2|sp_0*&7XaH#_eV9-E1R&w zL93GwP+bIbGVi=BD)IP4RbP}Api&vvB5bARuq2nERb^KmN52h&a=%OnG+<oSK%1VE zF8DROlEwAyRWgA-vj7X4-3Tkcv>C-7zn@jy;XBF2?jsa{pOJaxFLa(Edqb?fE5Qaq zM5rQA*A#K)j;{2A;uh*adY?!bL{-8Zonitk4*{vz7^ff;s*c>@*C|Ap6<P{N5VCf$ za$L0FdW<krL<JrhPoW4kR|eK=5GB=f)KAR%9AvYn8HYuQryj6H`Fu>}??;Li)zJib znWRI|Y|<XX#|S#<m7R`Ie-FO(fA<V0PUKosm(Vjjx0+US<!+4qz?`R0q03kvHH^9R z%$B7`8Z;a(Zk)@Hn%9j71F)c+pj}AOEQ1uHEd`kA2QkO0iUR+4TqnO;qaOH%`t6$z zO+qaV*1rvw5(fi@2!z&FS#JB}H*M>;N5=vOBa0BigFq59Qi+1jg+R0SAD|ra#I9y^ zNF{1$E!0R~YHYSxE7nM7-7w5pp(~q3W5bebskE6Zv}*5KF1|Rk($jlwH}Q&o2OjB$ zU}G)3ZN9#zdR_7z@oaTx^*7k@xkiK~Ur*m1t9r^6EO#Y}0PWmQEIsE%J~L!kPI7R% z4#=XX+*28u$4Mmc5j;4%&JA0J(o=3%Nn*7<+}Wn?=FpZ7m_}t-JnDy@b?LW-H6AQ@ z#rDc{)9H~+v`J$Pqh&f5Cg(fvCFuXo`Dhp4IA8a$bS_LE;OK21r16gTqvL1O4KUo> z7Y#YFRo^PX10h+fms*GVn{>-jbuHbyhg@_uCt&xD@>0$`ex)2;8%8tS&N1u84`bdb z<<-jKj|Y1OGIsPy45W-|6%5JgdIo3kbxJ3zdwIa?UG88!n<vFK;ndvOXAE87drFfp zd3k_-t^6mXu-liUbUeXy&SrSr%XBlx`VM9sI3#)70%zSTB-d%vPnO&>^t&|oE_8h% zuVz+eS|#>!hq`bwaIO|wr~BWDJtg<7a?w2#M(6D=ZK4LFd}O&cCS^VUz>ymrl9SBg z;_0W)x<=QnmntXZ4Rp6pH7-IF*Js<aw$iNF1rcY&{}MItu|~kXaSvtEXMV2iG9Z~# zUrq}P1`_+M0C;UB=DUNiF;C@WFVX+uGsTbY)U?NsuKu_H&wp!H95|OmxKFL4FQBMY zln4}mk|x23xAU4yJ4XCITiI-0Su+JCM&dGb>EEu;YPv**nrE*QiH*-{<iH4+Az|&v zr#sY*WV$}MvMnDQV!RrvLH~`EP%*r_h{sL>^a=Ns@tS-WhjU8cMisYL?_erzoi)cC zxsTnxida*^cpaWEMH$78M92-s>JsmpIo3Si0{tRApF~tchewg=v%o?ekJYijpqVTg zQfg9MpCyF%8iNQH6D=7MQc8bstk&z|Fh?NIrL5$Fghvi@W0srh@o(}zze;*c#QIuw zWz<X$&;pOqBEc^7Of3#A5Wgb7TQjCKHH?x-sLNf&Y5^QGd6@<hpT|^GVZ)i~I}b$h z;fPcu7>>Q-czOAM4?BFR5T`>3#0|`AS0ptUa>Ws1(KYM;E|T3eTYPofq8SlNiq4a; zWADB-REUgl0hiXb`&(yLO$ddlFPUQ+IBD8ujj)GmoYpExlo$dx^g{s=2igRQ7MBr1 zi=@}S9@Cv}3w<e&n$&dt59M&kJX>KW2OH=WTsXuHJrS!bQ;k6IT)jA$ir2+-r@wj4 zuB;9HR8+H1APF)D1?pME&FQhNCbVW-R$h15FTq3o)9ak&VuxGVuuhdf)=6#iK*<nP zqs9Gu8eA_rjf+NOAL}vu-zX&h*B5p;8JVfg&kg$txyD?1u<WNeZiuCo%OQ3wOL5Tr zhV$)94y~#y7z0BGa_h{NA!UqmwrpgxNs@!%=~t(*Vvk``wd`D2(WHjrj0Sc6Dv#{X z63)YZ0`?z{jB<G6`w@PrLuMALK$D?KapDz*H|^)o!WrjE=_C(mk|i_prJxCWJ8B-P zWW&R$T5Q>cGO}ycFk*;Mh01WD9d>U6Uiciol3^Ck8eRhVR8}KWwn>o~uHS$584U@y zaTtldGd^|feD1c<_m@xFvPnlDCtFBN1chtsGbGO(cgU(Dbu#g;*xjdw1Cav$MsQk^ z-RgLov4+&@n!q7YCrR7exvxWD!4Jr@%*NrT+T^)|oKNFhea4s@%CTafOj0r-k9P$Y zlp?41A`i!!nNsBip^<w9K@pqqu4=<3X?8O@*(Ey@;cIX~jDqPqO@k!gUM0cfgtFn! zaOOKCp0s~v=r+_dt1Y840zn>wLBB~k+RL%B=dUMy)XlK(l!qIx$Cw98!T>eN>m3td zZ?)ZYOdq6L>UgZ@>C^lghXQd5TGF(!;UxABB{57jkEhdT=E!jq!p$43m0(eBxV$h& zj0{X0&H8#n)xi2)wN5Tn7Ake6TX6fH>s9wsryK%M<wyHMVxlq@fK=pXQya62HS)`) z2zHgD-$>w%rUJUSuMBt}Zd|xG2Cf#qEC)h+xGvq{Ti|OabjjB38b@w-Ue24-EsSy* zrQxU}kEd_^{JHZv$I~Permjvnzx9p~ud9R~18WDsbTro@;-s{YsasCoi1>S)dsj0o zvIHgwW}jU@N~hdrfXK!HP9;29eXO~6SpnUKye|kUmy5qP@_w>1`~9f8(c#{jBnVV$ znUpxGn19Ca#$@2k_Q@O-!1s4-sWxY=j8HLAW=2z^FDu%-Uy>YfwUXjF2q0N=f|nk^ zZz!1xL;T6fp0-<`AN{(Wvj+_ywA;Ip=T3<nJt*NS+1);u4(van@=Dcc4}_xQ1_lW5 zF@a_jeX7HGiyyr{H{bx}k08Hdi4nuAVI`bIB9X5o5=e7%mY-m`fxmtiDhK6r!_PHw z1JelRPJFqcQfwl`QO%p9dtJXsco)$}9nS`cK(V8KXT_2Z3t&4k{CdP*ME6u|Z7t&^ zXUQGFS7omTc*;949EVhyUwP-?t8#?;hNbM{TE!{>36cM63Cdk2Vh8@lWQDp*jfKtw zH-lyoU*!ml-BUuq8UHW==Zir_#m|Q#A{~87O6#G9_8c^%H~)P02A}nNbZhQes$1-+ z<jEo{XUEK8idTJf{+SDz0|=KnwRL!9Tp06r>=s7=SaSy(W$3%(VSEJlLfqodM&=UX zOD@T2{eZnQL<qu^4Z-ZiD_N;f#t2{P=}ohUvl=bpPwPenS|T+4Blt4Rl|1l0xu8f$ zRzW7?VN!U_vNs_3ZzP6Z<Nj{ZcvQB2O`Gx&<}=xkF!JsQNrj*jY+FauzoHV{+_NxE zIMlLMz%8}aA!ybD+X5_8Iv4CDQn}7}XzC>DYIjIq*0`+EULmPu^0gU@Rha@6!Ia+} z8sasU;CmS&j;T~*t|}W5u2k9Q;p>lhVsq)HTwUC6s#Rf{qRy2}M3ALR<%e)m`|Y45 zu)n4kaZh2T2u|&6lXNsr0@|dmW8p(!3{~A>fp4ltHCTT@J3K`kqd{J_8)tH$b468K zh@|()pR)<0SA^!E76(;02Mi63?3fKN@q&&bgw^G8XQ0>eD=F!8s_>eoQgLAonACML zM_j&XBTJ85$0z|8!T3<_;kRdT6Oiqy)1P1r|G0$H7vlCCyb9*Po}fN>X-JRh#7)CR z0)ZvA%9&n<j{7-9)&1lUVBmHWKi~?g1EJ>UXe+ahd`2ssRPEg%TP@Io<;9Ji1yMTs zEZ_Ltv%ayz=B_1EcXzF>-7s|tEmax4Z_~<&1)>ufTH2c?S+U|wDt7PY&F>oY&3&*- zs;)^2Irgq^kZ`0h2j0bg{y{rnMV1lA0g`m3WQ5Olplz+tP@u12D?>p)ZDZNhmTUDt zrIp_(&*1THaa+p%HFLmZ0OkO{QDze~R!c}jMCq71u?A?8jw0HXqeYC%yfiZa4m0;| zZTAfaSv-M?UNLk=S7_4YKLg}<l}Hb>a^&}=p=2wWiS#DUn*g#PFcr?Bl@+$1KsA@6 zb&Av@)80(gmXnespPL{qLR%U<1xD^LIP{T#61MTg+s8R)JM)+jF?}wOF-<@-`Y7E^ z=^t7-sjYJP+xk&d>BT)m;7grbp?sv%J+1>ZuuVo$q&T(^%!LqZII#e^E4S4hK5693 z0X;824p`)XK)(_DxO9m5HF;?P%n+=5tNbSGf$S8!STw5PrVA0>kVcCLACPExh9Oza zk}>?ybCk8r`azWL?Tf9UpQr@o^M`)k)t)A139IjpKN=V@v5paa8E<=+?QF7^QQ2J$ zj`wpVN?kKfqy9q__kfrS_K8cysKA`PZ%4jWHgHHl#5)Jmj&m{_FNWG2NUkF%Do>pN z%D>s^_*0bxLD_P^17=UfunnPy#lMC-Dry#;Q+!I|I$lJIcf+zi-gU1`>sw>!gW*Jx zk8@VnvqY+kf%Q>O;Qm7!0;GIZ>gB1xiKSa5ZH{l;Dab)2%*jNwOeWhw^#{p5y`$wa zL$@%0CeMPI&_{G+&C1m_P{d3ZhulVHlIivbgm9=K@we<?5llKlM7n(5_AnR=Xt{z& zqAR6zg>iGTZgoJQp)(U9O=;$))UpVovN_R)A4e#oBnS2CU1_*&-;YzUs$4fA1ukcn zLyCm{I}8tv0=GNd++>-ny!Ym~tsv%1EG>}kKSZ~iGHu_Nkq)Y0fpEVNEcz+<nj3vb z&=C?5XA1)Y_E<I6aVgy{v7Xl)yq0|>rt(-AmZQcvFrCBH_S!;j%g~cGV9%-o;cVg> zv*`O@aWrd;Vf{j}-D4ydgj*;p<bxNkR8f+I2(AJXslso!i1ASH{6tlAZ+u_;$dEC= z1wy1Q8H{Zy`9#620j)wx?Zdn?LM<Xz^erJ7+?IrU@|Dak9H{w4$;Nexst=}=mQN0B zt*RG%UXTu4w_VmwBzm;DCW|eRQXH7#)cYBkG3JUCJ;Uj4y;J?nX;w~##@Ge*;GZu1 zHX)8L{Mf#AWu;p{ik?fQHANlHRNV{dTMRAR+!GZAsjXMu0v`-YK~=v?zd~);(iq^k zO?_*Em7bBDr_?K;!dc2q%EWlnrLap7C!@E1Jr3*gz<~;LWsh)0LI<~56jAZ~W{O{2 zPuI_?qr$$2l|(}(<b;`-6R+=1L0lej+Up+$PpG7jQd)-;VvAQ$q@zz0l?0Vr;70)F z3^o(!7yaE633%y9_lM-y;hWPjLObfurD6+<g1Kl>+sL_S&QB9(^)c1N<Wn)3<cQpg zdgw-HH)`tcZ{NZrV74ZR(!tfs%=cWYef?nas#Y&flwi9IspX_NaC>E;L>NF6At8gI znhiM}sNzz3tIX#76US4EMgc+u4X7H>8zPS&HS%KjH^9UngNaTMl0T$lUI{}bb5}mV zeCA9$;8RI6&@m^tPc+<usC%C?{R#+w)7WPo;>jNf<e*$4DE>xOxde%ID{;(&)l1$w zm@7G+XFpUspXlW*J1;DO56cVTS}cH~ph4ybZG7x+%~2hRsDe-J(lxo%4pz&2^5r%! z&@pDo#06eI`>2YbVToBB9EZ+1I@2R*C4XqMgQ<3((n7_U5W4q>UlZbiBoeygXNEH` zAt+BKpMpFxr3=WqXnDiN0^X76aEb@D7~N)(=&<u6L(QrBjMD}tZ(}sK`qEJswsR+& zB+Lhq+Xp_dGQQ^sSNo>Z=bGv7n(drV{~}PqoCCtzKV0=$=*hN$w&qUvKT3A_04&B4 zLto+@1A=QmQtD3ql*Aipt`UtIm+*(y)Dz!Ag`SbQ4QVV$#Qo`S>9F#X&bDDw_(QPr znosu@IG4f%{C0gM!ApHf#GOfe2vj3z{M>jg&GKvJ;-WvG9yEIA!0s=;lSZ4Gn=K+$ znSPOn;q!~ShNKG(gHc+$L{s8A1rPGo1$UQgXYFbqa(=$k$(3x)(!Tgm-*4ED84+fJ zmhy7TQwTb89}*N8kZFKt6%HqDQhyEVA+kcPE3~c^zUc-)-E^Z1gs0ccE?*>aqEEEe zwUTXXK`pIYd8Z%(WF)Gb!Zbm<o8q7wx`8gKA`ecQ4K@$f9TUV1W7cJ`7QaGEL4g|k zx(XRej>~}z`2zkq0wNcmpoq_~I+8#!oHyL^k1YV@$M+|W+CA0N!sQccPvw(*a2{4( zF;K3RRmm+ua5sHBcpq3h`E&YWikri}Zos>>)QK7F+sz5<`_Gl^+5}npgHGuTYal!y zs9O`QH>hNoU80Pxg_AaO!OMbBx;9?{oQVMB#-v{P#l7LPPn~7PR2;0Kf?=j2le|%A zynb@|RB-tmtl5F9uYKN?hrGwzWJE(IP&zx|RM&6q!{$Z4yuCvuVB_>BvUv?x(_Gzv z#y+ipq!^pn)XPEjNl~>jkJ`Cg0I*CzR=rC`Fv`_3e7tTz=j)KhD~wPw`_<vwrdG~n zIVRJjJZ#Yg$D?-XX0*&O$Bm_0S}0!|8o`xfs-HQ@J$WFtZ)TozR|fyXrtejiS8TKe zrCKFw>n?5({1vU7L#_#&fpC*-rMkYOvF-W-Q`P!)2brO4Q=+hj=m>k^J20{Gs_rfK ziyyTQ$C{_13@KhhV^DfUVy?=*H|ToRV35i#Nwh_Y&??0r12BV}Q^ia;-UHL=EoJ)d z(T{xvJ(t{J30idgmaZ^!_+!mG>{jt$<9c(_T-T~-f=+nM9O7zf_A<##iGDgDnx-_& zY@WEfaC}8!k{2>hAa=<C9q3|DSd_B(v=Oqu3sG$$$p|>0z&##rwzJJmIia9#)0P(~ z$`L9i-XF=?n;i3KNJNEMU{F&0Ob&Q))AE8HdB#Yxx|jL;Siy3P^9QFZf3`;12}M+K zN7XLw0pdZ6>yD%6N$Ul1{XnljO0nXR&Kj05y+sm}3l{SGq(@k03~;ZpY3PR21#y-g zP91Uff#Mi1lq^r4r;1h)p60Svq95PuoXI@4>%n|&FU)qF6z>Mb@p^FPN{RZwM5TYc zELpwKG8nXN*Ugo>8%aa?R;bF7!ndYgj_=+7Lk3-uW{R%#7bgSBut1q)6Us))4YoPb z=~DskM@J9MXK!QE4A9IfkN27sT{VaB>Fc>zhIS?B*<ecA#kGTwfOc(uvXQ+6kw`KM zPeYPIviQD;;DpGvhOCPZee$~q!A2*76z*i5OVmeVlu!2kdTRzyu7ZA8s<Mvm+Mf%9 zJ+=a97oBp;T-urdU(&kyz#71VnEFk_@(GGSP3<gH<g{H_#BiLx<OqrOF69k`G2&B0 zP$K#a1NI4o+%f<%nQI2F=79g}4QweT1Oxvs0*RN?`62Ra=-mRIGHVM)pK?MB1`kC0 zC~irzk!iyKpou2x`hf#<rQ;IKmFTV&t#|jF-DqW!H5xXSu50eWqsT+dfQdH~uAp7! zJp*#>W)Bl?-B;SCFL%o=1mBu#i;Tb&*l5qf2uny}Izs~L#SS!PYs(FyiRx@**0&7Y zGb*yL5jcL(XNXTeTCdWodTM(i*a4fQ`%?`Rx~GuVo#=Yw%GQ*AG%Dcs@$ABf@}jCP z@5^U-HNPNaF@3JsG8IxQ!7w19+-p>}>&|aC)4*?XvB@r@im$ie6}V2&J<iXrN&p_L zE2xs$rQuGJr;7&pXu`~}2*$DuhMbE|p!J-wK_0&gz|Y}O2NJE<7Kx1$IRl%2LrIg; z8e1wlchnYyNw$#ievVWHOD(m{lfRi({}$JJ=}Y*-L>(H9YVcCzjc9OeDA^nkBpmaM zONn(@z5n=fiU_^A57AKF{s%?Z;wKNVKy9v*JU8SsxXn!400k9Dty0rwGmTbbv$%fJ zTUS#C#cfnmc+yIjKHq&Es{^C~$AjoM)GOYI!CuBWV9_hAjfqCfHb^jVwm)lIo4W}h z0-hO-)OyR%iX8pkXaTm8pIR1Y^Z_X>V5YFeSVWzknFE&qPMmK&jA=BF+1!ipID?Xb z)|?esM5i5Rts+3C)9^V32^8l*i^ZT2uvO<g&K7RmjOYf~T88hma=<IFUO(`txemWn z5aPfaKgwumSqNtfSp)7rlXKKD*qS8)UxAh~lpz{_V8!&}qvPZ-7?8On?r98jPFUth zo;l+tTn*IYrTz^6xM1P!>5sF{8Bs+^y9(>nYZHC@5e{n}7&QKUM;OynEVY;S`Dd+X zkXS@md}D-mA8LJFt_6{i&^T68T=quY*8fbJ<j={zfAD-2D<I`NJs4xkRtz{KuuA1? zfrk-|?~>v^&?eXg4Q9`%wi(wL3L4ra9Rgi4m>FO5?}l9ElJcpnt!B^wD8n0pyYTM! zn~7NAKdrM%>>_8N;f7n<1UXI$)<=&y1OnfAMaTldE{h$$HX1CfnF=3@;y=FkuS=h$ zDzqLrF1D<7CztElO%>wM6GWc^fU>O07tEwM*(6wMno8jXH?<Vbp^w^QXqwrR8}!S} z?uZG{UxYrmwqhk=Tq2>G4myQLI(j~no6(tiKDx>}2AvAN?U={ByBpEYg}IZ+Dhe-` zkhhVev7I$0FgUAUyV&g33agq=hSCOe65i&g5l{M*4~34DQMr+K^ri7Lz|khL+4nUe zLD6>9y!V)8gvfJ8H`%9>B##88XJML4&B@0uBZJq!r*H<Th9fbuLPW!d;An6M%FjcY zpK;rACUvLD!+3dTfQ!>$O^w$mKmI3yRI_9n+I;)$S=n`5E3MAt-WDA_?Z@F>xHaRN z#0yUn%@Vgh0(W@?DPH4NAip}5z^^6P;aTDpoTdW~jQ)rCZPw<mA=U*N-S6i~{|b6| zfj=Udz97BmB^2RkSaWE9%j5t#H5Z8lNhn36uLuv5J@f$F9zTUvS({0u{a->;NCzj~ zhR3%*do4NJ#Q`Yb5l*JIe(lnf+~hwI#d4GhV8>RTgICmH3|S0w)S`bnz;L3=D47Nh zS%d>%EN~WZIXaGJyqQ?tggeYwIqc`7zU;|#>Bz%2EKUivgoyr;w0z(!;>$bK__DA6 zKc8U--Rv*luL8^aufX_Xzqn@ElwkA_p8?^BAE+&Jlwc|#Q2#g(`L`0PQs`;GEWtk+ zZ+Zn<vS`56L7@Nn#u}eb#3$vQ4$Klbo%*!h(Jh%DzOzbT!ppM0t;>2IXtU<zIpx;v zl$Q4K_V)tP@8~C<5QdWmFP?v}nZ|^b#zcOiSQ=3S{s`fw{tBI1tVbjdQHslOv?}3Q z(gni2+6Zp6v_SEe61TpjjaaunRt1F8Ih%r|@Wq{`WU@Egt-E`VYVL1TBWivi3=Z5w zbE0sXXaGNE#jkQ6-mt=cEs7XSsvXunLgzmuml9^LM<u&OFmLUtS~?Ys`#4M~{Oa9A z`l-h>**ndcUx9x=8>yl^Vsld4UtaVn7eG!D)<Wr>-Gq`zvt%F0br|f-iO1Gas5fK( z6pn!6vqS$@JGW(yt5b{*oqAXYuGL-dN|yyPGBHK5%L+jcpR=LCv8bQ4w7)1oxkhxU zs*_y8s-8^iw6ialK8E^1d>|gi97soud{)gOb68R<bRyhamv}DwXh_VEdy#c9TF!8H zA>RP@*Rh6(`?b}Jw5fc3i=R%)6dV1C(SOfi;L7|otwqqmJ77$N+;V~g-hl=*{4_9s zZvkb0YGX8GQpddEky1vSrb?IBs&hVOE{qJVc1?jWUDp;0`5=42$v4B|5R?(s%&NqW zZ@cZAm~*z|o}};T##*85VgUo??<sb}rh+f@nm{^WjDZ$O5y5Xkxgxc&ko{$w@iUql zDS{pCl2mG0O0zvGSmvjm1=D?+a|OwkL_*EXkoE!Y5bI@p&JMmQN->_{68g%PL6=^q z=qbpFZnHwjg#oVHpy?hzKGyGOja$uqpfNGQ9`tA;_OhJkt?@ib-ytW)+AYWbo`?7P z`C8upSFZ1jU~J%@BLk3sTR54(ltKRUvf5r_dwgA!`(H_quz+Cz#Q<o4HAjuG*%Xuj z6gdnU{>FH$?Bb4udgC;*c^l|YX}QGG|F>p0^ciqo^fNa8AG+SbF%P!+5^fsXw$s=~ zW81cE=Wb)$HX2(^(%80bH%^-5yVK|S?e6>Te*eRnbDcTYoEhhX%uAm`W|o(~4|}$u zF&s9n*_8zxH1C9g<fsrtKOFFRmDPWq#^^m2+7E4R#dlb)DUbVYT5#r)=Jt8RCK-|` zAbE#h*opEM0M;wgpJG()s)PAs;1opDDQRlI8;Co-!2uJOBMxj@aj85R62Z|VsZt#F z2Vu*b``Ythsin#x#IqCPho0G$<uH#tpK4+Zj_hRYy75ry7@A2WMj3dtNrzeZqH+;3 zZnS})gE6wYpuUWIr=7PF_+Jmb+jFIM<cePs?2?;USdsT_VbSM%taSGH5w!vVN=LRc zDRBz%kWDvDeq?K<I%g!2vT(by$_isl0C4c$HPh!531%>SK>H4f6TsS~0;egFM(@e1 ziRJaBvzYWKwj<FFiQO`1BlL*zmNwET$S|)&sR~ViNA=I12-Rxbv~x#OO*mU{A8?C? zP4LVWWwfECgagDW-1)m(M{pPV0fiYZi5I2iK{vMOtS${2B3`h{_OI!Sohh5uQ;FFW zk$15p?0&W>07TpZk#)LBQv+`^cSfdct#eHb1iRcNYxg(#nj>8yCD3C5!_d0azQnvy zk`UZl_w)udGl^!I3bB1?(Lw0uox%(<)V9cdSh7xoJ<#K@DxVzLP9I3;G<&rA7FoYw zp45mmw=hk017Go-*AejeLy5b3UpAWuo)&3#Sa-)srboK51P;MBND3zqL`jPAma5qV z(hLIRCd%5ixUT=ym~z|Vb|~H-Jjj|7!wyCcsCb-`GV(~jkQ<3NO_pgU|3l*&ClVF^ zk(AC(v#Y^`_jLdg_Zb&(sk5os{bBXw<|i8iUpPzJ!fO{ejO?P?Qd9mYAso@*xnhP_ zi->L4PN%WeHRu)_&UuNaI>nW9=5w&0%e3%D5OhJH!F_T^(7|Eh4E=tmh4d;>i=coy z6Pmoy0MII*SA1O5>9|dk8uuc!5i*q9Rvl>7-fcfB{0)A`H3jGr^r!F2d^>s`YCHny z3`v)4eLc@&VZ^hMEw?Sd;Xr!lzFxyMuPd}kiR23#j=lWpn`4`T6t23Ej+p~-scMG( zL3Ux%CU9)#D2=MP(WIbAS1}+;aFq)XeLQ^Hpzy_pCimPr>#QV%<)B7ygEB_#^_WFN za~{(d?q+mP@+&EMPc>lJ)ORJ{%T_4rUJJ5zj%o)$S!}%<TxeCbgr05U;Fkx}?T6P@ zoAb<eU6gbCvoF&eLl`D397lyl3r!ACC1YcFQSVD#->~${%Lh4)2o4ost{52@gO|fC z7;1`h!S!!H;PvjrZm}+4t-POqg9p%$X^ydq+{(?&1vrNn&n7eAC1b}w_13vz2Ai$w z35S1hW1czMUv3Pj_!6JCGqW~gW5hm8|BD}IiL*l)479T+MT-ZF2moZjK1q%b1}u`+ z$YK2b-KCsn(>;M<u^UK${j`4x@W7~;?}GsK4LbBYMRq<9`!x{)2@vvs5uoS)CcwZC z0<8NWz<aV}?*Ar0e}u}6zXS+849W<M_}iyI(}Pi^sB-~lB8qSQYtTy-D)UQo8}{pv zP<^S0sip(9uJa`RqA;0!ls3&y`ceIvB#a`M5>@t(^Z?5&{C$2N;)U5m=Etx@>)6=a z<J}X@w-OCPVF*}kUL4`iF)SK$b*X}10+@xpdQ<!whOgQ*cy?^<Aa%Ul_>?+tsd;pZ z&%beGBmo4+a*G-!W7iOLyBQU4ik58gIRaO9qrTC$Y8_n}<k08?{-wJL{4YVwVPL-W zi$eR$YlubTtAkCbz+0Fk0yU>0Yw+?96LNj9`|a5p8UQuJA$C%F-6|d7UyN)DyuGrN ziP0!=M+#Us^aAXz@VDKu_K!2<uAj0l&jJ3_VLWLU@@<M6b0Ue&bSe#)3%eu_o>L@$ z5{&E<IwJ$)d28G^*rNkcRi$(TYr_nGTEc{L9aQBSq;|-3t<M5v6zJ~Nbzxg+R7@G& zk4JQNCkQ{|0M%i8G7UY+L$lc6iJ>&tPSXE@L+N9%e{hH(ya)i^sKq|;{s3aVa=*M> zV4Zc|ZMJnl5PAhO8Nv_lAu}q(u6-<w5HRzafvGq9L+eLl!)T)g)M*|`VZ;flbOhiN ztzasFj`(9m7bo4;8s73b>Bu@TTTutJ63LQ4bC#@rzi2s0s4mv%(0~x$CUe8mA7s;D zx;?TG0=O0(@c+kyFCRRJ`@w@nuw?v(|M1}E|L`FA8ihbF{@8$wDiEO>`mr8eKHvAa zlwNHF;8$M}F5<^~bk|t?V4e45W8c4r`Gk7LOR`j*-tG+*iw9Op(z@>1|NBJ4{<CdZ z4!U4jKIM!D%n6Vq<8!r%H7Ucf6cNndlCjR>{UHN0mhHRv0pM3?)}#`~^ifa98XlPg z{>e|QFqUZ8O2F5v06uu}X$0Bck#ei<LWOjH)XTD6xDowItY9wYcoFR$8$~;h5^I3E z6RmxGH(kN|Cq^uVHnc9mne+bXrAAsP!Yf2T2VIc@fFd?`vPi1*1mQvW_-dhcH&}{u zd<Ze~?~~e5k9sZiq^CPp$w$Lvf`gnwpq~991EsUKi*cpY!z6$*&?|*7<LC2<!O*<V z@pK(x)@hYLa~Mt98GM`G+LQ=dGdU1qYP)1Wh>`tC@Ntj(3o(bL&4uVd+$0!$KwxdS zVS^z`IG0&95M!-yUGu5{nVN*l@ir7E`BEjoSx-`}*t!7N+!cV)J(5O_<fU0i{D+}( zD-dJeJo7vc=;wkNXGJanHG<Awr1`y5^-PiE!B15(txA8+RIU?H^D>>53N85-sc}a& z3~LZZR>lpf5IT>sFd)E6YDWN5&-RoyR{@3y#>Mpc?(r)GnQ<KW%|6??W3gj|iH)s{ zjH?IKO1CW2i%5LEEfDF*l_!O=abswh=E)_fvyyHI_tU?`j<ZOxuJGAAA|xz`){TRy z0vu(q+TiM1khLi-llssqZFkERxrC;pdn!4PxFw}aYCS+sQG@xXR!&SqAA0*n8<|qB zgPEdMP0U*Y(o!u$Qe7eLF*Pnz=wms>>r&234BIycbnBK|BD@FejFT%cmI<*YLp(&7 z4Od*VvT2W9B5Jsz`<wp!=ODS<6&fIC8#pT71oN>hfg$CB9}F6>s*J0O^^RYc0czM= z8OSabL1K$)mzS$SFG?kZi$fouh;!XUwpxSamNp(g5{VeC{D!RWGant0lE(b=WOFNF z<@j;S)Wzqz`#Eb|4ocGM#=wl-d!l*s$+zqB!T0dY?_rWd{F9jBGtw{G!BhS@!)W8| z-C#s!J101{Tzo|UVU=MXK{KZjyhdL<VwA>##`W|bJfRvgrX4hhb31`Go;&A;$(7tm zu$?^98ccuF+)te0A7T_J&2;NSG9uD4^_pxc8Kt*n4S|bTg_<mtOss=0!rCjV%TpDB zZE@t%+G8?h%WU&A{cCJe_r=-93i-i#nsP)@#ji}WBO)q*bygB9KgxJ!I=VXZ(`5-C zW!a1el$Z7uS7~|khHM&RRJgayN*v31DV|SomFRoe6U8#5rj|`=-yFuIPojIvNf3jh z;nyW*YkJyuT`>Ck#tS=mk4!ETKjpW0j@O}@t&z7Dn>`Ex$XvK3*K62D73$-Teh(xk z*HQM6f2#@ud_Q&(#l~?)V8Sdv#a4i;v(h}_@7<rxj=sw-y11}P#x)U3wFt7Gl(1HF z%Mmf)tk;W@3CPGuroW*YIVvesUX3`ZB1I98H;&D#FC9KB%Wqkx4IC+oOrv&8E8Ro# zGuug#^F}j?P{~_K<qi`$|BVJiK7LSVTV5QwV|Bs~C=7Ah^N0}1dLb%0U+JS5td+ZI zX*Dlu@YHot$o<(43C?U=#6_CK<}Bi@SkE!u;E(XL*P*spirNG7lBprld~8ZNg3;*d z1xD{z1dNJ}!gzlkld?te0do|m63onzQttLeSt_^?4D-_76uOO}wjc%%jNSlEp}br@ zwwLuL;C60^jhiL&eG-Fsxm+!s`?*iA0w<(9_I-0=bR~xC(BjUV^~G(t3NElm^}*De zO2?Lk-Q){PGbw}GFtN`5k_{h|sAOv-l9&{k`T<n*5*kjtVH?bv7r2h)C+YSK@}{`Y zpG9{(Yjcvz?74-emE<|-TQj{^oV?PG#WvQ80ah_V)aYd-O7p$*xbhWZDRzv3)^u=m zFpE09$HZRnkVJk2ZQKqRiKtP`TURsdo2!dlNQwT;FDPfugLD$_dQPC7gU+{`u#%iR zo|*37{<M6fhn!$wFMDao5M$QMuidFY*W=q0bsZ$<IBMRb@sGa6FR-h9N6IexUJw#H z03e?%#fycE=I?IJ6$$vp>jU=`JJ-rkFQ3DUAeDn&rb~423lZ&1{P7A^>pHZQk0L#W zF)#;zL>a9ZKdU7Y=}GYy$rEzmaDtv3mI&eV@y&pEGDx~7R2eREPpm@_FOT9w;Mbpc z=Xkw%u6U$)nNBYmQ>mPy7HmcRc1jIiS^&o>09@zX;tTi*;xr>YcoBQfEmD-^Bq-Ij z@FVtkdVDr;C)$+E4Th4T&V8g8s9xOjAJB$-c(9+nAr=K^gItvYq?n78MNJeSD2b+! z<Xh-el3#|v=dxb(P)<&BX#?Z&^4jo~x)gqN<zsgtuWw%Cy!~Q+>kDCh!vb&_RRZES znI)0(5Cx*kH5^P(UAd`7+`g$FLpzHZW0mxo`iOi+h_e|VGWO>d%7>y!B~z4t&b_J_ zOlpvp@IqgUyTuVl#oQH}Sx{t?az)+4p1w6mA8<|Wb{)iUp#IztSE<XGZr8mU^xqba z_Z3}9DzM*u2Km>~#uouY1<<Nc<x}Z>^#V!u&zFQo38k?39%6**nQ;+qI?Qa{w2JeV z)cI|?@9GTLjAYs6V#39?3}0|L9{@$*_}Qg^fFFoPcR8osc3SW<r_fM8vZ$VLcZ%pH zbBUo|?`L6~`S|4Kfh&G>u07kTRUP)*ABS3XiW?`)wkyDn1knfrK=*_rY%<FPF5G4$ zSv$-91J*re(YDTnI=cHD$DIy<k43uiecnXEPV$Qmy2MnShT~fB1fBTFPv1h^Xqww( z^JTBQZy6IQ9lMi8y^UkRse05bh3Nz-6z;P7lZ?!<JiRU3620izM~qZA2m(r$@QK~A zmD)38KqYui=Z^vnfWbaTY}3-X38S*pZrR9IhL5jsY!`jp*RhXEz73>lt+YR1Kn&VW zy3q~%9!5N^0u7F*tggT)lO3{oRwh8?k#aiOw&S4OvfsV*L}N@I3H#aVLkSL`lf|`G zOnJ;(wg!7xKXu@r62ACf3YwoqvD%bKI<H`Cd^e^*9wY(u%E%XIMIU1_vEvP5=ujy3 zD4~pc$Wb1|uVcR1;nBc7Srdu{FIyY2{WP(7Z;o(3e^F9$XPrmdfRx;=i5boy&K$jD zv9JXYwdWko9o)Ca=ue^INY`yB226`WskaLTNS#Jt<##2#2OdX}f{>6`g1|GymkBvK z`H?Uqo0b3!HqmHBH?g|EeYPvN?}H?XLKaOFf^bLEg(<*-b*nEFWs>t!dU`Hx4>U^s z)j@P*9Zwt|wu3E12?Y?}qoEVlB|cOgvVn9XVE+D+TId@L>46n%i`tqa@=Pg2csPN? zCwPXYC15Ct%zyHA5_&e31Xp>*n3p()F`IhQaxC^sWN9({YzFaa>}4H4TnFj<e+_mw zJ}wE40N-z0m=pyIFa`kQKWT|dHC^XfF*H7F>5aa)jK02a#9yWJmL%~)6sY=G$pQ;G z_X2i4$<8wLV?_Uwz}93kEk?{j_!HN8+i1#tJP3!K{XCU31DN7Z%vNQ6s{776#}e;W zeUS}J6}zjy-E3}heA4;#%86j0vtxyaEF+<lfMp|&lHiC2V4M$*A**UG2tDRpaTGS9 zm>9Ofb52#a<Cy;)v+5w7PgYBW)NaGmBtY#z%Hq6fllqi?ta3xXS)XN<*V4<X$Z~oo zR4-po?k)Z`!+|FG>tW=%hO;$d3Y~qoPStdLrsJsDdGztJ6#;NRY=0Fjmgr#ZPomiV z<6n`dsyD^w0B+CvYes!HKANtB)kYu7{)(0u41#Jcb-s3`MmcyybTAaxO&987b?8rE zZ5czbc+~B3%k@6is3eS&X^5bDvYt?QI!#$HA+j5g?&g9Nh7^3>0eTm>s$U~lYj^?O zbU|d7sf}#UIiqKCGj2Yk&SP4goLxSf)GtA22*0Mt07N!=qTi#vzS8z_UqeuVtl#q9 z)c#6a&;EWFXI@pMrH5srlh#7gd7wgDeV<AKg{NU9TFf^#{sRh6cO~Gb)!`rw`XtPp zJmb^v08M8$B<Y=|zHeVIli5+*!%@xHvVaYHq;<S<UYhiM)Mq?T{Eq0~?A+S>=!WjB zT0@M>QUG+)ITo7%s+;(&flzrr-6l0(=2Gdk{hxbPo~G6#YU>F=6%+uIwrVl+WpRx= zg3<bk#;*S>A_lnJWqCLRe}DY1ZFN(-ElxSCN4T8&<P%1$O`G@$z9(rP!!2n%mMiq3 zGHd%8h-AfNA}-hmIF(u>1FXYT@Aco{&-Yzv%YfV$7Yxq(m3ZZuWK|Oe`_*P@l5XGm z$IXduUqXHJ08$pk#y_h(ZraodwjG;RGQ>9Jvvfmd+L_+#tHmVbKd8YQg8DNO0*#l( zt5C44L7L~lnpwV?ub0QE`JsM~GZD58fAZ$NVeZe$Hq>du^P6|_i?S)%<~Pt}Filss z&jW~!&MHRhx4T)_sh|v9G=u3Rt-Ci41V0k<eY%VIeIf`mmRvwhX5>Th{Os*)Nmm)6 zPvi%)hHy<#fP2M(aI*m^%S94Z0HW}@gA?Ny`J5ZvX6JZZv^yuef8)q0SqIoge;MNu z;jOA*$g@5XGfz$Qk&fLiR|7Jgm!SM{YZTxzPRbgu@OB$>N=n}4x_7i|W+g`ZXLJJl z6$-a-A9#kSh=Ym+XRo<j-{;PP$Z6rGl4}BNrWK4E;!MF{9hY0u(Vrxtvn7O}B9|2J zco4&k@{@WZdMIQRafw**p%R%%f^z^Y;nH6J^0b7O2@6}DYnLtX+t#<v9AxbfylKFU zR-LSO^nL#k3qj`T5mF|0#Pr%g^`DlCT{<bod~t?8C8nf@ITn}uL#5hnR~M!`<l(#Z z=Y?lcI!mh9hAK#^vQ#0>xfvzFbzhVMg{~r3@<a?MSEV=<N2QCeC5#SwWDcS(MtUyW zxY1px!(S`3Z!^qyxPHo8G8l2k%A(sIA1B+$J7&Khh%i2X<41;hqyxt*w8^sALRl3o zn3RoFm^-If@^NQQybX-MB{3ng6zs{(Mme8jS7;0!1@Zij2+cF%2&T@6v7`b@xSdo; zSB4ekx}7k7_@B`+IW_)BVbPRo6EIc))4!CbqN|K5iN?o~W<n;TCIXsI)fP?>%SqDX z7pMw#qPF&nKQMn}r`w7`*U%+3j~f)@@-5zXS^W;A+|8OU`$7HeatYGio<kX`FNT*> zm2cMf1(>_;@b!N4dmrTaRR72Y38~C}u@~FZ5Y_H`G#IHjw(=-v?`IF#4Z(Z9Dv0_K z#U<o6f%y7Ju$^MGo3?~(N7U1_8kt1cRDMTV-2c^ud)`i_xu`*^2BRREEYt2WPiJF8 zC&fy%0@4QCMkE_bE$H_hH<Q+N@Fp?m9b@7MSo}{~4w8AdAi|)krDc~62}yUJd(h@q zVy)?jq;)z?WxsN|>diF350u|o8i90qTEX8OiluoCmm-AZhcyU*)=u3LYg^lIBDl&I z!-x{yqr6?sns~AVXUc#H*|k{_Ja_RQ9^_UOB<i9y<B!KX*JQ7Z$<%1ywclM|({6XH zj-d>VNMa`o<c=<}451g7FiWAZ^AeZH*Lb5@7cix4Y$cPxG^Z$#?uQ118ck&+sw?MP z252|EQx0Xp_yN-14xj>X9YiM%h_u5Vq*h!*0Su!a@^{V-gg1e6TI1cH9~(m<1Eq7u zXS%8Ak*j>UzuIynL;DgSLZfzUqVPWvbh8s6yLGNG<Q-=4n!XV%e+@qO2xza;*kPZf z=dI+^+*Xn9_<eO<EW3&keiyTmE8|#MOq%3{!4AlHF9IOkCLF{Ka%|miY2vcituQlj zX<^rjG2dd`uKa>{%reC~;+?qi`JC*C-^V4KC=MbFz0Np6*-pwky7hSy|J+&u6I-;6 zEk3szD{lL8Y>!__dY`ZG-M=w2yAeihf88Fd9Ib+5C=qsnpzfvS!NH19?WRby>-|A~ ze?G4TRtvzTD-tXgS2BuK%%t+jBdZ>@)sAQKY0&}OSZr-9?-pC9TNNXr+tz0^6m67t zhkGFH3F0zG?1T@(rqE`s5P&W8Q&(zEz$|uR<q6eSex%}z&Phw<M52En>KhGYOK%Or zqQi_zzaTdHy~J+vwTmW{Mj?NGJuk%aLbdJz<Q490_rrgc4E+}3t~4-XNuMHb0sI3$ zmj^=u2v!)$&_W>~L>t>clP!D`<rlHxk5qO7!xK?N=WGd{Pt@>UE)s*K-@K7_8lE2p zQ71>oJp2>>CmJvF7ET*07XQ>hHOj2%@a}YxkMCyjbW<NBwfBLGGqs5+K^Uf<{o+24 zQ1z9`e3z!<sWHml>8Q7v-HQDHdf8LpcGo))Ko}lHxuekX9ctB%Ma^%iYJM^`8Fc!1 zu8nCud4{4}D_Sewp=>+E>D<6x{0Dq3YK>_PvM0$B+Q{N&*mSu2D?)c!NS+Es?B1jk zsgkYwqvBC`FW+KBq6?ytfI~)mK04p3M<+u{;-=MT1`7+DP$z0c4Y|rF5)!?87$uxH zK=J4c_9!Zhu^y_on7TSaU>?4J&2=3tp9?>%TD*zVzR}cEn0I>UE`b!fy~w!8UNIB` zmVh>IP9fodQV@y5=_!wVA?Bi#ITR}DcOL>*A;2J%bj!eou;>s}QJ-_q`Hh6JZ*kvS zr;22(sI5_q4XujyK;Z5DJ;q|{zPG$Opubacae&kCmbG!;xlvYxk6N!nf)s_W%xc{; zX`{Sr(_8pg$P>Z=OJ!^*mX9{F^Z3zM&)<2~la&s&CB+Sp99L3D;EEyM2C4(?ilc%A zKAHpGKXL+IG>8hDn(=6jhP#;wkg8tolM%!mqa04xL6Y;SVo793_G*Pvk6!by0NW2x zP1mswkKKAf*$g)go?q*}!>wM^bLiFfWo<M+dRY2))Yi3#QQb-Phh(ykYf~+3dc`pJ zpVQ%2%+)sq@%+i>p6X(a;pK<mfUaJtiJMOj8=f8sWsA>C$<>Fq`SxB92a4{Pwuo92 zc}i4;mCp-<C3O^hZCS_DQw~@48?Z9n32}#AAi*Jlatlt%%&Sfr6Q@N-Nc>7!(RiDw zT0>p!H=t2u@ER=BYJgAd4fg^?h~I1VW-`t2{-*2cNPY1`O)g<b0oyaZhq1NvidTA8 z>`E7b`5VJE*Fa>;N!cxR)h;Zs&hXa^M1YqQ9Z`43d?Fqgi0^Pf*_GJsG-d_hh&SlT zPY9VL%+LRP?<5?pI3xckG@1S%V!!TC*DN(ctT`=z?Bqtmco#C6^6FxI#wd$9LL~^W zIbEcO1%13Qc|-iqzg2kOY^)STWgrI%%HxWoy=8aYL)6<rMdcM17SfTiFD;XSr7t9Y zrLxSzA~8i)>d(VjAoZR##f-wtUwe`EI%YfnE>Vf&cu{?Hc|<&(VJHnk8HL!$n!U`i z`Lgk_@iR?8@AVnw7jGy1uWnh~W|$#^g@JPrI3qd0*y^K<{YLGzCkW_ckJvh3;4^U- zl89iPgolP5Kk`ctyy%=;hX}Z0`v#|>7PM%y_GSOA7Ig-gHMyN+&yyL_@LfliZ5ex? z+feeh*;#~xGH`KSi5Sf>n6Ce9C`p+N(QwYZS;a~f)Xot43r`gx&G7}f;E@;pba;*( z?`qP6%9PdUuy(zxP;EwNDY**Zqt0$aDI=d9_h{0FNP%UB$s?vNPY@V)PGogm2g&Ee z4J<P=r)!(3pkq<lKF5b5DRv_U^wuvud;^oo@|4&jVJdM@>jMSnlr!=tNvMHkVP8RF zAPj4)1B-M}FLO!u0+n{Kw^u9+Yxz};+|_@u4%M;Oit{7?phFdM16Ysl;?(=Rzp&L> z&onZLVhPCE?yJ};D<Y1V)67JQO8@Y(*GqCgA-sJj-|F>&y$Ms35QXb13k7G`Q11Lm zc;rlzbzXM55`gQ$_Pe)bl&^*&L2k`S0Zc!+tn=h>i#{1Djc&4=`GQNMTRm{}J*_Cf zQHMgNlkeMN*u20WQ9uPs@k<wl&#kY&?Hc9TyS=^8rutK{C;#uf$Q8+u)heBB{^pdf zn@-PLj{t?^lu$%YYn)~kyPwwx`y<-f2&%{0%QNtPviL{($>PYD-*E%gZI_0uM{mbG zY{zg$r(vfiA=vxgJ%hhrY<;c<?vYtE)L|77O=OIpiP{NpaR)@IbLs3f7Wl%S2#dwp zMYA3`i|u)2XK;y_ReF6pyR2ELzfl|h(eVcvwK4t<)@eT8_1hofGtf+)!2MT)X?eiA zrnevP<4sM@b<YAsRI86_T{P-YQ;Xf--Z_@oCp?DHux}h+I;R)iXUxc{<-0sqoMD=& zj8S-6_3t-N6Fn2dA+do0w&w`qzSSg^S5S!KimXk>E8C=`j<13dBblX3Kad^vq1^En zws9s>e{h!O4-jbTaAecNf<(-T-}1*8Ho;q^6cPJ)vUWlIXN|?FKUs|e+g^=-+TIj{ z#E*e4HCts=bu>S_y7rVnS!j!I8gzHyvfr@4YSF3uzbt%(l0fR4TOrr%F<LTZm#$!W zojM*OJjU$ODiPK<GkILopWfVtJZ~LMZWb92F*B=w0QS4i**BTa_P-xiyWBw5ewiWR zw~WWZ^n_G%QGi+u;xj!3?^bEZIN!qVsvrRhQtgC-jVZp-ngu`^M(^syQrrmc)ex;+ zI@7WnhV6n3G6yhQr3SMMFyfC^SgX=$E{rKLA1(!J;z$xQ6^&>WF)N)aTZb_)OMEFW zWoa;hQ=YW5tvI^(Z&IR=)*hg?*cTmV?^|P$nn+hPRG2L;TeTy~D)z-5r6*_WWljKS zxsqzu*zmRPW9^&R$<k=aV`kf)G}{(v%rL;~5E??M;*HZ<xlMx`SX2^EPAS%m`B(Bh z^9Nc_SmDxFr}JX1sKeDu^x|CJ?`k5UG9=7W21MUSLKB5+&)b@Jk9d+{HvYzVLL7*u zv`me*%h2LV%huIggq@K11C?uTWB>u+vkX26mmcdqd4@s@s90{gkK7({lwW+FTVUsK zU4>;PmT3@nWNk0xfqV1e7sWw+?McA4ot1t#6Vxj+?(vE&Hlo}g($G?D7Pm?W2PFFZ ziYUUwZWD<8Y9(9$Ua+ad2`4oVK+GWR*hRK4FKIgwy?P#%KX&3Zfs%VunBoB7lPGTw z&GMStE`z^D6QhM^+ANfRPLmrb)TGizx2nQk*Q^hx=DrR^u`Xq#?(8*&(C07O@wT8= zQ!wCs5Cm7%5tQi%*HKXfV}cD05UAZmqN5Dl)=*XK@kF=I)q~AZ5$@;Cc1TpU`K7}2 zO)xkDJ~2?GJk;*Q@{fsfGy4%h#%QsUp|?!<MX-{SHu&V-?U>C+UDx?iyr?3B+&t<w zwEvih1~o?6o$>&CCu&DQ;CVd&JFMt*1Nl2M`DLS(dlu`iYt`U_uyXoJ30Xq|M)DyA zbhQ7uJ9V!Iev1$M+VrQUPAv+pVPCNDzVe;i@v515kIXL26dmr(=Bzyc^VpBOKo~z3 zwG6M$jeRBYT#sIK+{==jMTF3poNPIfZiiqoatEqgguB~pS&^dBF+a$cdDoZ3PIM>G z_K14ci|=H3jtIK+3Q#$_KVdOZ%xAPaL`bpMS=IXW7EhTynH)Vjav6q&iyss&34B<5 zd9J#G6Yn--NtBEpFkQ$2Br`NazQ~r4BLt5K>GwqY+0tkI?En-8#Gx$Yv1L}1WWi9= z>29;Q+y|oeLzGTA!5$IH3q?ZiT>e14TV!nPSBWj^S9ECPR<iLwEn=s1R~@3My@SXG z_EbC+^iWPbyFc=B!!ny*?vaM>@BajEA*6>CmChAQRU>QNclA<Jakg(}Pl8!`20+ zq;A-y+%)bt6-dH|ej^kuWrR|J@^ChW%=Tv9i|+-zjw)$ud=oKU77e5NY^oXV1k!k` zL(KFRIU)o<th(z@)PIqH6M^OhEqn%IR~wRQ>iI`vH)oktJ<0PoX_iD5@sHP(RHr*+ zz8c@$N%mLUmma!Jb9T?Q!ambKBHn@gbfreAFbvagT#>O}A^RKp?K0Lv{q&C;({2;f z6HFsPxggO<!%{=`u><P#p21B19iqmB15Dd1BISN!^0><@;04!=o$G-2|6>*B$AsXI z6)>fNWF0U*fUPnPkm=~poWN7U$w~{0+0{@Lpv4Mo=WwFtA$GMv*@_xu)ifN@-P26T z$7KXuxUXY?iwBC+qJ7iCb`Rge-=cjaye{ej7-IVnP0g0QfydCfCOs!wG2c92ABev> zCW0RI;YYWA;(hct-iSe<4INIwbMH3_H`c(y1WoM$bYRdr(!SLC&wujQ*nOn_n(zWr z7>9hJ?_ANm1v1EirwOr{Xm@na+S{=-)1;-EKt4}JG22vx+dn)9J*y*GsX~G4i$izq z3Sl`xYXN5~-IiB*c5Y{(Cbmu$NB+~~V1A~}>e%Q4humAbnvv>D?-VU%tk`TG`7AYs z>N;yYz{-^)jf0M^)%>)`#7mkkHHJlgWCdTE0j;q!u2wSGpkFP@-wGY?Q}8{+Z@4Va z*&*%p%6THZoA9>NE32V^JY!)F7*?CB3(hQXCP$W55B8(EUk$+jIo0tx7PA%d)~e+D zApon}zyEX6!*eGCP07uayOf`FQ9);q0g_A&YIsKUYLni%SSIszbCht4a^BnvgUMR- z*1-QcwN#6cNwvy+GZqhLE*LDb=cg>w#<Sv8-AU_<#2Wg7{v}PWWSPcX&4eh){!QtP z%YhjfINwVNBGb9$=7ShxrRz<h1Ci5~pS|NE>T93uygk2YN84k6z7aTRtdrHD2H<g} zmyGk#RMQ;^<qj?Db<oEi8;s#T#cSqmXf2%4)#v8IZnE6wl+(lb{St<dr!U$<RsqKy zV$ty{`36%(T%3!ULq+v1^{Kx#r}9pC72A-L9UaY4v>T&zUdVi2p(YAHI^JIw3r37_ zWa{NDJk|nzS{n3FU~#Ve(1oHh1CX4u-Ba74M~#q>_uQrHdDvNI$Jty^U5G8}9q~bQ zD#P`N$~4%)kDi95xE&K^k$p<QE1!TcCwJV&F3IF;3U}j|+Ax@6^dJ`{igtE+2egWX z5;zvwEhl&fN>GgzvoH&CYbdlZ^?oMp^+NA!Rsisi&ZWbaC#(cl)j0%k;!CrT>Q%Ko zJ|mL**b2+eBGXlwg+$1}ryJ_OjS>~Ubv4by$+(miDy5kA3UZ&Zdr$29G6a8y+-Ax9 z3HUF^?1h(0#6N%zNlztL*uEl8iv7x})~7dkN)4nrm-XDq51L!rLgpP6hP>(wY}mn1 zAyL}7Qoki5nSmV=1|T1Q>Lk4aZNL+h5=dIA=Xj^cUL4j%8Pg<SrXd<)pB@gEnDS0M zM%d0kBNK7ip}(u;zGH$j3LP5468pS(43LN&BDXx~hu5+XIAjyTBR~-F7F}j?<cr0v zHFy7F1Iu-eh~M4C%z4H08PXyvHeX32PLCw@uJae<F=zu*Iv}WSqi;LMqQo;bJ}b6k zw=-1Ur1k3;1PYSQI8YtVo_=V#7g8r*fEr8)n1o6)eqyM6u1OIUrUGL2vqWT9ew~7_ z)7%ky;SznBp@IdfaYf|nSPymi7A4Mm&pn>%MZzYvsc(YH)+1a*G=Dzi{ps`BETBm~ z>-TxWAf1%XfXQTo_|a@IHAU$a%DH|oS(&@e#mb7R7Ap1qubbc|=V*lJFZ5t{&R~*i zLViJL{O&_6GN@;^INpo_nu8QtccflDCZ|Y|W<rlu_y65LsaxU($O3%|>c2h(fc<~j zd-kZ}Xnc!$-eL9h#9M(=Q&aKZvX*F(;{7;-0?|kVPzmrvg*?9|+zn~AEk_Mxe6pJV zq4XmDJfpr6Lexk-;tUW4Kq5A{Q>?Ov<vr#l53qP2epr0Y=5HRar_!J5TQULytl6s$ zKP*1n4~y?DwecEIyYZ)QpoHson*~=D*E#SklkoXkw&&GoxAs3QKH>k{;<NdGT72$C z4Lk5%!VV{fR7ucePxWh|z`%|lTAPvi>>UaD`uP&#+Ci{|sJvRX?eOXdFw(OBc(+VV zycs6Cg!01_3aX^74>h=h>NYWQpFfC+AwssGoCdLHs*SM8V>yU`I~|-xl9|hy_h@ZT z7T8g_7ly0nn~^xFf~`j8&=bhcAR3nmA-CK?p2iems!NCbmvJHZ*SKKm*)ISZ7x{e) z$tnQJSn&VtT&!qCS&#khU`Xjp1{xP&hPOnG{iHzSVx5Md`mb^E<HNWp>)L!t`G$By zHx{BrjLh^{OmdJu-SpdJeDP3?b3<AE!??(rg3U*G_!S??k)IsE`&cJ)t0Dl|%qTBW zIgdxX)f6D=Bgpxlqsa}x>i6t<Tm_x;m-y40Gl<_~a=927(<xp9&wr8ielC0TM6J&< zDb5~i$g=@V@g%oQo(G9H4gjacgv!)O_<?%`*Mw@{26z&`RL#4TDou_YRR6X<`T9P? z``ZI?jp(OUub7%P1+dE{U62UY`{u`91M)V>40nrG;=u<!+JXSsi6kB=_?8!Fhh?6y zj;v?u5WN_E&sO>Xv<JE3b0@Lmwmr?P5X*{w)6aDEYROG%ftUaOEgq=~v%v6S57O6p z*SI?xA*$VYNBBLTPLAAwIvzGSJ;Pu><9I$p^xo1@^UBY<i(SD3ZHtJ<PapQ+cc4A^ zD}<IJp!_k^7WqrEW*;X->J!Rkj>rkyhd-!Otp1N}arxJ_2<{9qRlEPpMMv#gY9xRK zf_d)<X^3C7omdtgz)XD>H-Nr-T!&O6;VV+&30GIC-#&)8b@Tf4Kl5h)j+?n~4Ccyz zIO_HQK9$eky^aK?q^leLR!%EG^gWuuVpB%V66K~Bk~Yfxe*B^Hp@YNzpn9>Z>oa2j z4;N>P(XJK37n$Xjg%CfP(->^r#k&Jc_Rg_9ME&K`l~i&Q^Vg{ud!!2tT*Uq+2T<gu zb+d{;HNXuDjC5f6vt@3^ZFEKG{*6eDWPojtUc~*Gg@JUHQ6oBoQ3KN7A~ht5)m^9s z=+!Uy{>rezf4zu(vBH(AtAZ)?Rop7fFUNNMep~E|qBLF&e>{{oRDkaIq<)7U`5sR# zl(tYK9LTmvibqckW>9u~?OFlj0eP*Vi|f81mex%#21EOla7euYxg<qNG(;94t0gat z^P)-R+G$Qkq$<=P;##@CSMu4AR+Z{=vzz^S-iJH>=^uAo>|b}>`>#9hA=Bc4WmyrE zxU`l?4b5CFdlnZJWHp)^tEXG(9HrU$u$~XxP`83b8F^9C<WSlaR_b+aA&L`cA*p&k zQGToU`nvHB2JlI>H{_p!2@CD!KxbNvY)YiGe|cVm5%)fczu$uBME4S$+a}ORy!M)N ziiOlqzRm`k5x8T>O69JQH!a5oAe$%?KUt(93pj;2AMQUzQQ1)5dajYk-1<pB?s<c) zw#Y8M8ORGway*55^8GdZxZQPY`)sqb08=f^E+GJglcbOs4%?L`g^G@A2-EO#SNyWM z`kL0!Kgs{veV>~l|Nrd1*Keplv%i_@Kst^f?JNFL5aNi8`KNHw{}zrs)?Z(p0qCoH zdO2a1_5Vhg0rY4|O=<uO=foUXIDiHFkIF%)Yp-#4u)+LWIZqFlBN4uiC5=vA{&0Mv z+1ZAv*#$i}Xs;Gv`nyD+oFVc|W__}k(m4Z~J_AO~Es?;&G)fcA9OPut-+JAZ=ot7G zl^7Lqizpo0EFJdD9pJJsL3q@zZ)VC6H=C~nNqwJy?BIsjObGNMSmn}xa8ecY5LoIk z|7%9L#19g!J~xi8ed^rEcnAGYGoqF%T3S9u+5#xcbw3<GKI7_+?uuS<W2i5IWbk3| zZtP!)EM-yo;gV265sM{|5vOYi(qxl$S@lr&I|TBT@>MET?%2*jfv(?mrheS!bO@_^ zFgi#kg<omW_1Fg2!<zHuS{ldYn-9DJ&SsH;;aZ;v43kWH%lvmanX(v5)aLmEd{q6C zsqX-L88q23Mly4qP54V@-XS%D8(ll^wd&P*g*Lwq4SqAND~|$2oBjqZr&G8AAq7-8 zUGzmvn(=d4;>E{e@#GDuEeHE7X?0(5G14_BTsXDmhoHIdV7>=5-z!$v;C(VK?N;T8 zg4W@5C#&qPwXR&wm!nft>?-N2<kr407vchlAFOp2SV4s4BRqorD6jCp2sujRjnnvO zpIQwE<k`>OSvS}SK4gvgCyJzH^5DCe-Byw0x3midTMM$Gv`%zTY*W@#Cb+yRNaT3C zdAi=zI`kM_U>TYi8PG>D3!4kWMzjPA2=~WsDg8EwTa+hhyer$|;SmxOJV*@VjVA`A zxFR$6aj;9N7{_1oY98=#>!Omv1&Ei?WtQ5Ko%r3mh*E=8Lu=ZM(>!RtW=%kT<{o=D z;9r1=3*Cr8XHtbvpFx(c;%**0&Th-&E&Yz^I|z$22Rcf!24jUWlRL^hqxMR>)=MjF zQ($6<aD##Il@0INBpCyO>2~;2QLZGQG3Ik&?GWS28(&_%J*tgSnBfD!8*Qa?bocpH z!mFn`M0?JC8ISEbtyR2e{_>C}(WXk5f_taMZe6D7&$M>pkX@omh0f}Xnf;8nmEh|R z>kKEX6|&a6AxRl+fpI_(h%r(3eYf9Yjoq_63f!6a?UWZ<u-kb*mu#XKgfB8cS71WC z9kfAe>vJXDez~GPN_g3QsDCGki2p%ZEofizjd$ofyfHbytx06hsXn955Eg(aZW7B| zg5cL$ZyG4pJR#iuYL`81b#$Mu!pO2S*%EBwvh;0zjzX?y{A&gHW<JsSBMc@}MZYY* zWKIvc4h0kRS|7SlpM)8PI+Kv~*rG)jY_0ndJ2H!7)~hg**lMf9tMBKsips93YexBz z`#7b1qe1HXatw!KBzLrhe4Oax9~wgiUkM!7rn1?gLBdqTZ^5JV>RhQ)mhF7JIlCbL zv(`$Pc)uh8hqQkGJEMhd^FK3M)KndxVEql0{6+GiwYKt>`Ua0LmgqIz#@)1gyJj#M z)3LH&6+Z^`n{?q*YyS6))};QX-zSa18#24Cu;3>$5-KueaYbRtk-7$23yry&zSyM( zywOb~5A9NdYx+go3)Z{zOHH}@x>fU)TF-V}XyYD0vQZXs({8#yv34U~?IxEU)MYG( zP2Jv)s_t{{2ijJINTx2+M(mmPXj0yf87&>xj^YWr#c9t64GtaoBmB*}JFSR}i;~`i zn4Kc?G+Cx*EyJM(-;2~WmL!2*4J!9w$zE4D`N#ahTwrQMqX-=sINq$mOPE9A7SI4A zM**NQ^W(YHH;@BowA4M`^0Z;&E1cVfALn}vqLO!EC%2nAy{9-)g7v@LBSTOGt7wy> zIgIHDbu;=R`<dsyecO^X<UIW`D!Z-PQyY;oAcYNpD%}RAMsU?*MK0`vD6H)US^BJ{ z@7;_%&ZK7oCYERhYu4GvE#)8S7G+4oOaZ~m-(|e<XJ3NXocIKy(0OHz(XB;LSG*}6 z;x){v^qz>A$MliQ4FmGLZBs5r+)YK-CuNi$FY$1Ke1$gPriE$;a$WZs5%{MB>F1tf zvQI+sB($l!hl~Qt-o>5Ono^jA0OU_1$Fd~gZMTJNdBlRu%GB}<a{a|SXK-f`{s7aX z3?i(W;EY>3Vv6rWMl#5zvAuMnDczhD{_DJ!jxzDtYMm&q847U9EvR6=g3Ln9s{G=6 zXw%i9zQv}6412*aX_wvfLd6c4vkj9Dh!3E3d?YE7CS9bz{kHM0mB}V@;ZfDlwgU)m z{TU;$>zIo8`bd9=8mW0sY%>j~T2ZV}b#T>1Nu-=B9`QZl{wHAVqj??Dfq;Ge3s}y- zCn4X8*WL0kVZmXj!ZANm@`9{i1RJ`+{wF1GlS12W)?T@-e+-Ky3pe>jwYz*=eE%5) zHRCKhjb-zx(|P&0dUF%-d8sb~OYB;t-w2M_#2A>AC%IhpztmK!tuP8AZZlmeZXUm) z*XBR4t<qepvPPd|JNI$~R1d1wdDLs-4ZwLjsX`R(VfhoQvfDaNahE|qgf7zlE*y=X zsHAq61rTzgR~xcpqj)#b>3kux(5B+C)BV_K`_ni7IGUy**<4Gjs2l$9X8M@&dW9{j zO=F3QnJj~#8U6x?Ior?&h+50_T32<C{9dAU$Uh>*Y$_1K#b$sN2)dA|v^Nr}xZK!b z!Rg9BP4Xgbw%cCfa^w<qBb6B2i}kN;=}nY68QLowhUXebdQ6MLSS?sPvS|vxm;mtL z!1!k8oI2p^6+2I=>+*K6LqzXNt99efvp#UL5Pghq7badpU(KRor$I;eC)4YO9DKBI ze@w2u&Ef)Gi6p>z1yiq=2_@7p$*`7TR&KB%+XiQCpSvjM7fv=sO*DLPnDk`M(;<@$ z^Pb%wL4*_iDAnuCyw^?o&8W+<^q(ceZC4wstSG06`Pe<nI$?sgFTz}Zh8Pp*?)?1M ztEL>pTvxzmm7&yLL*6RRD~tl)a{uV3W@GK-zQ~tztB3%GDQ<#V_ccw~dnUR&r8MF< z6#YR~TSPB;(FjA)2FMjrEM+9x$S9c5GtDf}yICtynHR*zk-`lznn9;iOoUB#gJ1(E zklrBnVQq!^;;Rcyia}4M1{@)sT_zkbA3oRlk~}0xyna3EodG8?56u?FCtFJjeBha) zH-x#_Gj?LV`-J#~BvPE&2_$Biy3W%0ws4qZC*&+r@N`MwKasmo4JMlgMDG2+RC}5H zH#D#Pan$q!!L^vM=;1&F#{v<I3`8)=w8ocz_dMt%|KA9P@ak6hK=ATk1gHIjV2!~D zFd*-SKY@`9Q6dQoiZay{{i@G3QtU?&_UUz^1tb_!>htZ`RxPhFH3E4T5Up9|(%NME zPbxH9C`)kJ4Ia4YK`VO68kmxD|Aou$S*NOg=$7TJc7@N4{yt^JTu2S%-7jQE^(wA> z%Dc&@7C(I}j;kmelI@qZ&SP;e4g(V8caqGpi&J%JSPC=v-Qg~<fe5xC0`l&<s|%2K zfe7~gqfyMJ009uLGUS4wgPdyJ03x_1_S$bcYR3#1p|{<Bg0!QPshc>**jB&kVAG0b z;0;;TGVO9CbLrAKB7xI|8!nci+DmN>_BVVM@@p@7i`~-g6aexr{B{y#BkZCT`JFTn z!TKMwu@rF~Kc+;+Z9UsOjKMpyjtF0rkbtwXjRk;JyM%)Vnm@ve62#w4gIlv@c<^WG z*{BoSzWhW}yJ8Je<t;#U?!VBg$@s-BY<Sc=#3(?f*6$pA4`}xg-b_LF6rU}=+T>wH zcnQqM&VLdL610T`U4EA+Vb?$A_cN5Ype1E}aIk?fJb9nWOsX`EoDxvJikM0GisEu@ zX(|K|&9rILNY~3$DmDUgZ+T$RF+L^M6$Zz#ZXn91xb9RkDbfh-c={+unZR}~t$s$y z)5J)4g=Xk}#vq=&Bk4Niy5Sivl9EurfZsv<q`L7uO3aJjbtNbq|D|5xI$aXP_@O^v z_j-G{j7mat1kWkgQUVdVr(k>F%!b$p_fCij6h+WtO!sIHhJXMrxHTE(Knb*o68gUY z{QG?2F8Tk^3NtSMp%wBifLg%_wc)E#9(2s?dLHfnLo3|ogP?FM^O;Zh9P%`$z23dN z$bX{oz#pXJon^!ohic`_AF`&B&`eC`g1^DRz%nXNHh!{(BXmk4-8g6)&kEMN;~TY1 zWN(+jWw&RS4oDRQNOuk(Ej!3|$5v@XFWKQQ1~reKvTHJ<FK)R-c_-^Y4uk?M*s$X> z{?!T^jvc^xZlG3p`KuM^fLh^%m}7mioie9Sdy*N~$gHR@*6MkUsnwhs5FGb?4SN87 zk%@OZ0wI9e{>C_I;VU-Wa@&4tAwq6_ho}MxK#;iLRZtz)IzDu#Dd_OBZJuZtr&&8t zf8uVzf~)fi&+asX=Z@6yvB(G0(7;H7RaFRNa1I>}E&+3!!|MbQYUA*^zN%#nP0y0i z?QKK@dN=+X4lsRR<p?fm@}_3-^Q4JaIFW97wf3!+K?0H6_!6(zErYd$Zkw>p48@Ta zz%27%vT~g2hh1RPz9w%3J(-{}0Gp4!<o)GgN5gPP{{<)(^ia#U(S={Sbla>qlgM2K zl;O88xmaOd0)JxnwECn8T8|o@;Y|rnB(?t)3q#*k{NqHSl-mWJlNT_VM1W%9?=bg= zSjZ(7V^*b-n~)cw$o~K=z`y7SmlW#?x%pW8tH|fLflv}D;sBm-`XI$f@OI6ad@M@7 zG9U62`aCQO2L`C;V3Saje2?g<FFl(`PeP*#>O*|!%&XsZ17)`ODJ*lgK?opBv_<F> zB;LPT050&RmHQ@g?@%vjdc^UMzzuPfO^l_yBPsWg2O557b}PptQ_eRSda;<`#?znn zPVxNyuhEf`GwJFNv2gJZ<pMqLzq{`%RjmKHB)up^hz5hEh$foI58SQumvaj>wuEcL z!ot%2k3fPSNI}?ky}*cvd)s$@R)ZaicK3sc*26U(cQe4-^F8P<?(BZ1$PgTi9VlW< zaWqeO@J$Tg$^2k)_))l8cxkN-ir!NlU=$zFYUjK>Ri14<ELOI*ztZGcuZhzK=k=Q{ zc&N!3CfKDv{%;ij0;GC%Z&(E{V3qAc>y6hz?5A+~a4fC#7aejk3#|%HJK@7*Qr8Xs z1^7X9uhDv&4zD5E!`bd(@#~(CL;^s|NP>-zUiKoL`XAQ)(i;~{!#i!L0Jz}utC+!| z3Lw^QE|lyAacOD>uGgV5$=7!&s5wTB`$!~w|DQxc<Nrz|q&%F8_gqOm(f9o7kG1#_ z_&Iu+h6>A!ey`aZ2w*p-S50+1JD6tH(yT5e8bckFsgLzK<+_3>X#(gt)!cN8NY%6e zd&-wpK<AnG6t?#l_*F}#9Je6%qg2Vh+8)Nm1WWV5Tj{89Qe4T39Yqvtk1@uXuG>ES zaN{guz&UWgj)QCk#OLPs)ui@&Sxwbu7GN9!S2C(bo+|w#j=*SP+lBr@h&ABMvkp1A ziF7hhBg<}~9*EQkSW2I-M2DLu90v5<4#WXI>3ZONK$u;XYJyf)ytv@6-u(x`2&A`k zL_45y1F0Vfb`(wN0iy_*r;W-_&39Dle=fxZ1`-Cwzut1aeik-C?RO58Lw*Y%GEWa~ zBip1I1||}S(|*y7O>y_yu0apENiNBgyyE9IM;wM`lej@t=1eIBy7Th@S(nvpcyR8B z;DCxG@`c)o0C7lct~hEYgLpHRu#OF;!x->iZpfHWh{^|s|CJ2@&i}!%t^BM4n%~7j z<&rG|1)L(9zQX-yG?6$Hu&`Wc?yX*jFv81~QL1CDSyz@nG_Rk<P<uZXMBP?>x1A3S zlU)I34<<dcfv1p~1^oP85e(3+ajLd!!@1y@&{)Y7R1{$boVL=`Yjcg_h+FNrlDoRE zniTjBbOBYXYnCT$oAsGyrX`b&rZ;ZwDE%UQ{oDa{`~P-qi<WHvC<iB1APWPP10!Nq zIKTwhvGM#>4i;KfZF<Uw$)`s@S7+cCQN0G6Z93fgWFL0Bhas<L>c@b}p`6iVh2LG~ zA`Pe<XybW$3%9w0DSf7mV}Tgnh_@7N0tT!#q9x71vIy6JEt{Q*kA_P}12R(FN5}SE z8#du@$ClKyML;)*ncZvaYu@EZ3)iJ|Mha98WlbN-;RzPJ_crLy!ml@Be~zH-igJ36 zAoVaS%=aHj%=FNuTL6O#YxPu-3wPXktG^u^P3d``17H<1;A2S?kcL<1N_4jgTg`BA zlCjJnna@>ay5^SLc;;$Zlds8uZJQ}p6g@vI23rL)TD1=v_CZzXhxC2ftXfa9AFcbD zmj+Z0Tyt<A%3&vHhdp2gex(*5u}^RFK-j4#=?{@H1G8F;^y3Y2UvopJ2kJG=pHi+G z5R7)TlQ16`P8OA><mqK7l_pigQZtE?oM#_z@Qb)=wi%546G;S<WTGGf2bnI!ZYq@e zjR`rUq?^_NL}>;P#66V?x8ScZ#;nC7hoM;{Z{Q=8=>yMXbYDK2HZtXwC`&D1B(|M+ znI0Mv*t8Mf`@P|XDU(g+BA_;rZS!~h)3yC(vRoeo4G-PTi_GjN#X;9>Q1o)%!-W=N z7tebb(Em^AJ1eZ2X%EEk#J|!v!WWo|)wTb)<R$#Ypj{)JPOEYu#_GwUot%OKVulq( zwp4@gD+*!AI`q03k99EkETD*ieY)=!xV`5mj}4NBj3;nK#lQbeKoWj^dqe#No0t;Y z=Y}Gmg4A12=H}0oBSIWTBAkCBF(~l=(RB{YnMU23?v8ESww;bUwr#($@y51o+fF)8 z$F^-7olMR-GgUKR)%=C0_Ooj3z1DqQChnPbS+_qqbs0L%=G9{^)Xt1cfEnXn-BM_% z&1h?Iw;ATJA;}@6Ly})zq$-haIt?bcIYXNq>T);uIQyUF^TtePN-)Ks&~J3?e2eVu z`;v)>Q?ldA{TJ^s)*SNfmp%YpZ<N|3W4EEFaTt+~V<$@|Q)^(zVwh{ldI}{yxi_=P z%n_<@jj7n2!Cx0V%Xzi{;FK(dkZ`}~Qc0j%Wav-iA$+LA)-E%uhRIEV-P~hJC+8x@ zq=Onw;5IjEiavD|atKApVGahGqm8aeUmZ7$e&%06GOj84N`U(YMg>Z=ZX3r^!(*x+ z%y;)t?;1?f(1@aI<>_M@&MBv`{&?DQ4N1^6q9;B6=(+GsjS_qW+JI7Du-M2`;j2w# z0UG@IFIY_qp3r1M`ZsXn49(?iP$1y~zg$<21V~MN80K|%fpH5U&jDSQ14popuAgsk zKK}0;;M9cpL_%NLJ6d^^!MJ(8b5+tyV9}>WmQHr0J@tPX`g*KZSh`WcK+)hlF_97d zoNFx#*jO|coh?m(4zxPDg{ve>mH><GAIYR>mjWBmfohgf;4D(=VLk)>m>DC(dyg(% z8%V4Xsr?XFb36n5HX$-2gLqU-zC=$B&oH;rbaIWl7A>J78)1{ZK72Fh5G@w$;^(nV z$Hsj5%r)aKi8*wu0VumvfphjB|4~<A2u2ktiQ#|z;3i5$Z2fk4P=TSR{z(Ie09I>w zc<T(se_Dwv3GxfB7q-w#_i)kG@fnQ&rL1R)V$golAi*)brwhQ6{`T8@h)0PFKp}yU z`Jwhg@7RL}K=<uN@Eg<h`!RE-y@$}%t;#lJP6D3(>T$b{s{L6`VNW3O@cpw$_|HXs z(OXq_Q@2)I;*a|slOOp*S-5ut46q&BZVoJi>c(0~02P}?DOx2%vLd}IgE}Aro{!$5 zA`c}|$}%I|;cB`RHp6fSTZk2Wl7(3JR5C!0!(McTIzWzHaZkz|mWK7FDsLVv8(@?| zn@w7_(d!g&1ap^3<y34A#WUY~A8-VnVb^~xMGHpHbQ6%*2&S5$u_J}~jmbCJ5fh38 zr)TzXjd3o|GlkGYac;?DOfPdR9tT&&cGsNYrl`=Jg%LW&aTlBM2mql_>zK0=Vu#Q} zSD`JN8q`wPVzl^<-Ug^aTQMO!7FU8-Gv8$7<>fU~*r=~jm5uhE2TWl;n4;R&G-)dQ z?Zpky0@F1h*^^p^*0S4?1qxxpG|1XG6yt(5<G4%Dh<$VH_mtE<>+?83?O;1ZK^BOG z77UF#D>ul>=6iVqI%ITCBMJgIaNX5sFoZTCY}p;6E5o28aNXq<VueyORt}|513clo z7^y53a>F-koHO$-Aq0?`2J*5gp4#Td!F(2bLjxE%Phe2pI_GG?fj*PH5&<<VC+8GT z9dni7y2d-kLd!T0@Tl)Pr<~xr<~yiD%ec;g;UhYy3_{y14skuz5S$f96pI^Gu9sI1 z5BP62dD>8FB6Jv%3(lclUZmYYT~?jNF-)9T^3G~Og|`}F?(0htPwpg2v#C+8>Omd- z__vqzyj~=W;T96+Kof2u6;5{WbCBqukC}>+BMgdiO>_$icU&BgIDJAJ7%IcMatHGK zyq42|Gonr^sAe`!s&J_9t-$4AVLF(m_AA(SrzjP$H8%Q6-Mx=rnIe-kvQspvTTo5- zPQ9DP3~6?~;ZQ18j3sCIPR2LC-W4%w3PG;8bCT3Lm9&=CzlRPn+?gud8%-(yz;UcW zC!1~A`~2?MncKL#=r7Cwwr!z_s#p=O!~LT@qCqoh)zyxno4Xq3!Hoo}pXlTA?)Yj< zuaa^|?6T49lP>fweA&Nj=RG-7T84YI5LjPQbwd)uEvi^KLmTlz7vZCuvyS3FqOWB` zjPZ9xLs^N1fwME*ETaeUN3v;a1hLJOB%@*HtFzzMTgR<~n=<Gj+`@W<D}+W2({qqM z*fV$bk819VvjE%OD*+dhDZ1Ru@#{OQJ$&TmyIGB*db{)C-v#7d_1Z(bNF25#S;_+5 zAnkZu*io&aoy)<fibGYW_eN7TuoVUM7LMSClTdOvzz|bl&?6zi8jqR0s~9)t&}t4h z)uoTeYgpzp*lMIld8-)8Xo~x9yHpn%^`PLxRm<?<Zy^lbhVJZ4lY>(c|EMLeT$xx9 zHmEbZruOf|8UhS!CW5OlvJ}MQ4Bim08e$jTHBx@bmJ@VmTEq1(?KOF4k)-UygPBHS zf2%1HphDo8N`+az6-K!k^KKL#R{Nf6v)g&~0lhjxSNs(jp@m-l6TJ>k4E;x5@41nW zcBNwlp&8XyszvOA1o~o9KE*wHb1-n=n(l*Nzvjj|5>Ut=#7%iJ&~JC@CG)FQ`vv!- zI#_>C9pe+>&mQ6i^l<>w?h9I_8wK~Aa-;7C@cg5QWc~LHKE|e`o2ENXsFVH3xEfm+ zHNkaEka5ub@Xg7ZraJ|tc^5wg!kU}zEm-Z%#jGy38b-{)L0Icfk&t;FZ5CAPy&_IW z!ax?hhL$@AWoTsM+)D3pi*h?IY4qAuzY{mNc0%&-+m*Zf1sjhVM$Yu;>|9khR@4X& zaAYHL>~B9Tce|$e4Ze9<^fNbQw~ezzO?oh<lcS4k3T^9M^ywIT<4uC&q2O-d#A7|# zIM)VhHI%b68A*3HhTc?k)ObK?v*!s*C~El4)MdE6ygb=%lD{8{hC4Z@qiUx%Cz2dt z@CN^F#@FJ;%c6NcI~&`y=EoHC@`Z#G&{%ytb16enxp1nP0!Yt!Hx$ujc+xUWa;#j{ zu0I{^6~0$P$WA|1zj$GHFx;b3UrWGFe^zI8$LU0>U-H*F6r+vK@!8Mnt<|yc*qEo> zrAWIq_Svw3bCh98JVEz1W-A=rxm9Sp^@N~fIge1ootIKR+52}XVmknlr(~QS*p-M& zn5%48$y2s!Zn;C@Ca`VRpN-r@Rgo%{aNWkbYTv_>H63yfS!(aAso5Ot<^-EJ2W>Eb z)rCvfm5O^U9LmWCVfVNHz5SNK(cOA@r@H<|2+$RYzZJslj!WmAhI@??+Ug6p<q1o8 zINZ6gtIH2@Z3RnsJlr|4Ta6zGY+dT7jA%W1aq>}ZYmL0#Jmb+e*foElh0U!zI<9zy z{1Bh<!MH4bZ@hv!lge><#&NGfx?AtuXa1~{XCS;qZ+2f{wJcg*k30Y485a8hqJ$V0 zE^_iBxq&U&5&Lj~^-wGGM$FITSr8za*1_H-hQIS<@94v}%6>wXE3hX5^a^6(IA-j$ zrx_}-_cHO#bmR~?|Dx<l;rH=g)m9VW3ncBP2n`4O_d9uv@LuA$jR9vJV;g05wjH(d zSlgbjUU_EVV-Rt8U;Az4`7*HkjIiC*9sN&1Cuz57&Ed)PWEEjWHcx=Z`AOopaBKJK zN`Vb~SChX#w*WOFn#tN3*tw=07YpaNdYoL*iq`uQHB5?=yZDkde9YdHcPkdnXz8uF zPm80!=&rJFjibN(5;Dxm-cx*Q8+~ozt-kMtv%UP{Ka9xkTXbt2ol7NJ&a+75J?4Wb zuz!0TI1HTJ&oz&ZTqtxk_<$w2e@}TAI}3+>DJfTHjRl8M^*0y?0uN@(qt2#f`kbJ) zNdK-+llBWbXz!eu)&H}*onG@rcA_n(zW*nmw*z(e%=-zk{sr5bVjT>0F}f);=WUJH zwQ^rYp}NgwE+p(1ymr9)oa-7<JYk#!;vM~5^1$yg(zBjXrpha2jzK64%jr|$*}Hj< zFXg(qma9>bpWHhC#)Wk0R%dt!d>RuGm_EX`I66J#dIWv-13{zyj1aEu#nMe1e<UK} z-F}^LQ*!C$bA4kuiam;7|K)*_PI)F<b0Q@Ne;q>oJ@xSyyk~415#dydqz%I@7bBol zTD(^xaKcHF2e0|+8kiVU+nQ#<GM65w9T|rXWtz2o+m$W^Qh{2IOL{mqWEG$(<B2)o zD6>-4n_Bi{QIygSIZH5;MyF#>G#pBT-<fzsC}r(Y$;leKLYTJj$^M-`#4AqQry842 z%k<w)F~_=-;g+X9;*NN=R57XRO)-CkF)7w5V}r$?EMUVhuNzE}_7P@CG^d?hjX9FG zq?^tgOF4c4!nF(MRI(lOWZcg|nJCzf<LKvCB{xecpIe;Gl}#HI)2qMAtP-iyPFhJc zQbZd-Slh0i;8JEDyh=Is6uvs*OkxMwtnM`)fIDW-RXMSp&$lswuBYI7@0(V3{kCLq zWYd@2--BDT@d&pIfE$mNYMh;IDxHlN8cfObM*T)4TqtEQNTA9ZJ3(mMtX|%aR<o3_ zF-xGz7g7dF+os>3kLsnXWE2&iN*ilKtlPE^P#(&ezsc{h{Ce(zdDy>WtNFVlrR_V( zDXDiz*56#t54eA1tC`xt(bhZ9r29i$o4EYA|HcORYmiF+=P;Ax54~Ly=hJ=(8(_+y zmHy8@kTzvIx%IF1o5Oc{#<XrEW&2(>xpkcP0RLYbYjh_|+j`3ObMjxWgYLU6-w&eb zca|0F@pqr%yU@FzufGI+lZFGaFB%VHedZ5D?hIe;1qmegXW<AM59Ph*4}Cp;5u8ZS zL^s(D&;M=hrbY|C=t3(Ax%fac3%LkDGYhqe2lj8o*%m1vH~7$LP4qS#!qLGop_M31 z_oCDMWnu_6gP;wD#U3uTXC4Sb<H4dRsY~xm6-E?MgjvTjfl+6Yk=nHi8KvInWg-vG z#E$sB&l+06xG^Uc*%OLb!XUE9Nbf3!oKhEwSh}-tSv>FGvLV1fyg_=(^ZDo2+!B6= z=yF(%-}LAnr^{?)c4xW*nU4OU$$Qod1p5-8BShuw#FtAfd!F<O67uUk5$x?jWKDo? z#Y!=>S=WK!>uA;dVtv{nxfr4NWFv2z(47)9#Z2B&s~F@*8x&XzLemW{J(24Yg}mkH z4wpYQe~I!9pFVYa0sa0}JmEe@m1W2YfP%?hYBMbItkrim1=4(}vk23&&fVsWn^U;~ znp@+i%-5W!lv|rk+g_CTHv+=c*NI6_J;IRPgm*8>JkC`MUWER+V3_=j*mT&4)0kWD z?H0)Y2*%r7xgS$tMxE{Pa*RIU&Gwj8$)@iObHAl{*Gu1aJV10On_l6qRP;LgZ@mhK z$l<VIj_8)*xZ!3Tb*ri+jbexJVQ`#(IOA+na~cJTR(QuG{J#-r1(KA~LQ&n1aD^7G zn&SWOMV|k%eEUU%*?|4G(Ypg5lkYF|j~`Vyi3aljeb<6W)lUaUOQlNyg9WDBgh(v; zkp&PztdIr4E7i-!NuhxBkr{9Z_et@tc8@ZoPgFHF_Wf?Q(#};qFNAqnqVZAJf<me= z+HP*HruPxN>}-DcSJ%9l<K>oUldx)1IQo3JmF_jo{ndV)^S;gcenA^3|5Ibq46Oh{ zJ>rOFwo92!{hbMM`Nf`o>kvqpf9})vlk=_5zLC(QZ3qz&a6b^a{_pQdwlCVwKVPc0 z5k&NNw2S9_tmlX$0m7>hP%(ET#PB0SLV~_R7=gSUW<sgkHblr9A#g&P{?OaHfLN-R zvVd6Xm$m@;mm4qynwKI(uN!JX7>7v2L#`@=Jfxp^w*#TZFc7ec!TUg8v|Y*ko1}<R z^elxNYzX~aK$5m3WsWn}QPaiT`-8y*44~e)1l7@|=6Q*E0!i3m9p+v%KmyCG_#i=U z0xG0U1ch9f#3mhjL@oq>956GWIHfRew7Mz8Z<a39UCW8pVnYz+#EUG=wE=hk-6+5} zsL;_3S!huDM<(z|ngJtNZw`<~(W}DKna$}%*mlkyu`o-|{&EdQRKsmS(Do1A8B^GO z?<+WVid?f<3~n0*yBJpjMh3k+XU;{9p&_bQ+1fV^PSAKg;oIC3?h@)A0X?#uo^854 z2`MMi_N7}5a5b55@TaCY9jo|F9;tl;a&^?h(SOsL$qPIO*MRtV$XgrjktXM^Vdw{L z(C{I1Rl`&bDJljU8JUI<h?m%)>wOBp8922h+dr2t9SU#K*jcDdnul0AOZ$!}ES*pT zr8qL-q}@vTEE3t)MDs}tCy$)dY*HvPm96c<&Ysv-Y~na@m8=oP=)HLG;3W(5lL{aI zO*z|mcQ<L~N&!nJPi>-l^eWpWp-&RfzPOk-cQ3ml*8@ngv^J*z6p?JL$bt*12i`Ve zOxPG5bhLpnzo#<o6SC2e1SV#b-2WXK61YK$hovQ?Ph}7G5p2mzu#W~}-hEc@3Gv7{ zAi#QB&3lojM*l)e9i9LPiHSsQ$Q&p#W2Z|QQ;&~35d-yLT(orRA2)3ssihqw*zvhD zDH9+LM>b~hHV6s#c85?Q-DMB89EO7N_&4S&Mv4;xrm0`L0vKn;MAYUG?n}}RUKPo@ zg;$Et4Z5*8s=&Z2l6e(r3w9!LdV(0Oxgaaqx_%y8X++<%bs^i$DMXs1dy5UI-{K9} z6uA8t<YJ$Caj*)+TXtxf-5Ye8_N64mZnr4HF7o-h?4{=9PwC!!!IysoVZ^5djW69> zZrMxu39w`jeD+Ich*9)QIz+XWn3nAWX4>+F-M4V36~{Mpo7P=%fY4rQfY3pzFPH5D z8<@`?HE9##L@|td)Ed?0roq2~=Koq5@W=9n7x>wykTZ^~bR!?a%F>V=n&>)lN+^H@ z7b$L*89L22X@blQw^faM-d`{?iloV>H+UuY<TyWyRDqLgI%!6DSd?}(a|sY2sov4Y z(d++1scUie!|DZ5PwiG8xhN5C+>y^0$2DJK=t~r%YeFIYU2@?DhC%I?#!~6$0M<Av z8ZeRVBt<_%`?Y`xQDlBF?6RC`*86wPP|>8AKRSb5N>k5J1SNd-#2hj%r=u&v1rrUY za=Z!GWbm&MfJlmJyyKwNv8M@tas1@G6Py=C#V_)NjRXwoSbMJqDd!DCnZq}Ty)X+i z)iq(5yM{Y3Q7_Mw&UmV16vm+oa{q|i4d9irUdl96W85OcnGAa7Pc<P3uSbG4gv2{H z?TK&%Q%KdiaghD$3;TLLwDVa*;?TXl$-J^@hk7y$RuL}Lc+;YXA^7@P^!ncxI4U$| zi|Yp!rO@3BiU?+^Hjb(f&|+G0YRyL%z9HpR;}y$so6|v5n+-G}4WO<{9x1A|0>Jk- zs=^|z$Vjai)#S{}1b`xa=~+ZbJcx<-a2*vVRbRPYF1HcUBX!<i<A(OgIXWgQ<)Nmw zSqYyXO0&|7DTT`TtQ6NFd_}dGu>{0j=*sUZCRC)Mtm5I1ENYEo^dpq?uT~WF?CTz! z4*B$zM6Z^u2Ev(1KPk5P(bVF9rUKgr4L*<@uG1I|J5I&h#Yd-Yl{o`0B@2f1dSZH< zY&}NST6!PJWqm!eY{K;sa99gxk(;YG3pQ}HS>$b$Oo|z5Pwp}@a5~~C!f)(*Ch=&D zKy6-0;-@R<yiBd85lW!}g*jm*0{4+2Aw|7ee(#b`r*rKH+M>g_aoY87&A?4B7+NGu z0I{wF=`ZFAVo!G*#-S0}j+I&8V06XTbt6swQLcfl396!GC9y;C%_uBB#hmYzFz$X$ zG%@%~5&_A*B3ZVR5abB%E;&OS)qC#OVf&*&M~xty3D5y4ck^kCNA_R*$tw+|G}=!p z7NArGUs?;_Ts2QCQG&a2Mqp(X51d;22h$x*CE|dx<{3Yn-68S;VbLmImb~rH(;vL` zDO$1usk@UJ68s|kICB_o{h*AGpJO5hs-RRFOi<uhemq8_68w3ESlM%0rc$J~vV81D z-J&;z(6~iSgt)UZE!Z5p{f2b4558EHMo7Um8d-H6B|(cM^;)wj@WAsbp@z#4bkZta z<^DCg`j(vn8)h9As;5ct%GA0EYQtvfDkL6?6~;;QbC&*w(jb@m!L<lmRD((TAt@BK z?WDRblKK|o5iku_-TF7^^9X7~U6T5(2x-dRjKc^QazkBq@~Ui<b6a;Wj;b@E3D=>H zkh+>|lqHDp;`#C<4WP%d2>LN}sfP6;E7ewLW~E$STeatpPc6pg((F`uy@Sa~!H5Lo zw8YSV7SmX1nW451XVi}fI}cjLDx*M@`xY+^i_T{*saAEbm^?Z*KUO&d-)gCVEu}{+ zjJUa<XwbKXT<@p*RNH!I{1N0%_mK5yTmh!lkoMH=s4Jnt2SB+z*)SDCVQ^^pIi-1$ zW-_H_#CypitKTNCL=~Y&;~I<m3SE#pk9b2c#5;%)>4)+&%(cN{<-TSe`e|2~-xuMz ze?~BsP;Qj*jQ04!x1KP|@JVKz1z?=W{96$;8{%`V%5B%bu-p>1_=`L+@it^7TVu(d zj2W+n{(W~1<v{)zNP7^(JrBO1M0YU89mTG&kpqt5aImF$n1L{);*irc$2FE@8+4?0 zm-!VLooFiLxX_zgr0w<tL+nXhQRY{Z?jSQS%M{o692w7oNqo6T<pbTU7J*nK#zafQ z(sF^|3?i^l@f2a7<PsRP1*+dPX|;J8Lb;eSeSkyq2@BmWg>FB~AfI-bQQtf8hZe}| zUhuoMo0;I`H|=oqo7Di}5+*`r#R#|g6^edu#C$!<^-Pm@z11z6_2<zigw-wb^-j}H z^_EZ*FO`<u&;Qtaf>sfut3>+oW0EqZ8Vo;GFCF}wbN%0Q_#rJ=ca_ELuYZY>R}%+E zeZ6FG3UI`|WMCl@(Og1OP~mVveqckts`bnm(wRA(LcPrW@~WFPs@GC&i-id_-t(F- zm$W#8M5z~t0PCj9s_V9umFupp+UHBItu39!H2-qFC`TtuLcq?SZC`p4ou*%NzusNs zUOs`i1EoLM<^us2gMw}Ku;$}8u`iUIIsJm2?(S>R<~LPXz9;>$H;e!RU!9QLol>Jq zZqA&MKT_^}(T-Uz`(huNLAmXU!Hwidg30Gg5sSIfFTuEaADu+^ghq#{MlbTve;kc5 zrlj0Qqq+B<Yd6y(Q^`M4ahqSFgVZ<5P|tyXTnhC*KC^LQZ?{o=8E3lBLI{CW1BEZf z*ger-=mTGpFNwI)4pRdM+xEs-$FERAB#wJ}TlplMa>@?aa`7T?<aF%G5$uvArLgn| z34bq#(N&IqDz=NF18fSU!Yjx!j>uwPGOmiSO<xc^(m^Yp^1{p_(&Y;@88T#@z$Sp} zd{o1@1Y8&0T7AQOh*B0kiDyG2AeaQD!30TjGL)}F@es^xQcW5K6ajb<BdUe6$}-CA z(8ZO;w;GP{`77J@@O`r-5-P%`=5#f##cytv!>*>S!?Zba{8bjqRN^w;rI`4m7Eum# z7Yor_ekk+%KTY+4L{+#}M9EQ>{#U>v09E%Hg4nroG)91%;ItNdHE%hYv0<YK=Mrr= z81dP4m*Q$En31PJC2}NXXJSajLu*u_X_XWS#t!}BN@0*HYn>$fUIhk@ZX1z}HJMRa z$tnpL-J<fSD9vcs0r}_;9}F|$`Jp(<Br%O7Hge&ADldloBX936q~#XEh`2!XK!ql2 z{Pq4hDP@9Y3P_YFq9g~aMgqxiaCK{erLkFcQ*mQeou(wzXTMRcvN{P(BJ@F@?N19h zq{wFot>?$qXu_TQMs#{)Ro6-zE-~x*eMu2$%8?UQ)0y+AbV&h)e3N{n{^7}@_%rqW z{twTrI<ibyX)y;e*rroD13q99{1g>ADZ2dd+v+_A8W;AOnTT`cXcBpI&$~E#R@a;Y ziemp6pAx0YfJw-ry9WG2Ip{kYeQ|>aWDRm6{eCI7ri&|F9rc_;9g+5W<)yr=A%BB@ zJ&(3)we~y-2)VX&x?f6#8fT3qvftGcs3ALX5eK;z$*fX12nv0o1vk*pQ*^t8Cf+q= zUSS?tysi8f)FIOE%=_9|k(y#LmE^i>Jigy)Dbn&_NhQNxiB&!@%sW^9MK_qC=`xo& zaoiJSD6`Ly!wxIX*w8~*@t%IG`!l-i(C?cx+P@?5RY=ztz$COnAi0APY2ZZdH9xnC zAGB9(1BO;gS?vMK?+ZZp&$x*qytR_261Jg}Jvy2RE-896Lu3xeyXbU|H;y5bhNeVS z3|3oeZFmvvx@Bvl5_}N#%!+!w&4E!aqidBIBc0k17ut%Vdd>GtmB`<(yKihb;|8aG z)zmxuGHiS}FEv5=PI9b^zg_2cf0(r0DZ185l~cP+2+~1WOO^q-tF-+I%+vwO{fZFU z9N}cP+>}~igq{KH)3hkn25Q3i%&L(#__|~^X<)}3F0sREJO2Q%E@KL|Lk9*ZW}Bqn z7O9#n96k0qX4f#qzg{XXmDSvRIwJWnYsqs>F>o^&$kqYXqlI~KzxwG)RQ*QyMl#P& zF>1l|LqM@OI{bjmLtJJD<GMi}s{(Q9yF4xkv_G9ZYA~yEv~ZBj$J^bYo*fdJm1;$4 z$vrzoQ>7N@W=&D56HXk%zY(ITCozx1=)|16IbXF88bm{pMVhAK?Gcr?Ew_A4GlI;> z><8nr==JWR{B@@CrA8FX=aP#52Kq(LQt7paWe0x}Y-|EO$Dk_lnJaz9t%g!yqFAPO zB(axwby2EQPGPi2XL(Juq^ns7diBDWMi<1-O|421!fTQ&L4!@?j55j2k}O$WMw^0L zq@0sw*2^;MUP!FkMb}L`GBlu_?sC`itg0N+U1yqub}~63$0hh#03-M;l<T@I-O5PW z9DqXj2yCFKpeb;wNxMrv8tNQ5MNfwU<)OxySFV`2MxP;XTTH`u7CWfUA_6*a=HqBC zXy?*196;364Q?t%5$p<^WSCA;G}aXAX&MpHpqGwH#IpI@2M_<;nSZoH6(w`gL?t#Y zxwweCT)%{?lm0X*&XTd-`%!rmpCx@zB;skbNgo&|@QXPe;v`Rob9OQa9WzC7;l7%i zTdBltpXvB3(pk=5s^ocwUe1$oT)0(^#vlKIr8^eEp)wu3GAzeX@H|Wsf@LjC2N5r_ zvO0@z^(TF+>YgOS4l9;@`462jKWW=NY15AAE%~(M=2XcjVt{$z9R9;k%1!EnHtCYi z(?Fn2tIja_TbvAGNi5Vka6$5!>(rRVCrf~MzT0_4G8ZlMdSCB^Cp~6HF<n3`*LBX^ z-QUtXC1%uFa5K5G5Cl0{Y*P*LdAu-Ou*tNll41fM@?x%2dAAH*2m41O>w&@mHE9ao z-<4|IZFtL!wMvU>@!I5V7C$RW5KT!Q&?4Y|wWSnG(FB#9&dU7~Cj;TCSj4!M+9Cn` z*e%Q?x`{XW;FO7SHT4W35>DwBm3&44jA@z-O`_gxyXB>7;?sS|wqWrwhy(gGsYE_6 z*EWadc|3y1b!*Mt_($i`q4foWeQ2(B@A#$T-`O+NZtiWUq@<K4%;no)8XL*O8*L!o z^qD$JzPpBWxR*DBOt#>$;jg;1cNHc(63l+N%Ud62k=SQ#H>T9l6?S&&Dkj0$IBv*6 zP~o)ZL=}-+AMeN(7|{}mnn_c-+#`k$Va1%_Ue8hNx-5^3KZgC2MgH0O?E7LO8%-^^ zQX3nExc{s~HB2uYj4QuLJVx`Em~Md%gS<U+CDx}{=1OwSg7J8DE7M17Ei%gf>aiDB zRukGL<fEI8&R!}n<Zc$9!nG9b^7IA#*(tx%XBk}nEZVzasR7rtkVKX`rTtqxjeqZ4 z_88oBue$LwZhx&I);X@?rQ^Mq4%h!8mRR_9Y29HWiS%N4)=zMJ4CxtDZo~k=%k>ZL zdqx3ic8jKN4bL&ouag}Ga;_AG{Fo!(Mej3$VqWfY-Pr*V=Ix$5hPnr#S*GMHz&sdg zYDGi0YARxL){~k*t{`HynBce8^(8O2`ABsv_UDB5)=Fh6(s&P+8E8?hKj00IzZa8# zocrGbQq^I^M(soRQ;hOOj0TV;WA;Q6dbbSc*xIf~yQD2nT%(}L4?|9PI&<;&Q{h~Y zxlkmY^vI75@FLyRI+KKVyDL)B_EF7^vvQ*+Lb4BDa5HMNmPo!aSm>!58yv+q+wc-| zOUH-!hBkGyh^lk9H5d>0{V?strY!kjmYkQB65RGHg*A-GyWfq>*Dk;wmCZ~gHr5fg zLcF@%w)C)gwqQxvm014@QSLRPK~H0cS#>gWhI!<1ko>kw2jZ0NAqFh;Bj}|DL8eOC zW)M><M&slskJ(>oQKFPdVKpHxeA59jP9Sg+=z*8Lp-Jwd%p|r?oYKmUk54h@>JBuQ zSwFHMj8IaU`upG2j9h`~7=nFkJQbv9vo*}aT=C^0R^7)88EkhVh0_gBtAKN8&wCRB zK(1BVnRN5`kX*)+)r?@XvWKdmU?0Zuij^$+qwjk=fR0DRxOSzrOYA2Pxxjr${W?~& z|BLcY4fR^@{@ppWshyN<+2K7!C@gMsRJ`LaO-60-4L&lh=>;Hw)u;KRtBv0`yf$<{ zjv2u8zVPsq7uA)jU>M=|gDFC@obI)>1JFgE;_ZR>z1$G=y}l&lk;7zMlmP}G4Tv%Z z+H>BoNgiDwJKF?cAf`w03BG<+-lIP!$V5cAuS4yu`je6BH)u>4yL_+i2Tatk<?}DF zyDNg?ywR|t*8{=?ORPfl0)k#!VUf$=hC(HeRnwOd=m9+8dnC^E*a4&`H{1k5qexPr zMBvf{A@k}iQ0jFH>Q=T{L77ZJ4S9pwB5^}GPGE=plnThsuR0=SctYqvS=bIAK&}%6 zUkvdCmVc|c!#uPK<Twtta=JW@aP%~PQ2Jh&e4U<6J+KENnXjcU{ITp8uUR1|tUY-~ zJ#_WSpt#L2hV!l#!N5ucS55y<n}N>R=x4)D@;f1Ix(lJ&;zp4$60`DMB!DPk<n^HJ z%*-?!ZJZGv51tHl1lUu95=Nabd|K78SxJfsRJdjBfPkJ83uLOX;Jh<4VkC3#!bViz zIF_9_Ht_zZ3M)A7x))Yv^ghP$t=>2ud0R3!f6)4kuYd>dl>x&F9=UJHu`%pYi~+{E zo$Cy905_k9cAW{BlNUyAFL~b_^1>6_8(vj^0%{s&lDNVM(S0!VnMVWB2~J->LGC=| zt4`IGn9wuS2fu8f1(*M@!D>RH#sswN(<tSvD3B|Ff>~RbE4sMvPF(+$2t*{$kPZ~0 z{(D$Xv80A4_?2W{e0&+$rjq3XSFk#gV~@Q{m2T7`WuSpghuNFT8e<SFk8!_a*~AyH z*e9ibXsN>-#-6Im{zj7~?6SzTWjJ4UQ+0?MXkq(w<E`r{kbQg(IqQoy@4jR>NjBoZ z1&qivhl=-3rgR_|`hEE58B?&q`;RJX3tPgzZT1*{FvKv3?n7@H8LTS#$ja`qaLOQh z`n(c*aCjKm=InNnFKP;Pg>Qm^JTG?N_xgd<LY~m#K@G97Fg$zj2*)nE_3h_EgPObB zH=+<`%I0Ab&+HoYN9*Fu?L8xhX9qnSKM-V0T`q9N7cbiq5V#oKb0%T`SZ=7p8nd96 z0e0(TIcT>*Z8T&!H1=E{FhcM&n0;gKn>OIP9#reN1B~83Ij5Tw>3WlWnhkZp><NzD zN5(Tt#^3+(Ng}t0i!}`O#Tz(sLnpTj?u}c!1D!R1O*7<XNzfxPehaD<*|-o~3M|8w zpO@nk;oQ+Y8$vua`v?OJ#GV2^f)Msgodt6WYj-_(`tC<=K=}PJPbEL1a{3rgJw77z zhNT}UKRU|57%^3MiwmqiN-Pk3wVk1TDfd!Rj2P~DU<rG(sm!HZCLTA-%sicLgkqL( zhV?=acL&lWB@|*~ZvgPh`lbWSz^&O$^&R<Zf!O93s;k{JJYsOd{Y}Rd0Oa`|D&G6_ z9K^;rPq_(Tc_*<AQI$)=1rJMNH(#k@xYV5^oBh_@JHwn+gOG8bwMwWqY+)8|pz-~d zJKoTp;_g>GyO$rU1hbA9`wL%BFULKaYTeeKL2LuLc+ins)0h_uPjr1lpqFG@(&N3$ z2fTS2`-4-<q=?NjgJ;I&Gt=Og2+sfp-!KY6q4sJFa6Db~AVYlE`rIatys><XMf`CP zYmvCbGhvrd;?P4b>kuV`JnHw2l)Fm5F^ZgI8ttwSv<u1^IbG6D>I&fswpv#R40P$V z(^RHFvvDbTRZ^W~zZ>2lP)bv}wlKxreDeGTIeXy4$$qTXI3;N0)q2+B$Tk8l8-rZ# zK#^0LBSS2=4rY7%b`6^^y(S9h+^^a%Jvs+Ju+7zF_QIITF6*+*5Q#ra42KY>l4}v^ zi}3Q<#$R2t6u5IOSP*h7#goX2Fs|K;G=HP}MAN`_Dn4s{k!7uEHA6%B=^J(DE_=lH zJOgfh_e|F`g!dQZ4APzQtH!v`%JA#uR`7+cvoBC1tTv_N1lBpL=dak_bl39WMc1&q z9wzDNl7?}#Q}r#1byefA81O%oYe*`I&)$$fejK5Ezj-hLlhwA}a3#=tNLRI_Q!Yho zvbkJQF@=rCiD`=HtMNfT(F=&yhtz}<f0#DoNM}~L4P8|Mi)K=UVVe~4eo2Fda?0tu z6CKYa{+iCe{*c8R<7~9{Ag@Jv6k6L{_4@O^dA-%%`1QK|=Tla6dM=mUk#+Fz{BIK~ z$sX^Ot5W<mV6vMRc83V#^SsBKVTkyNgk{yY+PQLcd54}*FHuc!YUca__4RpPz>`$O zZf^SR<QV6laA7MJeEL?vK9qc$r}1RV6IMNLR18+kCG;jns!MIpi3)d0WK;wCc2mXu zV#PRr?CfZHChR=<NHx0id_C;jlbVoSTJX(3yuzGbpi7y_n)l*%g?($4I$hO)R7}41 zvh#A%^sdp|q8k=DfPqPBv3+rep^#F07k=arTt7`Me}dm|t$BP*E~Mg2*xY%~(78bs z;5Wnb>@CftmBzz_U@NO_@21SzrcTw_ad{fjyWVRE=x~<aUV=>lEW=nQYi}U+%Cr-& zwNFC;Ayf-&mQ2qIrKZz}uv1~Tn^RRH+PNbA;q!4@)gQ#m4$IEh)h}t>6m?YB$Mf?> zN=UBGdu*lO+fS{bjMmo6A`bqZ)N|2BZ!Mjq{(TLw%-kap&FvuO_Y&O{<+Uiez#bPS zFFjx>T6Js@qOe|YX&p|ry;^!IT1G!K^}~Jxp1}w+UgLBTlNQbcgUxGXq`j4?n9?4u ziNZbXX6H<g`7tMjt0$Y;3)Gh@r{wZCBNS^bIlX%c_=A2dYOOog)^BGNUJhf99kxf_ z{I*MD*OqWFc-RZRyVzaR;S*)Qxs15>O}oXxtn~iYJsw|L&2r>1wt@!NaYNEnzq|JW z_l<h?+CZv_4;a|!YC30=e=_hoCtvG0KGO5a;}vK*IzP;8Bp1-F5QTv3z#~@`r1GQ{ z?zslh4MT6U4;5T@Kw%A!K|F3CF}N}pu3DTJ+{VKMn^p4`v~wM-vxv4|@$d>E|G|l# z)d8BM9T6DEd6Hp(%mz4X6}klTZvJEjhSNJ)e7aC@1|j(OTfy0+xfPwnCzD^)@NmS_ ze8=BiN@<?+X{<e!)yQr}{|aCJAyit5>|i>BO)=gW-b$zW-d(ivTiyqc$I05(vn_v_ zl^tPsKVJ*NX8Y}8$aoR_d*0w+@WK*L%yJtv++f|D1COvSRmLTx3Ge4|5##qa-^9bz z^bE-{b?%y1B;Nj$Sgh%(SEz4(%K5NPcW5bI=+^kan=v|fed=Ku_{Z28Z<OGMLAW6! zib<TZ;UP=(MI>db@VW6p!ZUX^J-TCGTlGCEt?EV+4mVPrQr1gp;k5{gOKwbAs1UKi zIrVeU!e$XU(Yw{3<vKGXry=+Tcp2>%Vks$wFVq4+j}V(MTTZ<v$+~GD0L_`TmtkZX z8!@hVHL_*JI-7i@_W}#W@;taO=SE<ToT)yKSFSO)((;9hBCMGec96u+0^CFCHop44 zNoIM?u2hpWSs}53uF+J&+9dF>9PzyQme?M<qAB8MzLZ%w#JB|y_HXO~?+G$AI~Y1^ z0>R#VZ~<Hd$y4{S1ObSUS%s80eVhT?ROD2Yov8W&+^bP-G5eYJSrj)<4t%ON{ON;_ zFC&9Bl-J4y56tAX$Jff!V-r4S{O@jE(le5W+ksz-?t}yX95$k7sjFllKHCKHdpJW+ zoE|*#j;X{-PhoH5<`bQP6j9G?HE_!^pSej!4}ML7P}GlR!ue*Pv>64K!4c7zBwctc z1m@%^U$oH|sQROVc|CKQaXImElHeX7d0@R@CzeF+U<cyA?BE8UMhSm*6=f;xWvN<{ z88~G0gvA}-EJ$4ZVJqoll?C^tA-i+^eNbdFLs~2%XSqOtCQu{;%-g1XHlP>7wsquz zrU2Vuiwij!XNzKdR@51B@WBJgR$`${I!vu&m?wl5OSTE2zAa~)Iq9NUTsS#Jn_!+0 zHnQ81?2ylkO8<C~SjZ*(@_TaxB~+5%BgGlKo9U*=Ry-T}qO0Hy{e`KxS?fQxOv*-a zLJK8#CikTDYd@(7$V_L+Bo^R9<Dx9T`v=dPxcJ_1)l0F6^f?24n(9MrP%lEIn=>jk zu8|XB$AY(){D(IwgdcG+Y+c-s>9U}D*B!hEZR!MSYqN+%TrDOGp&#Zmpty+`!W{C! zHcIUqpg5mJ@VCKG$tsCsB#v-pNnzy{kXxf+&x=sGDv9zDz?66jkqpSc<IdP_V(9;1 zu6oMjOoN1#tAR5++iNGAVX9We)Go}<1VK(;x>AsIh&W~nr!OcSFYj!AKOOxzunMx3 zTWo$rR?4n~TDM?Vu4<)jwHkHn=h+2sRkl63KC~pe=}oql?VUFvlS~z226!ehE13xD zTqdOBj$h#T0C^vB_!nRmuP<Y|Zwewj@rN!Tj^!Y;G9U1R?{(kw)VXS%dp)45i5wox zAO&=imyK1+olpOnRkaE&);PLXiXh-6Co6Jx4tg9TbFo*1a!I#Q@a}cta*GvmmGsBE zwc4ll{<~y&^k7sYtQ_yUa{XJ0chnsGZ!YFu18z#6hjfR_Du&sL>Yea|@c#{_RLneJ z-TwUEO9A^YT^l~o!qCZrQP{@N+LXnJOw`ic)XDjOMit4*y4LdwSiW-eMba4A+NJYv ziyiIF>1=sBlg^j`m|&8Ale+Qws~NKrN>a(=Yej_{;S~TK*A3)L;ox)DAoEb<!O=gS z>36<s52w5P<!8blIsHxsh2=7pA&>}ghwAug7SxR;K$=M$f~C4X1AbVOH~?#!o%;kO z96(+Q`8tSCQnY`@_PNmg;GQT!KrsDffC}T24PF>Hky!sla&SGk-<JW{I=f(1aVz-F zhs+=rx$|ekiGHLY3RloQ77`;qd*GGvF{;fw{GwR`Fri3}-IT>?g=`rajKF1*LzpnF zS^f0@Qg1z*Bsg-ToyBB3Hu!d`Hw^GEDC+-J<SA+Qf`csP(3YE8jV!rU=z>{@+5Bkp z8kBE~^w3c?3+|(x8l9o$tBT~{0bkW0L+(cP`~eJD1kruyK#9NlY@P+o$|qc+(!W95 z%#@s@GAk--=iB%<*pAU0(AzmdxQZ)+FaPBMcD(t|5Z3>mjfM**rvv5bkYg`tS97eP zJV;l%F)RC!euP%_UQz)-jFsDL3|435mB;sPa7P7VSCpc$P8y!(Fyd_fot+L>R6U3B zK}h7O8s=OuOXKZEn#TE~hf=fu?VIR#W2E4GNUtvI`a;UmC8>K{TzuuXz21j;g-}eR za8^&9g1>XQ<9Kyh`STw=oxklaO6+%tNgO;C`2qAl&Waeo+!A0)CgkK~>iB<-jLN3c zyaLjvTq1`pd_W%5ry@Amm^>I7I1OuWZxkzj_ixjZLeA1%4UkhwN<sA8-sM?b2j_>M z-cWXr)Oehh$p&8jiKDBV*WR70|Kt+B<&-|)bpc8QmT2q~NAbcyX00Q(iR1boA_M#( z#11yx`F5>?_h!oO+5oC&Y>4sDt|9o)@eV_X9QyNfIG^>J2T+DU{;MF8NU^aNQ*~Di z>Cpz$=f~~nLlbeweF2dY6KUQR1BDXvC#(fqH+`muR9ottkm!sXny#a;GWO-YDT3z< zsw+P$dxLizwXz9dOqDWB;HRk6t}XAzbFlmig(jnc21BRb<~KGDe#;6g<s6z6&Sr#b z=hMIb@P@QrCDTGZ-GXxV!h^u8bHf!&PtI*T@z>gAO>QXbytKLM9Rz%@micskqA&>b zG-=8<8pgnlCifQG*Y5%qn!(9*SKT|J2dMHS8tctR4o^6sNI8FB&gGFT=L226wS@d4 zF9?IED1IPT0jZ$u8h48*E77G3UsMZ(Tx-rELNL08d(k&W1upW-M@9u;HJ3{xbA@U1 zr|vcl&(l9aA&N}07@!nh1(8wd{s~bgoVWlr;ioNu9;>}KVPRW-g71Ck2`}eU^EC4z z_Nq_sInEFCjcKWaiWcztHAL0$*IzEY0F6PRigHgNf{|j+Hv$snmb^pLIig9AVvuP3 zZ%HJj!L{)ix)v*PnfD0Ot7oQPWt$L3WM-RK71b+!KFw<V^lw%9Ja0dq)YkxKRq6~? zm!+zb<H^{+U8E5xsnMTm3cf_OA5qen>!;3#ek-&xJFXvX%p1gX=wACl)M-S(wm>wd zRm14S(jCCY3)<#<J!hb`3FIhlAE*+M#98zXkNAH^OHF?&l5l(rd%tB@KP^DM*YaLK z!T;Y9_`i>}sHu^Q`Ts72YT8c9s%W3Cu5wO}T)#3k;&80|Am)qWQ1U=*{Y(5xf+E3S znn|*XC@t3xigL)R!(M`beURHGDqT@LHwrvdCyD#IBaqp*rA$#BPg9n-7HRqBC0@1N zPg>ibORbE+Q~9s0Zp1vqYYY4RFiZ&tnIUO{t}5biWb_gCpt!l2a8w$(h(2ocQn9ay z2-X7mO0LSitO!N;V+WxDEIn0b1|>aZ2gRXt8V1WYI1w@NJTo!Y{Qd``kr@_+I+(>k zIKB=3u^j>i`82q5W$<x6O<Gf#_o{g6gppJl^XNTbnIY!jibF?bVsHdxBAC;{*!L;V zX`-7aV8BU^&@@ztNlVwtA<m&$e@#=Q(q=YpK^sr8WmT)hh^Ut=WWkn3i?(FVIjsnE z9?kNFxE8HuORT|B?Z=0y1<4m=H=V$#FEBhbKCRHLyZcwbCC<fP>7NfH9l}pvq5tz_ z2DbrdyALxkd@~kyi`;&K_K#dLyda)A(!?A~XP*?#y1e1y0or&NSsB$nT#LbcO?1AG zHp#dYx=JdpMA1rV*<F~ff4rGS#A_a}UMC>GklTn~@TO?gL`kMK$*Da;lJ=&+lKH~h z)A7-pUbkjd^Ik(}p`4fG8f}*+M`9*0AO;jjRFci>3MLPi%}s!Z2S<PqLbCwJ0K^eF zzr!hIQ8sHi>r0BUjH}DAJ%UZHu!OASk*afkFj6n{Qj)+{E2Jzn7I$A_^f(JXa|d<J zo}5jTz`*8zLe6YP`$bmHpQl^=jWO7ErxI{rb|=C*+gx-Ly3Z>pjL3j&EXXa~Y%2kt zu7nbYu*08{-KokJ&8NdY4l}ynl!TQQ7XNOIX|-2nvzur5o-y~hHFJ&yTEoScPJ4=8 zKA_K)4(`}B+z;}rw@5?a$N8H*6Ft6^Jh}<)7FmgNSgX<K-frp9*sH4dXm6AceQc5d z9qLLQ>eS(kDn&}x*IRUi=PEKh93+7JjpFrYIRz5VB3!H4TF63=Robp4V;J3mpEtja zi|hq(QrbxLc!6BASQJ@kB@<BfCpnzRngUz9hO@R_5o>k9zJwb{xsWnC!d9&_RO0-F zdf^*CJH<j^waaah<vxG5#8)qa&IAL_y2FUh$o@z;Fq}yM1`hYHyzFCdEj)n)V;=g_ zCO>n)%LEv)Gnkg*@?nju-!s{bv&7H0_+^dbJ9Ct4{%V`eNQ(9eisF7j;eQz0>~qcz z2=@K>LX-SVH)+Z5;qj~!N#O{{ZXl|4hrx@<MMcm*SkXGhipP63AoBczXo(Ex5R(JL z*pZHHFZ5pl`CvKeh}`_RCPW6_$&gAgaJwq}+tEiFVXO}a1`C`)zR>TvY2U#rW#y_w z8n+HOBi>fBZc2ZFGj&m+yYL6)^8ZSk@L9P=l8-+KXu}A9y2*Kmwa~|qK(+TK(DOC7 zB;GBSQ0=#NB&MkD#>%ts=Q|;ygjyw!*G!W5PQH^L)Z&r>L^0fD_V9tuH&e(tT-tj5 zur>gYXL^;-?4Za6(A1qSgseIQZW<4VG&+V<tO~}ywLF+=X3T#CJz;ht`<jt292^K9 zxpOs5opwaXxBS|<Czk3Q`di(8vMhPJ4*D4PKMk8ISg%E5UwDpgH)JdF*tueRU4l9O z=?%7e9Hxw%^7wuI;QRt2`+l;v-07cad`1|4;wpW(McoP7hkrpQe$0O`PTc)5-GvPI zg&H|fR1hVWMwy_$T+C`z@8=;SM@m5&pht{A82Uo8wfS@R>&ifQj4uka>^UA?m|$J9 zMXuq5?%#g^DgY$DS7qPc1wW`CKluMko9>ueKJydl|6hq^ES;TgOvL~umWF`;DagfY zlXfbqXkVK>_X1|_#jQyxb8^=H7$x(umb^390cep)a29j*4hF=vtg%mhO!G3O=<sT? zL=NL~)wmoPRNqf8D{W54TPK7&b7@$py-wGeE%V{91&Liir|A*?=MUdwUlU)?+kdP- z_PGef(98?M4EXwugu^9ql@-&{IFLW}*3#;kX_0jAuX|Dv)dI&tehoZ&Jn3k;yDb|} zLJZ|Os`Z9FGs4aUD{Wy~NI{ByogHPP|H-;lgSp^NVmR#TZ%{`_(sfZXrC+J*TU`=% zNLB{h7&4j#GU`*g<KhZ1UY7()74I;j-TSMfrAeiZRZ22ap(k!FWt-HfJSAo(=f&yV zb1Ws?gI~7@w#?zyENW+dxBMznsUMxv$qm1>Ki<57Zz-*Z<gj`U?4wh8ZDUS@5xvL= zdhgE`z1s!9Oq_hOnLTl%)OC!{z;o+0dFks_N5(#Z9l!RKhQ>F0_NuLWICKzFd>H>| zRNZ*jG_FjKV;5&~wl`~&Mk$KT7w?_Py|Ae7Fy8rcm%SMF#^|dd16o%0;P7W**KUWb zdebM-E7nnTX>3!RK8Joiu2;KSXv8}?AjvYskQ@~t*F=KC<W?P5mt>=+y~IXux9(}v zwtnaUhlbBV!qxTVdsW_ty5zcqQDSZ#V(y>;zd{QJwL<qs3PJO^8k20uJ<#|v!YPrq zPlC)+QXFxBJ4ojMuAlT4$ISiRXJr(zH#X>vD8p_RFC7f;VzMp4a=JREBNeNE?lUlA z$HUE?n4K=motp2RL<t7m!Ood2GNK$B7o&WD{s#(`mNodrqLt|lf=ROLh|1WP1-SXF z&YPS=g{gvHKUjGmz*2;bE!alVS;~GwV22^)XUpJ`!j_H>XIAOwhlzWt72=6tcH|TI z9nzQ;cZU#&+Zu%9u20gDw)LNPTv#4=p;jS#dK@9oI5k1x`rLo;L#Cdf0*#YU<@Qv7 z$uB-~tP7+`CtZx971)fe$3|Wq&R4z-(<%Ac371l@j;)inFcj>2Kk!8<*$w^360Md@ zJo{lf(T0)DcLJvSf4o(qp;U#2`5K-L?z?I{_8G1a=3bXG7v1-jzT-D+&FWpBl)DX{ z<CEr-KK_!UxvbPRqr}E9gJ-}O!fZAMrp4^xx$M}U1xYX`mBT)lb%vZiG0b1mC}WzM z@sy<_7`BZ)s2`Q59YJ<>2zsTRrc6m3Lv)^qM;k8Ts8t&-<H%JLKTjs^4tAfj)mkYt z(w*&Z@`PjX42>|w5S&R6k~4{dVigL>%p=|79T}&KW{BS)MDO856HAoi)Ohp&n*%ef z^%?;)8Ag+kS&WAko7~cHgFFOgJVj1+W<=tgPtJ|vtw}whTwQFaOqM8$8N#fxh0$j2 zoMsMLM>F!~)=1=7iml>FGhsnXdv96X-o>tZncL3S{)ENeXD0C638M*klP0p<DWh5} z$)js`&J^c=S<|bU4!ESX<?5V)2V~=6Sw`5&1mmEN!Ns&rbhxJfi?DZ&uI$;`g*&$G zj-8H;j%{>o+fG(&TOC^++vwP~-AM=CG4B4o=iD*wdA~Ep_s1S%R;^iByK2u}nDcpZ zkA%+}ZIigzc7omw0`EHdx|ke=aXdwFG-sihoWVq!WllB3m!NTh0k(Hz#u6RRtS{7U z!bsdPJWV2Ro!pZ@L=XSh4qWnr4Eg|M`RqZvaE|}6fEHhjtQ_pW7^QDBLn8gx<*2!> z(Z5%tvGMf^;KHb3A7EWjkWfwaY2yR(48ETtsMN5jT1>BW<AdF}+P4n&7(71_M{zS? zr?Io$O}(-u1Hj`NnCH)$fv-klr#i?^nK}lxuuY#4Pqsrxnf&YQ<a8E4|4ABungJF+ zYxz9L>t(Er;|~~`dzs|kj&L|=8HvpHFmZg_w%Yc;7+8_us&DWfHY)j8T)_FJF)i22 zkRI|&%-MD?r{d(6IpgbT&d;I)E4#JSXGVi;GP+Gv;%Pv%O-M9{XsTt%7{SA`h}<=i z21DbcE!+6ZuwX5PVsXb1uIO2k+6sZz1I~XQS@@cZ$_3$k`V`CgUydyPwO;?f-j!ay zYD<5G9=2PjCbKis%``D!BRD8VL%QJ$A;~_I8et&j8%c$s!b;~bFfio@xK%7e*?`$F z2Gt9BD9?hg4=;CL)gaWYYgDP*)Tmdf-|?Mnb0uebo&DJRVIY{7<FoUy_0kRO_<*30 zy2lR39Q>b|KgpR63zi2;j=siB&Wybdgcv*vhfBVB-p~s%gi6A_b7YQ;@+%y}VdOUL z(h5QDS#|jT4B1h7A<R6ndPi9FN%a1bdCl6~OMVvx^+V+af38=dyU71h;5E?OA;Bxz zzRPTYgz+(%By2FxO-V@S^ydaJ^c)8V%yn*&i0n|_5oh)$dF4U-?S{YXiITh`2^Mbe zjQQpqo-eOFZd*MbUSp*Bm+#eHZsh*Ts#dgmr?{Dl15(dlDuo2vTO9XCo?mB!raXFF zZtkA#{r-B$mT@92g@dGfK#{LO!{Gjy4dRZ8uq9FB875P4^fh_13@{ZWzvIaS5}$%5 z(@o#ykLg(rlAprlxbUG(GsfET;7T*)xH9|u$}R0JZR6PLEV#HvFcz|n?d)ziF-8LQ z)>gN-F@IRrI>c~fPY(1iT$>kenF>Nb$2^Y5K|`-smf~`n>zIhziE80N^w_$eJIgh2 zl`<^J8kwlBu3~aB0im(7MhB{^kC>dKq4H8j7^<s7n4Gks@**)cR9+T_WkkTY0$In- z%jPM@zo(yCFoBgl2Wop)&_96s4P-3Sw{P+{=3SwD3pyr|xX2496)jB-lyj)D94+jU zCf_d#-54W1n~J*YX*+nATX?m%F?9$?I-NrZ`SB{WdDkgZfwg(UFFUJo7g^c+k!j1C z0>S<kOx5L#Uw$weaJMvcSesw|Ij<bXLcnGjB|uA#8xdbOXKxwS;2d^pV=1X`ulJ*a zQ08}Lfi~{glxRaA)LN>yH{-95fMy-7v8{;aV2q>0KU;@tzF&&9LzMU{-93($BGmDS z>n>XubNuXS1MqbSwABe{w7T0@FX`<lt`nHZ$7WxPC=PAxV<t)D%*|<&B>U6qYY6&q zCPs8<(o<@isA9xvMSh*2L(KgundMjjhs#{kmLtI$lNNVZ{)3CN)%|mY^3E!%gUz>| z^au_9qyakZ(*c9}0%Xc08z<PQ5+ABd$!&`ZtK&*_6(B#v?6s-1!@MJU%yg-_+Gd!j z?XJYM2i;79Ze@tqWbXK98ck@2?lGo!BBk8%NUDjO8@J7nCNGDX-(599WMayYuc#Kv zSX)C=^vxL##wnS%?VpU3<&+L8uHIJ!9bR;|n*{<|#}+<!_RFcTsk-4Xq**Ibn9RSJ zF^PWDXaE6I@Why|-Jry(>7bXGvtXvI5k@fBe4u>&aoB0oAzZl>h4!^6^brq(wJp8% zm8G=^GIre_+3I+H+n0$iwy`Tlm=*PV;BB*EmzEh*L7cXO0`;f}thfs&-(xb+46rUN z3kjWkI1$M4Tr`n`2gGD$edapiTJ8&%>Zd&u#{p{FsMG5WhV-;<WWD}d&7Ltb&Wn-f z4ckuymW{%;bx1qm1h|O5Z+`wNE%g+ww$d&vFwyI37f2mEWzD=7uMEJ#`azg^RR~US z73O2x2FWy(bLt}zlTYR_sJIJF;I1@eU$D(sHxu^Ta8;9(z__7+liqmHmNSRQq7jW@ zBn<G5*r<z=gUDPm#UHCCZA_EH$aFK!8f!|hqI6-(gv{h*y$opg&?Ujl{^gnN7yZ32 zzxely$?l)W7Vo1hPa<gNd6q}&H|4dFCMS!^uAktJh%TFw4*Fk?psEy{%<lC~X_2{x zHY`R}H1Qnl^Rr@Rka$l+s>tk2TH*7^uFQbh?rQfclDLtE1JOT!G#eS>`g7^@4ftf$ zm)Tfz+8s@Fd<aRBdo;<=HZ)h}JJ28&q6gNxzcwUMO=#k!)|>deGhAj09OFji^JNo5 z;PvRP=33g?C-f{XQ=cZXYYG&;i)PMD1yo`D#r$q~^h-RNj^3KS(tI*kWB7CLJRdx8 za?WV&Y31bb@Uaj@6;|3%f~QKpNv_i~zc(T7*Hy!U_Cs+=+4pm+N7Gn2Q>f8nS{o)V zLm(-0z%*meAaZ^Y{RVWa-epUyedkZRl@7b)jarut^Hw{j4UJZT6*~<3jpLTfhN%+C zNZXapOXxLcZ?VyfJKa~`*5PNy-v@($l>ULq*OrY#j<Wf<n!Iu17QsrbPt%{gf9|>= zzwevCdH_)O3ebT9pE?gwPdQ-vxnYmHla4x*f+6Y%KKJ>5`ZiC)fn6K)d+fpAH;I<m z9oupg((&7vaGea{h2t(5DkU#RmbGW3=O<svI8hw`R-{dsd!|uZKg6K$&zi{u059yG zq>K#7r!oqd7Qz<dfKBw+tgfVah=h+%Vs&R_m!Wv8$lN7g^_O-K2W8kP(oNYN^BqPM zaH;TM(8z@!`82C`dQ5Q6i~4Dr0^I2i6>vl$J?aVAm*F{OzY^Po3s+-=H6S^HZ$K)$ zlgn8Mg=qbv%9!zow5ZQEg4%$n0@fJo;x0q~+LGkV*oR(d;rudd?N?kSa-{Cm@vebg z(S%Xm#g9RuCMkLKNRJ^3wyEDi5#y4Bu@{Mr%-h(d*FBLP9mHEHDQw1eP=b_5-9(uN zqrqH--_t}HKotQosPfGjRq9a82E!O?&Jeq>Nc7!{GWnpwuZk*-BbSJ~0V!4I+B~_b z#)ZG67`sr)lKxa+icys!{^W2b*$y@uaIe#Bimk7`Lb(Y-*x|QkU$Z=yDXK#BNH??w z^FepL$K?KHbsBF+>Vqs%ccQ#3%beDYNL~MJ5A!}NWfkQc*PMPl6Ggr1!q9U707dmo z7nF1}B`kf6uW7h@Swz<!2CN(sd!S12t9WAY$0n^ZWoSeouOnm^Pan<satoFYF~!T! zoDb@<yy@jXT}xQ?gK-;Wuil<=rZkjPy?hf3g5(;M_9OqS2zQu3{Of#Mtqq~cCo#bD zoZ31RZPJ>)aPM>=(%T9QsZ0m$dyo#f551$gk=Tzs?SMDcjt=U3Zom}@Q6#)4@acsr zWOW1gz{Bvf4^Gso4|CKT7TxL#bGY9p^<Scz1A;-gFSvL2B#v+|kUb;saD1y}kJEpl zen|*zkM@M=tZ_LCU4$7JF9L&jU86i*15jOY{@p6jeE~W~46mOc3j5!f!QGAH--#XB zQ@13Ia5O$s`E+h=3IXt3#)_kTHIRIVzr&1YO{YH+jNCZ)s7l=qrikT|GtyR&O8f{i ztNfszez9*#*H1=}v`gzAQL#L6C?e{sk?BkckXP=9mPa{}WNf1F@F&O!@(PUk9qsqn zODOEW*G~vc9o?s<M9qh@(U_qfA#3te$4;jg-t`lol@fYSxdIbsm~#63)phTJG}vQm zQdH5rmzohWo0COVmmlA*=GrI2(cM_*9^Hdbew^8B8>eAo5)aKge^Qv_E$<Tga{^%q zO9ET;__=&HRB1T8Peu@N`F1a1mpnh2<O|ogLenzNR^s~(o;tx)h>HbhL(<bNrR~-> zV_sw#QJ#WKh$X=EY|O6>6RLC_Nsic_&3UzQ=uzqSrx>PgYWj0ZTsH<EUp4KF+s*G8 znv@Qyd}oDN^+ZhR?NhNpXr~-A9^h*7C%2jqH=2i;DV9IOS#@>_Oo4=Vlh6}}*3a{I zjmW`kWR^pcKJy$V2;c=fQlKuI4j773n3p9#=1l&(@CE2LR~Fi;XL<xgTeLKn&k?>a z*#8`$I<lhqQiw-Dm4(*mh=NGR7?D9gZ*j0#d*$KXV29BY50(edD}<%zh7bA39&znF z*>%R8G!ww850?CNrZjCapbG--yIE73m<dq#Mb4R^H-gAR(DB03ovzyR7DTZ>yemN9 zxjM8M@CSNuuTEt5U=705?Rfb6XMR??BX|9@*^?(uHqr_hVG8-X4=@WcI{VRmb!eX} z+rG3X?uGIg<zKF^+@ih6hx)Ovv#u8bx!ZbcWH!z~BBw<RPM=vWH)tCAWsiznEui&A zv@+@`U!=H?48Q$SkY)qUL>A$CQV7<hQlSgwCovG<Bs(O=SjaO?JSqwELcSgeWBA%- zhYvcjsr^c8RM=#=dGDK1=at)~P0YwRt3fkV&#Au{d@j8xEg4I}`<aL%*;1L;nsRR6 zbx~S5#jI~=&vzq~9WrSF>+{HCLn_^B9;14HpluC&OXDydcSemye}Qc?JaSpp@JRhZ zaTTz1l#v6<n4Z$8+U@#Bm;nh=E!t2I6CmOJ%o+(2y10ttFe7ly8jGJsrzQx-qDtLS z7^Hz?UZsAW04hVARl6*fPoq~uJA(L6sXdUYuP#U<eiW2CXk4Ou)cGe%q(d7aaNo5# zjgXBMRFQ1M5*^US4w59zKs81F(*Q3hRdi-eBoo@;+8!6xmtot@B*>{#7ac#L@oWE} zk+SnK#CD2FkVU5{T5()Sp7!PvafuF{+Dnc8)imBZRPnxC;}H`i<n1fhTvre;!3%MS za+LhE!mCslD+OtFZ5C<oEeKR%2@Zk;EZ`rFEs(^<ruKrGnOd+;Ejjg19eAJzc?Y06 zWI&b8{4Lk;OqFaNr~+xBWu_Obf03Me0BM1P9)9@)63G5(@(iRt3@T@kRKOcBs)bhx z;j9)5DwXc7M(>;XkKhC9rtLqf|J1yWxWoyN(X*;;IIB&9x<C)=mHr9nfj3BF1eE9* z2jwE+RR%{?Pb(3n6yg7q_Y;(70}{3mL7Hx$)*L|H^^^@eM<uJls|4|wwS)2$Jwb0e z1=73(C4OIn6v{x&uUM2LlQ)oxx@pF(pMpxu6E6YqLNg$>lYct)&&wG6lRsru330Ay z0gB;@9}-dZ`~KW<PO!+Owo}|w@r?bKamnNG^9a$=s8Jl>bp5SS()r%VWwP2#9!j$M zmjslu@?1SKp~jQJ3LPv|im#%ESqLFAr)>P-3wodBKE;-=@+!(Py8aSa79I105(Cs^ z;(l5_JoHZv%*|zVCcVCVLS9{OWL>9u=3MIBCHvOhp!^VK%!~y#prh*FpPB=P)AoIz z9mWD7a|woY8fq+;pvMHG(9YE@=iFX2St71EiP!w$6qXL3G3(f?i8dT^fpA}y^L$$g zF|BIwgjLg;a)neQUep`IS%M@4#AgLp%5$h0LKChjfIL^CY!X|Xz#||c5+xa5T0*>@ zA}L};d_zy*=Fo;1RyCAY%)B|QTc-5oELDF=?Or(cB26hvCr&x`7BNz~Lt>j{@mg(u z`VtL8LrsGH617miohs)zE8w3um`Iph|C%)5iX+=WbqV>mva}M#T6ZO60nl}c0SU!K zMNJ1e<J;Ep*mF2DrXjRBson1%?lImNtUpjhyq(7DVQ&aQDx1k|cLGof7qTT&5pmzY zw*?9k7U?fBoCas<k6*G>-Vhi4Vf!w#8Jeq7_E2(czW8qFzOU`aEzw=6wFtw}jk#^2 zb|4^OWx-o=B&XQS_N)T<un~h7RF@V0?iI-O!Dwg$wMN@HLPl|L(<NV5J7bYH8?AY} zNcBcT4ab<-EKLV>F&06IQW+Fmd~BLax2kSTb+;C}<&qm!f>3t5DRo8dMcz)%ruF4o z;#_eBzpNMO&Yam3A;gRR&j}pv#D)?0?|`!|4C_a4%RK@^z8wIz^NFf0Jr5@S?rzV` zpVYUHi><%4PWY9KGt6Mp{HCTl&B5fmFJEU25PrFz-r(~DiUjfJ@d-BQ7d+TG685Jg z>Ho2x{J7+c-5a)YYvV=Ti#$i<`=vLRWPj)6%QeB{hA;I;%x`qz9-Ww7uBfrsaAUAu zY4%%^NXA!lCn8{n{Qg#Fh_vThSKmYA!($EpHB*gHuROsNP$+-UYJ>1Zw65)6){?qY za(V1w^YSFHuKKEfsrV{*>Ehpeb%1g@_z3n!;jff8L$XNlB>F7=TK25qFV&k&GDot9 za?yJbe@b$bhe!3%&XV#mkTC>UsohD&^1j;?1q+$$jsYZG_czor<gd~PX74FCq`>;( z6Wj6E_iC;2JhrmClPR-TsK(e{OZ|QWR)@VW+2M~4Qx;G3$2za-cL?uUse=Zs61PRm z?C(BRVITdx>AgmFCk|R%*FxGJcS2h6H$plN*R3@(PkcShAJM-d-)kLMKdKy<fE}C@ zl1HXp65zS^1i0H86S{qi`q+B#d9VL%@*Zh$4Lrl#DZU*%TD<eTx&TBU=^y;>p&z{O zCB4zW5fA}f0=d8e5bpt#5zzOwAKU~IWfGgrv4t5tb^y^8mfkV(8mboay-xuR_7{e` zVJ|GSKa)scnHcwVgCn>@L{6U%8P|2mXd?tbw!?&q_9DhIn)@04GSoOik2-8UTRhRP z8yDntK6n{E>~ycpGVuPDkfdp-HAr*_u79qO+p~iPX*pWC-`&ah+M0#evnm1_C8A-s z(OB+Ul!f!L85aki=4W`JvB;?!3rmw&4(2%~bvP4RvZ&8c%53m{$}EPl^{JhcwI&8o z7J4__<d{i=lE+jUo*sd>7m|V3BQXj8p*e|6f@Tq7fax*-V5cHSv`;H3ABl-Kvq?_6 z7#sH_qax(c4`)ta8rQo>F*x~CPdf`@a!junTPX<DGgx$K=CX<Ad~X!VNnW6}c40!J zceY1&<B=5en`R@+z01{1U0}p_CaMQ`JyI+uzmqQ?GGW-btVEY?(hqlRW{bI8iSBQ1 zeiI{m(3?vui@fg=E+qe<=6XDZd2y!^iRAU&*krreSasWOzpE>J=*jC#<doM3s?KeD z8x0WA#tn3>W(kSfP6+AP&Il>$CIO+33jXPB>>m+r3?Fz-Uhf;mO7AcGm9PNLPBbFs zH_7oKj>OFU^--Sv4yo`}w00R?k~8#96@wU*Ah6pB5~Kk~!cBySz`CMkqt!3HOhKb| zV?R>sPW;8=1pYRo`{^x`#QZQyfO}~CQWy8i=Q!}~n*U{<_fYft6QN_TQ;e}JM>w&A z_6%1nbfB&2x{&*+V}i!u7|K1stM$k3R<v8;*f0B4m3wi*)O`fVL&PRjcqfRu&v|vn zYx2BGf^b}BVm{lU9P+Otda3J`h&acef>U2PRmku7={R|gzi^|zqUr4xYr@T`y#QC( zObAgkb5r#9fC6U1_pg6Og(kc44Mwy$gOuSuuIw}ygEV>qg%KI4eJ>RNVk=>hb^2VH zDHfQFs5{vd5vakzK4vHrmXIQx<G&A=G0LpNf?Ev!`?QU7Dp8ezb}fNE+qp&jnUG}e z38`X0B#sR;T$<3+Af2H9dv;qCC55<@%4!nXXEP~eRp-Y9leqo52#A#hxBb4G(nYic zibJ_%PE*?Oq`x{zWHFdPYSciY4_p%8fEarI)?;L77=BZ+G+PNPsCJ^}^v_9?ZqkS5 zh-7f+(h*V#3bnRUy`zSFQA^a{&&w}yrBsjRXZ_iY`|-jQC`L`^xP#NmzVgs53!a(x z79&_nDPO8QN=3>OlB$U8Em?92SBTk%gM7K4ipySA@OpedPw)fjP%A5r8F34=Ri-Lh z&Mn2RhgXEd=gD_(&go!*6nA>)IYnp1A)2Qxh!vD?bkb_MnMvdnJW`5ZCF7u&!sR#M zP~ni-4hvYMWHe=Nq-p2rG4b*c>;Iti7?Vzjq*F>Uq}*~x@zb7^BT{qmB`wycpz9<- zQo<d*lBGjR+N1z?O_h_*;|T4wq?AhUyyvKohaiHG&tG|f(uu%Eb{UNI7;+*43Q|GP zkb&ad|0F6(A+K8nB$?K~mJPlsRbPfT<bLc{1o3O-5OTs}H`4qvKG>tl9*SDU)R<8) z)3b$s379%O(4_ory7bDdLxHP9Mz`wh1XYDKQ}4}~$6;(~Q9sdy)upfu;uQD8e*+P0 zw@(h%5pFnFyK~e*rUjx*|J>$TT{i9nhZaHHNc9vz-{1{?mp6pdjalJY6|{N0E+`mu zK-&2L|Ic%NSm2{v9Yot&0rM|-;{T6tj=Yh{zg==tbrcD{p^kivaOyWQ^sjEN7uV3T zvryFq&a%^n1hh9nE{Ms5h<Wl>EB*>K!?~=*xKV!u_hXC<SxWeGi>h9^IMUPdMxlN} zm?jGs`3oj*A}4caGAC2u{cY!)5LjDiQXHcFsFB<t&=!`>i;kN-7!udUFTE?vvuRy& z`3>K`%u!O3peJo$46~GdbBQI^2j3jGA($UFgk(LJU~&U&$5AXZP{2q^VXc-(fvt)E z5QJ#SoTmHj(It)@7FXhqa^^=y4ukx=?M4&7K}Pee`y?GorTN^Rj<K(a5}qP*4J*ZQ zSA*dxK(ki+SliM~`Z_XCBwBi?wW)Q~!}yTMjHY3Ci*B%oJ*XsFE9I7CykmrGPsV<m zn7-HCzW<a&5VF}k`X=Syy&ALfn6_#bQG;_dT#b{SRnIZ*E)HoV-IiJ>1INI!Ig!r! z#AXjYQ2FHEX2TS{HCCFxhVTioN*=2Q&f_v35V5zbx&B`6TR}S!`78+>F8RtbtgG0I znHRD0LTsHTiwX@kL;ESSvF8K^k{u&Xnh4ioBbm+(&z3R$+_N9f5x9DT+IjE|Q!q=L z_>Fk<>+{Jnp`^q^Y@ma%I3BlvTQ{?X*Oh9g%AGifq!77^Cs$Q~dG@O({uO~{+N20M zFvie^^=y8c%&!GWrkgfB8Mg7KZH;f#^&NQQ%yOu!k3(a_;jnOtxPmRHyf!pZ)hzHP zPb#iKFLFrQ&W}^2{CmaxCq>+FAvI4e?wP3?>a=Wx2_lSLvqNz?lUNpsWzCS{Ey8`F zDS9r40db}O-U@4Qg#BFR8K2QJc%tbX;F%x5@`W#7vKU1ZZFnYMme0bQVhrUqK2h$S z<$I|T!IP9htHjDP`3$~EN#vC#Am)3C8rjTSkDO2~`mzU^u14oDliz?lgoAc&9imN@ z;=193?Dq7j0>Kf;k#67`7+c~vJkt#d7euu8LwTHW*&X29-Gl8CZBD|*_>%t{C}_MU zSVx&_Nn<&;{LmWf7@aBUmf9p8W(^ZL<f!e^FT{I{rhDT`{NQns#i%syb9+{X*AtL3 zIxKd_kbya!EJm4ahhe-CK)*?!eVC#AQv}}h*?e;JJigK-nH8LNI5MZy_9)L5KW{dt zzze(P<9~U8!t?h+MM{D$DxJXpiwmfdk+YE<g_xPSk(=$mT|i?KCjPme6n7vWyjF`* zyslwzJrKjGMZ(3{v=rG-V;7xbp8b>HmGEf8^!iC?Tr(I0QsYly<;-Q}M(XqX(<}IO zfGkEhRh)bdoZ54)tkI%+@s{Q1&so*YzC;o#lE813;ii6pc<^B~zhd=B@W34MW)ev_ zIItqc<e~gxDc%ovJB_BDvfAI=q~g_GxKb$D)4B804P_iqcgw~j@etX$0^+aBYR+O0 z-5UitG8iY|EB?FM^LNdZWhN8%5DojIs>@%)An0rCU8zhFOg~iGzdyF7Ad14v>%wl< z8~`^|5Ipw$iR323cLZ$M%qKD&nPVy{oy#^3Qk=5iY;X}atKa+-J_no5c`YV{CferF zLL~F__CX!*gQsUq(ChXetGr=Xx@?<oYu#+<?NeM$?F*jgT=1v&fmyeM$8qO5%7;0W zwGVvxPt!=-Al;w`8V2wr@brQwu>a<`_^-jxbTBvnmyL{_n^G9~iu!3+E3oCZD4_Q- zI*3ZY{AL$I%6LE;1LGi;!Brc6g6XV<q9+T6q%WLx8r%4O)U0vm*QBfa%iYUgq)+jV z7XzUPD~Q&@gar!W;ASZbxOHH|&obvJ6Mw!(BLWC&pAg5{UUe-pVNG^JbSoJ+){T|m z9qW1Nn9E@g3sUup6V$+v)p4ySW%;c$!{0B?5i9FcGBiVxp!I|BlPRHp%0r7!iEkIk zTOi0aCF$bssE%-@QkwY2L8&xS_gg$NpUXcbb<Ud2s!AW;X-emPIeFKlJhiZ5c!gWR z0D$jXen)V59zvEX9cswd9Lz3%p;quYV(WNovVZpMvfdg<s1lYjZ8EoQJdz{w5*+d# z|7|_>$FdX#iz-g?a&{c8HH+`EYnQ9ltWB~>^BC<+yEqIxqVk%#6@2QuC4CybWob8i zqA*ar`-`}K{Cn<-kk?V#g>_wzXZxQ!eJn`$PoMtHeaw!e<rj?j>5~;v`kej$cB}uN zq50RS)M(ncsx4u?UrqA3I3y8d@XvmQ41&y{=z~NpKxO6+f&_;QE)UpeZih>jGvA+t zgexzPC|9rAFQRFQ&~ENQrO2PPtYWmbv(d2weQYjF{?@;)tT?Y-`59#5LCbeMpKSZw z^Y{S29RQy^zv)SWyxvumU0vV8{b_#81AG6?<oL|C^xiAg8#n)*nRuAndK8r7g%vK( z$0^@mBciX|`TIuLLId%VPdAKM@i!anw{20>>|%E}cKj846}~i{yd>n;F<};WyMw|= zGg!N*$Tpt2sd1L43kmae;)5DUl54{(NE^W24Y>YczZ24mWt+wz9MaC1q>%pnbwk+i zhT8|VuB7;HH-1<`N+6mU8+cO}8nknje6F9y62{8xDRTJIQ0K-)jEflZw5T{E8|#>u zE!sh`+1{#MeU1JFLHv?!nNSm$kDTF26S4YG<Gfjy?B3sx=KblL8j6#?Q>Y|*GPDES zNr-k)_%+4aIFaHicUYbf>NaDSg`QOfhMuYv=GDK>)=epvuSKAS<!>BmIyh4zu2-?6 z7LQ@Iepp3OuLqN|5+d52!G|5dk*_94i3LO2xHuZIFIvg%s98zyr;8YeCn~WwOj&NV zrNls1azoc_NijCfHiQgd?s;+z7jFO<oF-(mn&Qdl8vE2%sH!sL;(L&~t4Z*od?4_9 z`4GBuOw0s-6Nc2%ZA(16sFC%B;t(TlX&lM*Skua}h~_fR?CRC3NCU|X$wA5j%`R%Q zetlcl#<m1I`~pr3y!dbE->Yo%O>HvGqDxxlnK6i#zGg|v6e3C<uo!#F<XZ!lhZ3S2 z-UN|`+2ea-zamOi%0*evV)0pwxvf<4^95%Y(bm!7sbU=!$&${Il3cG)8QmKSk_g=+ z6tfy#@DQsFxY-B2#sY^P*K-glw-CoP_*BOx<*>x8>R8SX88b6mD+0|#gOyRqFJtZf zoadAAq-VHrRt>`_8|l8rUylNzMf1M0qrc#!@6^8rnUg2RjEE@1|JZnAC^2T?i48Np z>{rj`jd6_HJ>;@QN~Yg_pwlmNq9No=8XI3<*G9I7EpRgPZdpdo$oNSWB+A;)r&EO> zdD)V@gZ^7OB$g8Gin^oQolde~9|>v!+N7OyS>dwMIdE;8=59GdkRcL?gDPKeB0hIx zH;jx6-NXhDuHT%u3yJjbKhq3(;ypI%i6S}o#1NWCGqQ0}F54gB-5yYMdqotYR)CS0 zr1bD2s&Wd9i*|~PbJP5hZ$10Wi(RBIy!Le&hg+bBG%<o&t*hpM=v5>Tm=8HDj)JSf zN;DPU;(wmKk?ooYd5r=(#MTdeU9ML0H!)}RO_i&u@zrk#h<+QT2BA@Ww;mc$8>k7S zv(T5sK~GW0b*1Clvi)v#-}?AHYKN`(!R-}{<a#<k%RR=?Zef-)e?Ol0Jp=`=OZ5N( zA)JrQr#{X-Vd5q@Utw61S5RW(bJc`QiVyQkqX+I0ePbeJg?12tP<ZKtZ+FGI6q}M* zXI<g^)eo)pG4)1GR?)+QKao{?%daxu``RrJtfGV~llAoL1Og>`f&Rs(8swaHEPR&; z{3mc`8(4x`+9VnA*f32S3bBO0lLcAKA~mj8^PI(k_QW0d(T|I6ri*yd(2ldlMO*n~ zDy<=f0*n4wIsM_l5-ojOnU+T;WdZEcK-iy8Ix3M;)qeI_LmeZNM3Y&SsyNF1C%f?# zAnazh>oO8!$hhf0&^EzJv)|@JjL-|9@p-K0@i`4=_lrViGUQ0}EJ|yeMUWU8Q)^ta zwY2)QjtNwCLbyVIR&z2PiVrSa#Eg<L>BO2+7&Ube1=Y#`6}c2Y=dj$U^a5r=qgb!Z zU0;|E1SK6%Jml}X`%-+`JmxzyvPpNp9K4Gjz|WU0%go$aVTKle$?tfk5o>;c?OGZ~ z^L!+&dRvbSB3D8YzS9swS-Mz>BmK>@aIfMWM~aF1@HIjwk=lNQ*$GBwNRI~Rp-^Ts zTtd4@XSp5`bVpT_4@Yl1BkX;bW4ZvNS0soX1v8&gQq_fCWqTwmeO~ZzdM<dF47LV{ z*zC8q{<J}cvz!W;GlpgD$V(7})Fb>Hf|89qT^F;8?21{C$z9asl-;T)ZW)`tEpF>E ze3(2b*&j7_#Fk4|z%})1lCbYYwhxCHMJ<1al~!m6<o0UUg-yyhyOHwT8Zt_A(Bu^; z4T~S9A!BdYm0h+_OkA>@uEph1j=+rFxEww@W*wlfI7MD~u38Z;7~Pk|A>>Uv`=$_L z%<V%t`@RCsx*<p@vJ*~>Q_ekpOWT5o09_TCzCsmjo?S0&`q3;lPm{q^MK?=$%Vfcu zw!$Sa;bQsXY}uQs%9kRla~Uk8^HP(uo{ielT(-7OceyNxDe%4I-94-0yt1`pdSUU6 zr>-|!b!)UlKSlJdJLmkQfKd|V+MK>OuMuW;sbAx&)u%7)fTrh==1qddM-55nal#hN z-NUfLl>mZoBRZ1fwKVLTYKV=Z<>sH<@bGVdw`a+S9jV5!q8PE!Giuf&Y|+pNF1es^ z%_Ez6WbZEj%{K-tV37C5N?T{m&}7*XrfDIaRq5qIZC%*EC%N=5Tbv7`34dhb(z|yU zh1Er7UH7|p=ik!by}hDXAAOEDiZR$93MMu&mtHmP`grm=9<x0yy4?zYSi2rcQ?}y) zmLnA7MW>jy;zz{wJqzq~uQM-1@_6G#+2bImqxWY?rQDX{oO*7Xoc9aHUKjah`ECk= z(5Yif(QDM{TWAl?Zz#-OoKBx^cz#BH>=MGUOQGPG;8d0LNzl$w`dKmj+|oxc8?=s{ zDh#mL^n|ZMVBxFY7~_4&&7?Xn%u1#K*jeJuQ#igD#spz4FPd6dSL5{^3aSxKQfM3( zsbS~qp2LnCS;g9wBA8S4pHZzG;fB(0{!D`b0d(V<xp8)w>a3=SWZy!x*JU<DWxe}x zjvZJ(%cnJBu=JGXu{mk{ER0d(cC#f8R2ws|*?2<Zcr!@)+;8#m32mVw?Q?V_5P?3K zRAQM#?pVPfnoSrPLKw=}K*)Ef)g~YJ6T%tsexlw=i*Fa*Eg+(@v5{(zQNRaWIoY+! zDNlatw%VHiCB@p6EHU^;ld>tHGyl&qxQx%##!)#T?W|vyG*ZLd+N0fgRP00NdhjHr ze0ng?9v8^|B4M3jir`ZeH;1_51Ek*-q%3lv&(ds*zja8FG$eAaNj<Dd(aRTOG$f?4 z@&3i7LPoFjx=3zRqOic&<bm{HDjnxt9P`qoIw|Y7bKc`Lvf<p+w{6@LlyUTO;n3kT zvfbW7-jz0qCv}A1t&d6kQK%lH%uSBaz)Tvgn}Aaw#`M#(9WwdwDT6A#5YXo|F40Yc zZeI)CjJa&H{=~|aU8NUhP>**tYTU+4JxC?etA{v`&*NDV`HfW{|7miJ)neX4;n}%K zu|2BIKz`HU>t?HAu5R$rtw?pUO}&<W3bs->ff7vZ*mOTKSAKFCT>0@IFs!#;a8^Z) z_FQ;t2q-r^sOH3=j+`KPWk4yLrkojS$vZ2*b_)5Nx9oD?z+K6wVg2ebCimRK`y8{Z z?Ab+3XYU6kdZ~6V(<>Ioy?}^%O+7}wHFWv<oXxRmU*_VZG$Y${1x@}bU&V}sGG!;T z<WCj@_XUj(aGWv7zZ(=PtfAi<U_8bdtr&heX{`odY!S;+o6xqdeh043iq_eGO%;-6 zWQVPPDJ)nv$deGQkqzFFajn6q+!=4$v0&S4^~}1Q))9MJl4G3~CrzyXKH3&Fx*DkM zMPxo&6`Ar~Y#d2!oQdp6qG@8QO=UY|>(+de+CiG!?1${Q{$_%76ac0-=ss6DnQcK* zYWDHWFElcBC$SI{v<3JhaqS)V>l(66Z4Q`AJyo@Fgg2Z-vuqmgP&l~Q$BcssvYhJa z^LL}LU6oBg;D$Uf%RC)>;zIel{8{-?kbZru3h)0sS+?U4^(wy2@rv=m%>EuZgP%k4 zjLl!Q24Q8r3PFN0{tN7bwaGdU8XpDxzR#hy%1z>UVC~*=R}C;ywC^=nwEu0WX#c}d z@v6sI@k)?Y07)(D%sRgg`n}>bA0mzRF9L)`6s+DhpAs#vGGn1@Wc&hU55b){3jUBr zHFkHKoULftYFy1e-7oZS2TV>#_t8Y261w3B``%qt?)yYe5Q3<k*A;DOAdV-Qz&2?- zO8b^Cyu{u5q$hH%*{D5XYkzVfdj2YoFr0gw%&vU%_4-rQhU?;sfA#rEkK)^@4T^+{ zeVNIZso!NciU|(}|GDLawP5ZR1_f%o!=yXe{MQ|)s-xMz&*Pd?<ZTJWF-LY>J>1QE zTVtPKl|-xpQ$&!Uz~Ay+lWqHIY_fl;)E^q!4bVBAuNqRPwtd<^SSIJ1hNXZ+KSxrz zQrquKBq-Zf4h;!eyxwk%EX-+J3}1We^}Eadc(>?ky?TFS`!oskLABfO=yx~1MW+eX z_<uZEwDt(n&-}eYyz;+!fO}HE`@wcO$Oepa1rNvfbiR!_+{(7QzYRTIrQ|&Sd2slM zbkI+g1zvf1ZuSyUY|Hy5;PneGte`Bga&UHA_J<#xeK=>5)B?)ku1}u3_sr)ux>Q_S zWgrA}`&!n#U9N!n@>m%X1V^S3ht?CDAJkS<UlZF>4#C;CN}L3WTBQ0Q&omk`I0>A( z?-g>EvXH=xvWB(h6?ATN=WD5vtd})Qk8N3`*AbkGp0L^6At5`Do{CeuNq2Yc3v!1L z5Lf$maAz&2sV|QQM6&YPoRqI(0C42X=_(HVF*12f$9%vYD!e2b{IIUELm8GiEzSH! ze!9k$uX~!R)KrdSgxIC_!#>hxk*~X<zPVl(xxu(S8jJeU0ZCdBH;MMsxWkugvA zODz^LKLT~6;Ds<&uIhDGz}dTDv$pGVx3!a9QO7(<%2LggLTiTx;!5Sg*vS0DIF_!H z>y34{J`a#_fjwx%gNcgPzRu`fwRC*z-QGaEd-`Fh4bJs0D90Wk-9|$E2@Iu>H^3U5 zc)f^3;%&6&B>dq_^5K+rG5&Hv^8R#0nPjUdzQEr&&)+%TAO0^p;m(p`k-xct(9fuc zWK24Jr^a0`?$8ySPm>N`jgTfU61Ux5iogp~v#|hX?e^*b+anDqHTrK@bvo^}y9S@* z>kbc~Y>Y0T31hml9cj1<Ep<-95|2tVk5tUt&CJ?Wv})bLT*%ZCqm(3h3!{kExOo$U ziwN4ey%WQWNS?T%l><z0L$$K9$=TKAtkYrZBMM*(>cbxe@2s%wSaS{Igk8rR)Avk2 z=l=yn5$2p@+~H!?r5hP?ij^$Kb`$yq2|P07mI<T%bYiH=9UAPsE0jpr@r$qu>gnp2 zSjU<aO9jk3C#i_Pn6u5y#gsPlQxV7^^Pt9pc>BnghAdQ6iB~HU3b2BSpu=smu3N^8 zI_2(Hn})q+wF8GON#?vm(oENj_(wVBQiOq8y|4wsVQG5KOSbGQy{Xd#5Ez;v>$-&N z9|N*c4qBV!SyD=L)NGnG<DEl&f=P|Z<2p$#)xMrXF;9XyRT&ov+q{@g(;O7(udcfB z@gW?%L6Ey_Rs@q&jkU%t<~xhnQs#U^#1<lM$sLqNjQcd&GO9w~@l0)yb8ed{2mJu# zoHNt@lY^1pIP(ho2yv^LER+RSU##e@;>_t?Ey*~@!gC@gEVJh@8@SeG;@6stEm1ex zJpgH6-yiSET!#BF?=syHMZpHl0q6>YFP;zRz?}K6y1$6QDVyVT!w1xVnxI~cHftr& zg#Z}{SBUAKNQrb$8!)v05-O(VY-aZFn*sF;6$~xZciv3X3{!Bh7%A|eeae_5<*xcD zPp~;lNC&gj-fC&?kU3@(dFXq%d!&2e<x2Uc%}T!jzjDS-i?Ebu`makXTPHbJ?;VPH zW55USg3~A8HEo1G0Zrv7zuVD=6qpU(3b?+e1;?2MZh(70YCv5bKlWu=K0sa}@Q?11 z5ua<W)C-UPDp%Yi4m=O}ReSIRLrS`%Jbm>Nce~zT){$El6sWc8v{6Zb{OeccMf)DU zQL%P<?{F)m-PVqBNPXBvMNRNqDf~9~6!H%__SdupuM5c9)v-eCt9CtX2k_xEEg;?X zf~agmy~~>8HQ$b}_V?QKUeDGogc(VQ{m8aUhtK_B+?_Hb*G%LEQ4m^#?;hl|;J38V zlJk8f(!v8mQHLtW=(7vYp<z3*Vf0!@r@`S!0dA$`ovll{dk%i&^tyIeTc#hrc_xct z)y4^1u&ZCBvwSW|AQ_v_WLl>VErH8mm(vUylZ7Ga6uBJK40LWv=GdgTk&xj6xNOg$ zj>t0b8AyC^1a61@c1xXA&Kr@YbWibeWS{zwLdRylxRsXHY!bC-0%N2dB#{m})pq6( zgm{*nHHlXm>m}0PuctkT_giwr%7K5Ody6D)6(rW;Bwj50fBg(jqoAiab_ZBFn$H&w zC`mB(mHQ>}ZgZczuiQjOY@9H5)Z@{vG#A3yUNbWf9n_)4@yM5>SXC2qWDYCD6eLw3 zZcf#4Emof~Xic@Ibqu~P%c0O#&5N#QT?#kKXrtg&rJRwzWX=>bPk3han*KtJZM#Sr zpebRI#xJD}<{7^gtiuKoast!fiBy|3^C5b4^C1Sj^C1P;Rs$@9FZ`WwgH$^EURNZi zzk3Xao(awb5wR--of(=0Zy8wqG5{pav`{d-Q4Ao0cIKDdq$(If7Zu_Zw>jvx7^g1d zkk3?Mpk$JWR_z)0`%_VTJ-4uv%^5w2LyHxxP}=kq(phbQ+bNVC3oxtSmkJh4f$^ic z%l#u<*&Q;G<3cD~{?<o&9)nqRSxv0WVoarFe*1c>D7UYZ8K}^RTd=@vg)F{Lynv^6 zYCB{uc;-GSaiTwBOE=kXj!gUBi!R<Z?m|hpz^_OD%jo(H>ng2Z$dJB#;1T&hf0mlu zkbThnpws9`@N`A@{}x66?-S@mS%pDn)G*)yZ9!%Gw?!aXVnd$_4q-lKF%znkxNV2? zf^t?8RqDbYM+WXs;UX~$gr8du9r*8NMxWna9^N2!(HJAyB3&X80?#`+y{TqTCDAg= zl&Ik-(bK`FT@GQ;q}ie9NW!=~&}n)BYsLMniSE8KQ&OkFqy8Lzg_65^eN+^;@U465 zlP$!gA8$ReIZn%_^p{5Izcnm_D+@xn4cu$01tU}s=44TC>m;}q4HoeP<o>E4G#MYz z6AdNKPh{R2i_+;q7G#~87CKk-wO~~#N%nXX$d4V&U4qB9wi7&vH_-!-n`Fj%Hu-&e zXtaE~*HGP?QzBpLfJ0c=6vOoDI@wh))~n;qiMIIQoN%6RPvnFy7I4&SUQu+75HM!B z+QPBq@3zPIjXyi5buBnE2qxu^(^Owz{^L7uWp9LpfrK~*Q49&TLMlQ6AW5NDDde~w z1kXVZ0yLoce*?)(Y^}ZsJ6pKfnc2I7&YnR49TdVQuAmUJe~*i*jpBkKrvG?HGr<uj zQv*Uiw<&~KVZbSseN0vYJ3+ma?W^H<b))G;lKnyS$KZ?5b##najKu%2X?Oj`A0+Ds zn#a6uyH0KePyYN_`RyPCR&7KNi`FXA7K;VPhQ2I<J4j@{Y!(wf+CEKoH6Fp-MNK}= zOiuojnVx+5x+jJ<i~yY&7FCwdTKVf|1N(J1$$*AS?lhh9tk|$6)P&h{drG?`dcE59 zvo0O0C5EzglMbW_dDkS?l%A3+OUmGJ0P8l3>Z?i~Do_l6?SS7|$&_!vh%KXY&Nh>` zI$;yP_O!V`?7+=l#aL=7n0d}3&ajxL10Ke{Q6E*a-g9)7^`g$3=vgz50CpjL*~ibm zpuSL1{I}GGFIBFx_1vgNpR>xR)TXge-)xS>2K(+!HI;XCJ#K3gmDy>!WTQd=7`N|f zsu;|-|3=uj=3J5VWsJ}{EyXoyW%{sEp(^cC{1RG^Wd4G&)mES>b9(NojZ#CkpM9ee z4~plI(iT4B4X}cO!^AAE%QqxUh9{d8%Zy~furP;D28Zr+TQE<latm*Yp;Wc-rn@B3 zJfb>_)pKkQSOVc3Z)EZOmUZ6&GRGJfw&Uk!D2~YFn7-oPFrOPu4z`7>B8C^)@}|&| zMNG7TXCc>XiQI>*qxR(g#_X9Hg4CrX*zH-t%<Id+=?xO}$A^zbFxV3c9o>KF*#D8J z&a0dune<}SW2zNu_IO%?jE#Lj4NIt(z+{vtJ>o{}0>PR#>K@`-D6x76ob!p1GdwAL zCLte{27yoIVGPfVeWDHqQwZb6OXJ4dqhN4h<t2O+UR)_9;vC|9b{pLJ8DI~&jysQ3 z5sS8!uz|Z2PfGaqeUV)nYTNLW!x?>7QSHt`vEb+Ucp4R1!5^XQ_Rb*0XhEW9le(x# z-NXM3BL@_E8VVQ*K$AZjv_7Ey|C#)v4t91%_NM=L_UkEPfM~cEmYEo%4Z*OKm1w}# zwR-mvLRP<$a@bJXQSMdXt&g!5^Vyh}QN0ouDfU>3NU`65aE)R-(y)eM{v2()07565 zd~7>_?-TScUM{xs&=xp$z1ml>32b$Sj+0$~G%o`B-N0Zh7-G>}kJy-$;P8+tICIAf z8MyAb6DJ1aum+N|z5~qL4!<*XH)hySRzBSkk#xmq4Lk|s0&kITJ_3x!bo5W*uZ$1R zx={KPmKESK_}Gg)+_z>lNDw~#z%HS4_uv%Rpe2`z1V%w4_TU^r!F07R&7|<6$*2=i zf`GesUms}cJ+Z3vA|eYNl^uwkcK>v}PU5S7NfK47rB{~pnx!^h&1RaO=27t=u~@XP zY(O`M8}cI3`s8o0>Qu<|`?oV=&39gx<FDu#7Ub}vwGrzBp`9`dr;En)0RCz1YEsL@ z<5LsX#KN%rrt|gau!*h{i}^4{Oec*|4xN9z#!?o^+LNyC2<G4`fSSMc){PL>4(zZF z{(+K4OwMkuIk)NEvRcB=ZZ<DlvR@|HK*Z>MZI-BM?_RmN_;O*lSrD#SEXfDiVHJaJ z>^?2FkP?|IbvdSHApf@+P+J(Ka6p|l%tCFnA?Fdz<!lHE$Q*~$JYr)V8BH1|I#4;H zzxAPZjAQvjTk)MzOC;`)FX?yW3`~R2KBw2_0@IL(;20ydGgx|Z7UD1t%dIc`ebrRK zHO7CKU4?!r$v1n@wVplkr%!bM&nyGJ{1+Q5ZEtVp46?ibnrAhdaGt6oXzwdYe{&Bl zbYOouhByud*e2NZWlRtX_KUUjv!V*4-zJ2jjV2|S29rK~Z?ekHCGDPPXPlBJL`1ft zvGXoH+baFG4$9xQ^QN}a=Dpk2PD^Cudm0BGSoqjpe)l$ZHoi#s-}jcyd@9HGST!16 zK+fs6z}&3dZAAVxc%sRF(06hJb@fgb{iXhqHTuWlo|f-YV527!ioxKD)(dpB18@0* z4nxn>v4`dhlg&=!ftPPy_*=jf3GX%e>uMm*H3p1-6b{iNp8r(<!55xugxC2%gFQ0< z#y@t4`&#EUZsa|8N~rE$5*skSvA}x4>MGp-Liax21h~va=^scNoG^dXkX7y@VSP9W z?{c_*S4C5WQX|zJWziid+z+6ut{ipiH}1g1a#Am6!Z%z+#)4BnY)`ORf1*pOq;c#w zUsu2qsLVJFGG0$s|5Irkg-~gQQ^gIGM<M9eW#6-7g{!e_L4{ixlou*uvFVMnU24%G zR~fe;WLeFqJBeZ8bS5~g->K=AL_INKX0+&_m4M6GiHiCZ%}7qz#EAvntb3*hn3w-G z5=exlf;{#qx~YX1z>HOc94LSf`r`^iJ~rjej~0`Th@``4c*oJg$=-q?Y+?hLyg+|2 zIk?EWz&z>T^KrItT-#m}xO227$(l`VEO!X^O;<XY=)5F!`kUBwx^j7KSdU#Ab{E*$ zd~a1YK7S=DKE|`w$!@K0QP-~ci)-RbbUDQzo{D%&yXDPVm%eiN=W4;{Z&a=+jqV;2 zWfErh7S4Ip=~%9(QvPAr75)N%e34iSZ@@|%<yxaJE9x8^p5&zW^;iBHgii(6sqDEl zSw^ZvT^TjH)a}($XjZX|iRc;{2gb&q$+mg^jlu#2as?fV3@K<nDC}Q3r&@HB8`j-A z1Xc^S-QI2C57mo$ynB1g&z977q6J)-a4q+)zmb3P{n;|QrGeJjAP!Uqt|oV7smFic zN<esfW)q{Z)Eu@*{JHx4V0JxEXDyPb<XrRBHpMF2!~EGj#w7@2Giu%r^P96n#J$5U zO(QI3ysor?ttH3Erh{PRhsknR<li;EqJA+11IEC@Ifc~Pa7tW-`OwIlYZj%UVtkx& z&yd?QDyaL`Q<iojubW&cK&M!ps$qEQdJ{EV*uxKC#=VGmKts~ItlRGHRQ)17D~|{z zgZ&N8*131dk5&LMIc3M_XD<06@<W_`x;{r}PX%5vJ|2B6Np59d?i;0Ec4BQRUM?11 z?h#(@IpRzMqM~|Ocu=bwOtMeWcRW2zfBgja%BmTHb7V@kXDh}iV0#S@g$f<duZgBP ztYy0^n10^9nPq-Kpug#;Y;rI9eh8w!6!m22Fmcq`e94jE8%3*ip&WzS5SjNRwMgxr z!rt7qy>e{?yu4b}Fn;Y%4*c2?Bz*fJd3<|P{dzGBS@Be=v`A}lhLs`sdKHYSA#3@? z2oC(KpUOxiL*eZ80QQ6RBMn@=!XjOv0>)o4YJ5+}B1@Ga=v?O_vaI&4;t1KczjTG6 z8yfOWkyBBt0}`ENzMJke#`LWlnDyT`WI6Z)JK6Ve_}CF6zGt<?Oiei9m@Pe3FeDrl zD5h>5Bw7?l(ho6#FN+tAL2h+uMmznA6v=Lzr`1p?;&T%E0XTA9B5W5K36Z6&=R%f* zo!?LdB~BaW7=j@wUya?&CSrQ>o5y($<WMYbRgjvydk`E<{wj>i4}&n5WYh9tACkAh z&~hb)TewYZ{w6x#bUp@eb;?ph)o^ch%25N|aBstHsqJPO%0b9lLuBe^bIMku-9Uzq zoUH+)rHmxG3N*~!4|RH?7~|I(EB247l}J*f#z|O~sBWIci*8)`^?yo7V{0(Y?rZoH zK2u)z)63OU;aw)?ercb2mgiNVJa*)-V23xSKtD%xUl*DIDxQ&*`H?E~yl5F_ghY}z zO%nI-65_)Ahs^>Q@D#B>y6U;TSQUyJlhr$P>(T<)2*AUf3-dUk3ACVdYxscXfYGdE zf!Attik`S`V+{H_YI&5f=ucM8lxItiR^mP5vG3i4iC>L7J;9lu9UaYYS_>LoJg2R5 z8~J@`iyz#-owAT|eqoa5w!&3d;UZilC$ztpOX^!9SLvvYuUbmF2}8I38^KjVx^Ci| zdJ@kzKLpIJ4j37za#s?pW%`wa9gN4qUwASi)pcacUYyM-c79zff=TkNFYUNkfGP8q z05K~N#3~wELvT75bt(IcjBL<I#Pa7JOR=sFgT;C9qmK%So%)@v|1@hzoc}+By<>D> z0kbWbbj*%z+v>1m+qP}zBputfZQJhHwmLRDJ^AkaG4H;4GxM+3S!bOeXVt1zyQ=oy znWxVXe`h#%HrDgM+vu@jL={!#yHp&yIK`^{h;jRlx9=ae0LbeLezAtn7zZuT8Nm|i zyfhvfxW@L8BKa{wHSr7`(IYE0bq4pfFjnf@^pH6%_Qf0{Lj>PCcF7`9>E1MXr}w!c zM>TjS_K`8ZH+qB(2htH3I7SVRI`;N$?iB}<bkF_mhpkogo28W95%xriUSLgJ;j^LM z=a`_gXY1&t1SoEF-qs|(-CMWC+u3oYQ8CA_!JQz3wZ{S0*~dw1qbX)j%jbJ_j3Un} z$gRpD@)msGFil6RYlG0=L1Z)qqQA6UnibbZOuT=#IIdBcWG30h+IX9KD||V6ZBT9u zHt>;bRJH|2PBKC3NP8jHkY>`b49bgYP5`WHD!HK3z?sg!_*yKt%@`i&TaJcfrOAJI ze~~EToiS1lOkldQi>6L9#b0=C5yNXq<es>7mKy{QbAlh+nB}=Yv&q;|gWdNStn7EW z!xerzpkYugPE%`p>`|{y@CGVC=B1!(LK!e-rtpA>ud!`_+!0X54HM<aE&fCpXQ%4Z zlIiEZ1FA=D7I6|)iBn;2^^A=*I&rQG>#J@{G!UX&hlg%MR&PF4(l*^$AJes;K<^aW zX)wEDcz-M&CujS2+(=sda5)mIY1@V_SPd%vH8#lC<mOe_c9Cq-jfNJ=uG@r^{g*09 z@UZa@mG%P}EZr)9*QpWdD#B1lyj&jkYa95&F2H`AVcSr2oWXE1(XZl@)`aI1)I~Fd zJEp!%kqK*uJxFVE`)Oal{F^HxMGy^f!1I`YQQOLd$+J3#bxBRy)lSha{*nLp6WS4j zrfH^`whKyP49Sd^aam2K({Q~r<8&KTi=Yg@o1FpIgB2`>MREB0?vR;OKYde{n@V3O zI&j)YV^h3fEsn}COz|@JD?6y;w}uvWwi*P*o^cZ!ZnTyiU39Bkqz*&lws<K`l<0K8 zCBk8ta~eeE0ALrS9UwZ}{@Vw=y>>9=z~x4pO1*btm7Kf1K{<_NdS<JlDY0n})yduV zh3uN)7Ek(iak43Iik3UTNGb}>sg5tu0kFg|G>oW2%1sFRUId*<mM@KhX!HjwOM0G5 zms(Ce;=Z3QYhh$phZpfgSd*(XwqNrH;>n;Umw8mbCNts*sU{aAUSOjS-n6)KRwh+1 zwkq8ZdP-(zB+TgkbIAjGxm%1<55h^5^D1U8%xKi}Te_^q(On%b#FGO}u9L5C033+s zK!kJjVH4`f0O;?jJQ~@;hSIW&54fE??szv!`f{G}bH4CMD1`*UQ-T9IM2Ue!<6#cQ zK&yVN`s@UiKx4(JnY@hL=!l#<%1{CaX>wmJj~h7o{y*;Yb%*>2tvmZPkFV7=EIULP zTJp*{eRphCrMsTrjv#BkHaN*qfHs|@^t^*Kf{tp<5WTTZ^ANrKU^SBitu0DxgkvRn z4Fy8$NME&T6@^`PP?db(B2FB)s?Z<uB4UJji=P=;9N}w3%(x3oQ;=qf&K4EYWFwg& zmds4m)A{Y|WM<AdW|0Aw?(5_ul+5^+Y1NQsnG3|s8|26HY*9OX7Y@ggz;C(mb~*&- z_JH<*a>?He;q5F4g}7;3(>~!a5;EpE6cz?gTyXOXd65Ar3#-zuxq)$h(gIfHg@n=k zfSo{wub;MnmXBXCp|+1-GoiMxA7%T;V%)Wp)mXW*@wND@`EN8Q45<pIK&x`W=7Bsd zV}&Igm1qjpM#&{3C0pilzyl}rhiuM|XfwIQnT3~Zh`-RUIsUs^19U>?WxsYe_DUvO z(hrHfkgkfKkFdR3^+rhc{Kb<Bbrf8Qy%edh`i&eRrH7<7GK_^)<)UDfDyuH!4yX(N zvUT)}CI)*=lXUrU=ZtFB6Sz4hwyAE)m@e;>|FiQ#{S-_C_Vqnf0jq!b^CEImjm2!< zQVU92hEmSOO$A9D?8Q?OmYBs&Kd7t8is}0*|5tzN|BQ@wwT)I#zEE?+7iy;aU+~QT z8P2%b8yo)5vC)5kv#@lkE`R|Cq1Z=Ry;?J|&jTq`g$^%CI;4gofW@jyu|dAEX5uEb zKXa$Zobdwop*X^$i&DUx%xFHn$@jdS?(FXO`St|XN6PEM6dn<Xt5LtVTOwO!r7`vE zB+ox$ZIi`eV*nqn5<XF=T%@@v!emUrir|j)YE&o04?~+$vI2Ej7^ok0O*JHu@Zw|m zt`u@6v$djl!C9!1xNIkyf7nqQ1d-csWnu*BE-sai!z`~XSRVUNYRiIx=D{;bLLiK^ zw`RakUZQ(cl}6>LXyT2+H7!2eXT){3cyI|!78_$33l`hFEUGH0yiCTBG$DT5WK7li z9sX6!i3lrVN+KflFQ6x0#&JC~z?A^byM02#@}6j*=T6!Y)JPh8YQ5ab-Edu=;Kfs2 zRQs(SKV7R4piz%zWWcC5*{g~4Zo3{BPKtzroHmSLk*ltx9p1tHr-R0PX+!`APixKt zJ$3D}+L<kkY!PyYJ<?bA0YqY_h=q-T5rC7((#kJecsMVu1HFD-`}ftCZ|7$3x|QAU z6`w|MD~6vAMBmM1h)1Hx2a~VlxrBY-Pr~ET2f0}FQ1lA6v=75TO|P+jzem)AI&F&| zLSa4k6y6+TErXyE;P!no(Yd5M5F7**4NzKrVw1yDeL|>H{V_XoNRBakm=vZnj#(D9 zQY*mZg2TnM0J>jXK#y(p0{`OvPry9)&|E+FWqV$L@&7-QRIzvXU(m9r&wtRe+KP7q zff&YWKSdB}OguzPG^lVNQV@NgD1RMZ7!iVR3ZWR0bOagD$(1iFL}j~PR3ytdpUIXl zzK((#7$Ee8m(P5Q&Am$8Gq){;gw1ktj)aBW?kfMKZDnWweacMVTB8*B97w+h`7><8 zL)SJM;C*Wb!ztZ16M*0W7um|~Ah12MU~hhlh;eOl%Rm{k3krpJuqD?8b$zt`uF<s< zAm!sf4CM)UEXF|H5?}F|j1F=g=QR_WZHarlC|>zS4U^LGPw%7Jd`R-d94KPrlEY7M z5g#xwBesWMX~N_hB{|C+bVW_E@Lz`XY4TjYVypS7iTxZo*<><J(l<E5G;JDtNx(gh zx}&8dFuzsJ@e>lOjH+!f_f8qziJPN1$h6a7J+KEREmCjsIc?H!pUHgmf5N)8xV~__ zLD*#91~IOY=CO?$X*DoclqhLQ<&hxCKP{HUdsI>2MK8ul>epgOw|*Hfrc{m5Y0(>4 zT0gg<NG102&0-Wd7BV6c`TUD!yCU*Ae^V$GpxS+C{erfy?l|||R@_~g&yEmJWq++? z^|=9*Y8VkE%Yh~+Qa)z(t<=S|k(aZO-&_?VURJ-(zD{c!z3A!dt={J)Re#!)W)E~) zs=<{kv4Q@^2dk@O#E@$gKPSbFVyM2PwwYr}{(0DdQ@Dl`>vB$K%S@lV+yP<2x9qiw zc|{c_U?fpl98ulVhk!R}4O=@-F}hqU_Ra;2!SSgo>lj%^xhpT0K#?$4H`lgGOS@I* zD$YxAt8Fj@B<JYZSC)iaAr1{%xm11`8<YjugqL^Y%pBz<Kq16ykaepO#hWi2<F|KQ zCKh=|%+;(*b<KB@qIF_>x*=2HUyuJZ8p;giWz1gscNx|Vetog!NWV1Ds*p#fFLnk{ zq&Hh+T&ev^)gOt7Gx>XU7V3$k+i|mv!EOjXn0^u2JggP1Z2wf(&OX2fPE+c2h40Ny zKbMY|_m^x_#GJL_$<4JAow{Q;-a)jxk78yL&R}T>T4`QcD`~n7FJ^SC+=1J?^XPDt zfIYoXKc!GJqp*B(jxIk^beJLM@I?klt0d=uVV8Z4#~rKu%~wV!yheC_o;^Rew|)RU z@}6L5D;#vsV%)XW996!X{1LQWwF~8;H1Hh)Lk6*$D2kAJHJVWRCHz<JzBz15o^U_H zYJ~x=ok|@(r=*8e|I95MMDI{7*@srY{Y}%aoqcy$zgPjPFyfkB3^Mc&<i9AuXHgzL zpm?MHAv>^x{sGpcNce`{RR5OCYjdOr7YPzMMfb&hmGDsO2L#RM*qOJI+Ww%CFY3w4 z@|HhYz~&6zo3!&2$d^)Wg=5fn^K55l*bRze=9C-*3>W4tjI+{g$p;hpVN|PO>I!%J zF!W+rsc4`V#t}tLuGZleJ_bOCEVO8v2L7yVd76l%rCLyM(!j*P$O1htz2&=wsuC$& zxGrUKvo%pQvNEx=ip1WckzvKw5@bTQvBaN(+mm37kiuK6?11!~`qAEM>sXBAQFk{` zPKcD)YS6dV0kV~d=R>FTS|4Y2hiD8jeU@@Wh5qJpYQM9ybX;{Qgb+ZreFskTfPLgr zm9%~_sDS;OVDVIb$e<RDYJE|Qn-Nt5yPsTtfo!}AYWQhi5Y*i2LBr6fxOb2k{zBf{ zDsP3>L<Ndp+r0uVv(|_h#C7=$ku~la+GpK;v=4gpMcps=IGls-ZmTFOl{|-X_7r=@ z4?csuIR<t4{wUZ333lMg)txdN4Y>eivu0%kIfpjAwD8BW{I%1FVr?l?q@p&7(hqj& z4l}V!tX^;W>#OnShm*-%VXmM=KUf8rJ|FodvRpbFu4tVnc6Qg7KtTcwMgG9gWeNZ) zwq#+nvv+Byp1Fy2c5{AHGLimsTGm!pMcoG<_Ys`|W0-C;Aq~h6OW%iu2yJV_H*v&_ z1f#n$apZ~IsLP#Rrh0`IHLO)boh78MNczJtA;Bw&*|)~SogjjJNf<Wz{6r3(gXPT* z?=KU-C6XR(RUQ##x>uX&2=}Ueek5zwLf6$o<K7YvYsHkXtR`KM6)~Umf!tP6x7DHb zM&A<PhfZ@o@dC7ilx^8Gffm{_ud5Z>vaY+??H#A4_z6Ik+Y3=fpUo{jK3thS65}kO z!naMl22koEU#y0!#|KLlLM-0p8YAtK$zU4r%aH~!ByjlFL|zP@a$KY$Q&{8MMY>}x z!`0!fJY!qe*6U-_UI=|}hI-K@qZQ*4>NO#^I`PKT^#ou!>>rtmh{v{3CXp_-S<aF0 zwpliivSJhBHK7<NEr<l~FbZ;qWQrtGM|j_8W?hujAcqL}m^L?lbvLDW<Do@vAJmy# za1n8cG_rXzh&ZRmJtoBTNWh2IQbvbsl|D0%Mal^?@5RFq^lDKmW-w1CIH2qiWMF=n z8v8$@QUfKb3|i3Be&F*=C5~S{ram~LW;VF49V(RGxcOJ5@LiA#r&=j{h`zO+*hv?9 zM<aR^zYAs`6f!BQ;T@3|$ed<|a^)dI%`Jv*cZ@94e8K4l)A-?6UJEVQII&E!s+UgA z>5sjv!<&bk(C#awt6VE>Ok0L26@9?vqyx8I34x{v<J6ptzaTb)X^t23vs8~>B$JYy ziiVDg!Hx&6httH1yN?>-oCfGuBiuOleSC*~-7d4=7xH`L-KV72Dg>D!TnU3)(P&*( zxSF)#-DO>nA*m{b+@1JsFb%&<3o$vu|KPUM?x_iSrr*<5&GefUtcy4QzS*`fYV<&2 zodO1_G}(fbuBmm1DF_b6(uy#YUswYeMy!9#f_r-Nk7+`hXzGn^r3Q2j`ggGsns^GP z@Fmvq#(j5cDMmW1ktDjFfbuLZjyqT<iy{0iRKrOW3CSFazmtMl>kY4`h*29RrwMtj zfMFjUU`{xqDq?SksNw;i#0O_u8aVMRSpp1NZQ?o#Y*{cw^UMsOIL)wX&lviRUFadf zYi_DTg(@2U2(&(h%o0ALrsD;TwNp2(H@WDtG2#yt1+k<NJ{F`y{1dX&vDd6NSU1vF zfzPg!z+AO}kh%^(Xd1HT^m76ygf6h`dq`EF1~U8_WIapc1Gn!P&A3L#y2d(F$Tfho zWm0XEremL0g=@UOAiLZthQ)@%A?v?nP$y(=aPh|-nHjE<ucJeYQmquVlL0FMvYrAf z0csB}M5pKuO8%z<`Z%uu9Mwk_(f1>7xZ*Q4?hN6i6v{IazSuAg5jBiLvz=(pKbM)a zGPd~SD?;q13mr}>N}Yl0Q00GF`Er2o%J1+mJ58at_N$`dY2;GnbvZ?@nPp5l1vng0 z3tYwz3?|;WGfzEPm%e-(?`kd2x>=sc|Fc!};K#zx`(jXae6@<i|KC{Tzc(t$8gM?! zN9g|m+YBIcQ!fvpT7wE{+fqtG@F#fW12Rz@#xe$$3%q@zG;YRr=GJBs%ZXk`efzU^ z2dCWHpzSOyTF2hDvP(~ia_NRk=EkDz76s~7Qug(Bk>t7pO)ufj!KU-H`}RqZ`3{#a zWS<m8c-b&zc9Ty;F8@^O%+0y^>zVV%gJ+LVfIeV<JV}gfJ;%5kNkaAzX)%=ga%T#v z@3>n@B6Y*=_pif$kil!81KE^ozmGFJ=(F0$2X_ZqF?K`Ope=f^XEgO1j*G?!<1M=h z??3|#5k5k}AruY*?-pDx0&9ReUak}ZKJw;f1@I??<svP>`765nuVk_IV3r5sLt-fQ z78O8df1wWjh+-6Q-^J9)*5`J_ygJUvI$H6TSub#w58Syzzo7+vW)2f<R)?DuQ&Gk) z%B!I95|)u8O9Bs^&3k+pvl8k13;)!?B#v?elD*O7k-bg5D+n1XDa;u{9UbuKx*=ri zt+mn8v}g;>)F$+PsJBL#xFiQu=8G>)vjgx)q!t0kBR}B`Tfs9qnUae40*-%9hGcsS z^_Ljnf^7^LBtFWaSXg=U`;k}7n}aWf&99W$CO-xVu?ZH;u}>M?1hI85FeaXUI?(;0 z6inocuu2Pi`-plPvT))mKYySv<{13tXsK)=V$sW8Ar{>XADmNh`QQ|YK}=i{Q~*@W zXI-2rJaDv=mN+=i8rNSo5F9<D&6>H0O17pSbc@<^9ek*mujel(jv}d|syEHf?!(`Y zlGo|{j!(u)pTiCp<49=HF=~l4RH`Hn?aVxED-ZTJ1N{#3!VV!DYZKJz@#1kOmA(=@ zJRY=JuAvSKF0aa8G;Sy>8y7ZpXa}%v!<zPRyB4d@(MyEm#nM_8X|4(E56-@T8i+n- z=pJY@PA)mEP9)z}ry5C9qo*u+6>m4eNV2Zo85J;~%q4?kTZWtK$WL_h`TdZ)p!czZ z_?C{1Gk+LX!204`KbTPyClZ823T|{#J4w6?gGJ%W#$HANzlFw~{Mn-;rwA-5xTwaw z;@;(>5(>&!h&73iOD5q>7(@ooRbY*CmBuZ2$rH+pD<X!TvGtaegyiSdQh;e{CAw;k zFeW+m!=J(;(mWr=Hn7d#*-+5Fn8uqIe99+Q|2}psCO?^nH$e~eVC-k7q+Cc+ca$1k z3bIQ^t5=o&V`%D|WWK$qg9u2sC7=|nQtr`P#ODbm#`zQHquZi*)#(bMW{}(TtHEsH z)uQU6v;<5n4up?W>>2Ws>~VS|?cGJGo|lQAet7>}Vb8_{=p~c;R)!<N*D?j}(t<{B zwL{vm8w}`5Tqf!ttOI)LP+ALWi*o+32dClLD@8HTC!`sTq9XEA?*Z;Fd?#X2HLD}% zLsp#M&lG02YSNp+x2~S*j`DwxXMa-cefN;+TR`~?6Oepiefn_|71X<nkMao{k?o0k z(-p)u`VTkf=%~D;_D{wW_m0=v7X`<C>FQjKp*IKjNf9dNKQHwj5Ps8(yCKm_xRL*e z%zu+LN%04yJ+M!(0GfMcTNNWm(bOBlE+K|?4FVW0#y!=p5!q*bn16%kDBHU_+0q{{ z!VBYZC>$hIyT3%(vn8wiM@K2Z$!)~K3`e-!h}}(nI*N^RQA!AypXbqUQ6$tB^7py@ zEJ~na66Pf3lFNLJbf>myM-#fF>Gy|?gvh4DO2{%i$WO>A0Qt3Tc38VLQv^boi2Y@Q zgjBoEG1&Cz2Jg)EYO8nE)~p1B=1gXp^Z3qWNZDeWO=R|t(<CO7C89aZ<4b%`*j&<N zR1sw(UwNFlGyybOtggaCp)x6P)PS5e$3D86q9oa5&-mQQ&Bfm;Qx5J_S5%T`SGyw) z*4|s#pT!j=K*m;#o?K^_#vVDzWxmiKP)ugD?xTVM($NBB_X_1SiZs@iLH30(bOrCi zjuI#7cz2W`z7njDeos%WeX4KUkgQ&u7Ah$+mXZ*m1IeXxAx)C<Bor6{VK!nK>L$YD zXk%G+UDK=)d4FVEPlhqiirZBOXugrvkm0gLxHh}S0~#i)Rx`t1x2KTP2MugFGA?s% zCUVZ_t_w4+m`8C7Q;i>~^#4kTPI#=V=1WAyiWtt6-m1bZ7XEz4;zebg#GJl-OybRX z@|teqtr-UMMJsq9*EaY%kvXO-3QMr9k~zDkfF(cPv9S}JHmJo@`^9&QHQv!oK8t39 z$aYn34cOSgG_@Sd0Ob|9vI}Glkl3O5ni0@dVQ*N@NTy<=RKW_Z2}(;7H;-(3R&SsI z;VK0}8Ozy4R9!zdn5aoA#kEj9je3IH&8q$~h^@L2)f@`i&m<-pYqE{iYgjKTEJIhN z(Od<Y6vkl@es?}KKht^(r|Ik8K*`4I!cOOT1W4CK`6ttOqFImSgk#$mnuOm~-nwNI z5vqAkk4v*}bGx<^srbc0zjY<6b;4wYXE#Y<d~?gscB9-5*}`DHFNgyN_392pf`!_{ z3Te70qd+Jy071M?AG9F+#j2vg6Er^6!%Ax#OdB~7Ibj=GKcueLWYgR|py&Hd88N@$ z4nU$nC^RXpd?3?t&)6+*S;viBjfeZL_EhMCWy(`2Ux>#QK6%CKw3h;khF6>lCu_M< zn6%t!Ft}wB(&(^9AT!#X7W}?D@>e4mN+A>qxlh<6DOB>WxWwnXWb7!>!cFlW%&Eux z;<B`knjyz_44+A`rb(CecvsYm4e=&o1MvMUlf^OZlQC$w!++8c6A3ww3DmnBV}XcS z{^x-;(sbdeY7)jS1;27hR1cTw!?)`>MGSyuavws(@JE8!3fd9;J>1!4#)zaQ#u;nS zD&MbFi9@_UIQ~C-G+NRzHkuC$swgQFU;WL;O=j}JXJUCTiR74BOGNnbO1T)ARDn{E z-7J!!v(+$=g4Z)q5})vCHoZ~_y*#Lw)IioZ=2TucC<cLG*bu)Y$51bMgEvd1@ds+w zUiIw=(H~*xcyn(_(X#NOzr11#6FA)PSVNg3h3ub*A|R>KVh3&O^Czi)Q}-cqn|w2a ziLmrd|8|6TyoX-Z@9Fel?E_hK;|FBpy+d01fi5yTg0&}VAG1iK^Y3T4aY+y_2u6fV z9a3eTph~*Gsm9O$b^LZGKw<mbah%iFr2_9QZ`@Pd4}t-2xCcWAi!dmhpw3QD@XH-* z$whDTqLLwSh6DT`la-rSXx#%@N_ER?hy#QneQjN|Qt;Yd(OBk)$Zx~pG9v)R#|A~M zZd_xs#DJByA44rd?U+SX(@rwQ=?<^zXM#~iJ}m!*I3ab%uRN$5IWJ?@+g`-$f-2}t z`vyyLL>&Q?iS##Sa7P!mljT_Vdkc#F=O}PXQ<@A(#gkKq9Lcon;de1(rJJoBsQbTd zhIA!yu_w`_pt>+c`mKbMfv*6lj|1IIfe-KZ7!SS;ycX$nQj3fOVbZgPj5^P#wQklB zg|yuD2q7LtZAFXGP){_J+{~N!`hAQ;)aSxiyuJ|wC2qiGNP|{sP5(-ZIV>5h?G9WG zVZVZ<ieP97`&^UU6r-FobK0)McT?p6RYUxp#fV-JtP}F1wzF9!F&ii=?KaefK(EpQ zUzRP^(u+hmFJa|5K-LY=4y%cJ@d?yxThi&Zt{MIJKkwG$+UzRxbH4`ghhNMZ-2X?@ z>t-t~BT2^m-|enz;>Ldt)kb>rwOe#sf2q`Lqc{6E<o&{Am`4I+GSJwQ;;?c<E;S&Z z=WSZi{}JuTsK^9`-QN8nH09;dm5P6>^ER9IJK;3%N&oxz<lG&kaSWHqfi)U$1U|}m zAa9NyzZHQj^Gp&9qr^q#yshwnLHN1r!K7_0ZY<iC$zsw^lj#Aim@;R1OBT86UN)4) zS9Iri8uGgdTWlhr)kba&Z`OY=Qg?BqB|Ga<cM63bDZSHZBd+xt(y2oiB{viQiJpo$ zs8Z85g}dq0rd}^)NYp*CsiAFEeHA>9PRr9$77~;%wT0e{Q(Gn`x5#25Mck@iduO$& zv3xIFPk}eLtCz-@3RYoudfmiet923zyLC%Dw_L6&9xMa2U#YFMe)}hj%DWh2afD8e zPcZ(*Q-Kwgp#R<GRz$2oS=O9_lv=ZAe=SD8Fh@<a?I_iCla)D86P1F6`zq7JKa`sw zJ#8>7+>?$e>SKo$H8TFAKAha7EI&qkVAfm2qotkxBF^w7t*LIG-DKCh=H^tp1uUe? zsr>;9(uon^dEaP}IJ2ckW7y(A2B*&~vgf08lpz1rX`<XBoPk)1Gli!miOQ4EG3OKv zoqKb>XE^0JI=*C;__NiRZ?~n-wCD_z`2Gx#oEJWxzhH}|i=dl`xeo9x5XAI4-KtKw zo~s;^@@E}WGU1B+Y)eb*h}=iY^nfzP)J4^gn|%WE@rhoIzHbSfY75=I<;}f=De|M( zr?%yyd*^8qHWto-K!)=d(P%b3Fp2FSnW=blt)2McMmB4}pP)tV`M}kqxo52h6R>-W zu&8{3aQ8INDbZ`eVj8WB>O@$=p^>U4D3k+=0+y4#>k8u-G?FKd!BVJg6;?HdNd+P2 zZGwQ9V77`D_3i)W)X?|oc*yvb-4$Q!rT@3=X8G^zj!xXLTlo3qV=*=UqiwN>{-KY! zsYKF$M8TpBgji@_3VB+Pi$<l}KiZg_AtoA+k{4ygKL|<47lZ(G8-f|gR1uLtC-VH4 zZtw19?r#3~@+1`%#B@{M7~!1Sq%NN1I-ze8r3Si<hK%wsBTklB8$|_X1D>bibs45w zFtAtgZ(gm_=(EbC>!Q>w;dfg`n|lKjD63&u%OGMW6>|yxBIp?yLS_-TVXzR5rRz2L z$dH4zsoOehDEw{La!PC91;hQj$1XJgr4Ey@U3W=TX%@#{M}xKZ7=Ia4mMWabdFEUP zEyZWL6~Z5Sy@sJ+y_Cd^`BVoKcH<g-Kd^bI#%CQe4RvMabgmwoOYCE+`+JN#`S_qe zevaTHx6mHe#CZ!nDHv(wD_id`;$%x^lB>I8A9kFq2`(KK#`75g3dfPH9iuJiEOgn- z%0lUe_cH;Fj4Zbs=??C6N{4X6qz607gkk43Sdf8lz+3|1172+Bf|24Q3VXM06Qbo$ zKyaEh8^fmgkwW-m+)$je9MY?Cz2c0~+>+xnL3DgBG4TwB#Z+}s*h9hy8FuYnL;#8W zd!0aFwvQ|zau^7_6yzdBHg6L@xkSJWyx@@!4B;gP&EqKtjsGZtN!EPuCW{+jVIYZ7 zwK2@J$*JfOZ;67nGI4D(Ot{=dr)y9&vGfi<qv$mbXA`nb*FwB=4ky7emr#bD(oG-H z&x`sQ<@9Z(+_aog7(*)Iv-z7HuipFrN)7_Pc9|vpne6&swjjG~ARyTP=TiFLsWznz z<D;XxByciivi<vzTy}xtQo*9s(xS1HT0u$)Rx)$8=}aiG#SJt|MlaEllGQN9u0Wya z*Z#MN2&!LH;Aon#EtXP|5wsBnWo?oBzepIU!T^QSXMyML^xtF)aem$to9;Wh(|jku zjNA45?F;VUXqZKQ%c?Z9*P!&uLb#8YzofZaV~YH9qp*1?=CvnxXV;;ju6b)J-^HU7 zKQ1?BTf4$KrDt0h<+;v9u~81munJ?UL;3UquM402vl8`t9Z2r+F}BdlgAvISCaQG) zEs*8Y5i!~ALot)Q?P*sNmoni~v;W7)X^e{U-L>X^p0|dvgx3zq32xITC3EKK?nl=w z9L99AQ9PY*PA0DDbe54*{mxWmB=HCer@n%W-II4)x^~c_lBQjHruNpR8oc0f7~AAM z$Yj$IPcEsab*hmlvuC^TiK?EKnEh$jIv{P^H8N@aYLvZlwps3+I(^>#z-Av;6OA=$ zf2+iAkHXJ+-Zd<J4#Hx8B+dK4t4}j*`ud@U=+T}TsB}KJ_AqO<eY~f4bzcB2Y3t2A z7G&zvI~yTE|0RoX=MUe$ImMihl$s6`z;t$$-6b7zm0}d@a`AYx@dCSdaj8dh2)KLP zdKJ6q6yNh}zFT>EmQJs<dG@9zd@y9!CYPKSd9==Y#-gowzjlB0_;|(dd`XS&;=1%G z>hV+g5+>|ZD5idwzKu6xF`2Z($l<4e+ukp>em&p@gif^6qn;#&qUaHz_8nQLlUDF! zAl;c{&%;K%&cX66iu^*cBh&WY0E{yWBPV)9QK4{hnV5(P1exjLIf6t>C2=aF8o4L; z%=((2?pXB0_;UtCUBpOm<DFZlG7rIZQsdB%23#sTU5$XHIFAM9$drhaf$Wfu8DdbO zcN)J=JYPdMdjl(4q}bb(L_xW+w}E*cKVI0wvO9%QzJz^E%ELqU-^YLeK*5(PfysE| zn;@&+&gKFt>%naadh$W^p9lz&+n-DU+r^4(*K+=fqfbAhBsb;<hDyc?F%U)1H&&Xi z5HrA7v_jJ3vt?LE;sdA!a>7Qwbsw+)ERa{gCH=+=s@B+{>&QB9Y;<-P4h^|a!O`k2 zFJ2~)1X4*WmSPPz?uS-_4WzoS=~}m@PvaGW&@L77HOqMa`-z2`1r||Xj*$;dWeZm9 zWr*3G7|6^T$HdBH!fgzH6lM3D`cww|7mpeQWYYBMhh(<>!mC@honZzFD^x*!P%**z z@5w|nJSW@tommvcr~QkggK;LcYb^K2aLW`?>1sobxZ<*vY#az12SDDypwA3vQ3<1& z4QZr?-!Vc$79<rW8e7;?T5KWX>e7s9u4!MuF!YM3g1Je^`F%=6zX=KlJM2;Dj`~N6 z!JSA!eBK%gtU(YISR%=fKd)OBEZ%C;jLPNg&4f}qJ1Bq9v#;5W)A(T+=(CEa66uqX zJQm(O&i@%Fi4JoG=>t5<iWJu=9iD;Mj1Y!gk8;jLNN|0YOjCIB48&Q4_)Mfz{RV8< z{SP(&{IE=r)k!)aC;L3pI}Jmb8yD^@EIF&f%sLF!c|kM(m}exrxzl^2m^ss7tTs}l zV@XhOFM&8VgE2STl*sGT%AO&|Xc(kU=l-gLvEjhdM~4nb4*_n2wpi4(;!UJt+)nL# zNi?(^6EwdxpHZN-;W|x6Gm<CH`z-)~=o{{AYKfJ33~Y1DL@mP5zFE~KL5OPfP?%ca z;s<>x0V)^|sN<u@Jx7pHco7r9MW7kX=0-xei400?g(Kz#Jn?#(tyP^&BsIct(_8pm z`cv$-f(a!HDqy+rirCd!x|2>bB5G}7lK2u<^<g&ilV)^{*H9887w1|!!^KOjH22Sk zW%g&@0%5)&&y<G62EoQRE%(s<ZxbKNth|ae^73Wo<*>XC2$|yZL9TNaLXRW6yamif zCRf8mmZtvO(-tfkP0Hauw2_slcaU2@IdLA4_-t;>UjVt1GfTg3Ek$S0?3Lo8%0MCE z4dg&RL2vTG3)nJr;Pt-XskvFfw>KZog)EpNDwYR=wvIWa^AF8HfzjtbAD)5N2k!x< zF?fhitRdZX)nK2e9RFB`v;-Ph0||GbT=@-dgdeaLy67AOZxkQLMW}Qb+nt<?D2vq` z4H1!i1Ayzr*S!8vk1)$CuC;hun-rbv((hv*gOI+FJ<(CVnyeo6ZRq=-Y3MQV1Q=Bj zT6KvxfdK)kG;$-9?Jr@Ogv>8ggwFSccAo_*y(TxQ!I`7*pRmBvZteKy{9MsN_)n7X z>{o6;_{1LPgA16P+Ht!_m5>o{{N9XJH~|TS0=?(Ql%~Vi*bl02I16X{qbvq$v+LZu z;>%nWJlc{jpFREZx8LDEiT{o5mcEWc{tE&kZmKlYoo#tHKUjxuA242SSHcp{W`a}o zK}vta3{++U<@dGlej$MEjs1&eATZf$ysq?Rr4=ec8^9*Kj)oXsrth`+;T`y|ZwC7V z5dTc<pScgT`)q{1`R0$qIb>SqDW-wS_ZB$~u)o-UxL5HT(Nz{^j~nP~kHJ5iU=2sX zTeAd?Yf#lw7Pi_HwesO{c!TYstz!p%7(+qqJ?)<nL-vmCu)GjDkbTJaC~NqP=jqSN zBV{;e5lrH`_^s^%x3rM|GVM5DRQ+UulLN@!Y5P;3fmsP-tgaH%j_5>j5n*Qw&yHgj zHr+{9w$@{2w%U}F<vY+uGb|U}<-F8WQ(@XT(BO44VcLDppWSfcIzSg=2K@6&rz}9V zFaMDKdG%B1mtL*@v~&oECM`!3bzZ*g(wAPv;%tfK0)kgUkMS(3l23%)3VSY)pDiZ9 z)`acTnxBox)x_GA4vBxT(!iUMypFw2H)TJo*n$7)R@8_M^_~W@juR<~qTa^eK#H;j z@od^J!)ei&ZY58>j;oa(_78iY1%3fJkY9@Pd>v1ic4eBasePSSw-Tw2Hr+~&d<a`B zc_`X`UNEr7U#N1~3cgyc($N}lP2b4zBsa8pT=x;S-o1{Xg3H_e(R4t@9XQvMC?-X9 zmuX(ag&M>6QW8bSgFNMOc|d_FlZ-l@9|uQXV05-Sq^U@ai85^)prAnxYE0@Yw!nRO zn(xQTD$g*^4l&+;7;$0!Ex9uW9hZ*FOLliTDNwG)gyT88pK79^0M7*2EA`44G}K-` zLvN>`ecjsa=O3l7Nt?~zH!1BdZ(j$!IbL?P2JsnANuj6zB^rPYL(<@0%waUbMT}P( zpO<YuOgW@$ckI+^XF1L-a?+*uqt$2=l(PL9mHE-3NxkN^Yh81F&Ecop^4YUCb5eZZ z1C(3Z4f5<kbiN3o)fgJE`090)X<_{Kpj@>78S&F5$V#yk{hSp;%Y_Cvv->CLqH%Wk z15X289JEB)RCXmj=OCCFd=;kaWR06>YGx-r$se2Lgv6I536&bVfn&L_Y&YcPg#F#B z$@z&0M|*CukYD@hs5sEYQQ&X4X<G5Ps@7%MZJK2?dA0=fD#0^=nyX<Ozui+A?lwA> z1^a8@oX0>&Z;_Gcw5eH??7jA`(lFPdh;4d!GQbhL*{#wO-YK!A6Xtk67Mk#NIqR@# zNic#_1NMrdtSfr;nJDKBxkSDuPHC3<52{7m#4yzoM)dQyz*3T_HT8{L0lE>CUvN2l zs}sqYcYCpusO=PRiC;a*VC_3Ozuc^QX{~0S{kdb)^P+yb;l&kcVy|yz#YHb2+WBTQ zr|Z;!v2iK+^t}jIte~$f;7^5Gwi)?e!*yZOFYOvmUtxB8BRdftpFDGKmK+Nh1L|<P zlYf{#t+V$+4{Y_+)EJi-*0dEP>N^+J579xVRk-w;l^<jP!SvG(F4!La1h0GkFX#;z zLS)#HjRNLdBpF}QB3Ao>kwjqehtGhz_pb?>uCk4;F55kmF8Qmqg0n(+E!wIy9^)*X zh;ddT^mDa~k1R|})ZWu^PI5=NcE48NQHp?mlFGUZ=;0%$a;8oQ26BRRYa00oye4jI z(l5v7av2-IrQiyoETl$5Y14byEWV?tpxdd+k+f8T5_8I}*hdF6Q<<RnVoDtJK1Eiy zI-4j4)rAO;Fqx`^H)<27xkj(UO!M4SI+KSx%}ls1Oe62D<a8%Es&D4=!h1yVTk)f( z6B&+WeXI;Mht(vHAn9r=4HFTKE`v>K;N@LnU9~L$dhErvjQVZDfB`#TAM%}UWiE@A z_(w2;l0Mi)?e{YY{l!!%l=*Q3%bH-ar0kSI)e@K=?!({e_<E(-<oN5T-_5M$c@6i& zuEQu$%PTMHbdV_vs2^Q3I*+9DQ0wd!%%L`_XlR_oHg!5uobQK4wbWTGktaqv@PFut zlN$5_^aeW=7_OAUdjw5Ql`4u|hVN}Pu1(h1&E{6v#OA))E<*`lr|`q&+h*!H6Sfas zFo^is*~Q4Nqwfjf1X<(xMfooc@ttaI(NlM(Usm!GEOQMUSr@+v+94XgrSJ3<KaLj6 zi4^umP1YgAl~8gQNIH+obBLo^Ix%5K_D6I8d4DQ}_Ue4fYJ)$k(`GqcZYMh2Vm4#w zem!HyE7fecr?nhwn}c~jyH+C)xQZ0=Bq%&)vMx>Jm=yC&5+hCfA1#mF3Y?nBCP}}- z_@U!U3-v^}hM<@w)=1*;6K^-vOglvvuDS3Hhe2mDV78UX3jKxbKt6)HRg0xONLP0S z!d?Vb*xrz&QmwKCIrG`7Kpza>pmPY?z3VCN8IY#LH_%m=*qeommIx@I7V1g<*_x=J zpWsuZip3HT`zk*YJ8^cz#F;0~xQ2B_dqp2m9et0VJAG;oEz6r@l?aaBuGp7c&eF=& z^Nx!^oINB}=^8)T6J|RilBIX`3N0%F=DdTXIA6P<EY^uLK0!Pz3oxvTg`B9iG5w86 z1kO~%lr0dm<(zM8f8mtDl3Fvn@r$j@i6Z&Yi;17vke}%Nwg!!kouiMtBl+N$%O$EE z6K|cT3|G12=+NDB4Bkuwr7zzS%pKt^_YJusqg31Y={`rKV3U{Z!np;emr<4u@Jr`{ z8CjQ)=#ff6xG?$>SZ{`|B_DGA1%Gilcj=XT{nHDT#eK%@>EacqQ95vNxO4_m!Y}fD zgXe{xX>X`8-zv;Ub+{D21RF<H^}DcL!yooR7qJRG9vRllw=uh1NV#x~fZv{ddGRHI z8%|pAS<~ucDw=RbiWU-uvvFeiK)+Z&DW)Zd_4B^2gQmJ~ni(@fJfNyNWtf4$k5+5S zQ5$NB7+C65V#v)Jc-~&_^@wS?Em(mCDOTvo2v#71`EAfFLq@thC`E8xjXQGv^xMV$ zaFfpXZY!7MWL*qm0I><*sX&#<&Dc^w_1gI@2Tb8pxY>JFPZu%DG;32Qu)e%lHG)$) zm!Uu#VRi1;Gym6T&Uer}%q~^v`W#Wo*I3Xtx)vnk>OQ3-Z~Gn>F4+sSWHHmGxkkC9 z1?7k)L#&c`c37dv)djJ-l)MSq$fiJUT}rSc2*srw<qd}lD@X#ao3Y#R{S?{etAVC| z+@f@Ex2k-eWA;kAQ;}5x){Cw5MW&*gQ|43idcJLXN=@C@+hr>`gH_HHF)kQua0cwC z=WPZ{wPzQ-mKsf~pILJZ$yJpXmafbPhh%wm5`}K7y=D!r>b)1g*gU$>xg@vwc#d9y z(Ch1kTlX{h*N5mF%tOk5T=gsdbPFsd!nz7*$(((%oqf_f^(J%yD8X^J<m`u?af8w0 zCj0+5Io!Cug?uaIM6k5SncrqB>b8{dUWj{IkJ~QwDRaw8xvx}Esny}%ICer)Ht}i= z9vir=BDGZ<bh(nO9?J0Y$Ji1Tb-VTqEuUi{6c2e7>E8$GYs0%{){=09&MabBmjqp} z2BB)^l8)SxjitQ@+U0{G{Sd&q?{sG|j&$HLWq4JGWeee#E2+8>&h_Gv^>{f<V|}9Q z!av}3!V&Gzc^j(j>rE{RNKQ5eN^ESK)3mBJG8Rs%en?0w$Q8pSHM6Uim!ue;-h`3X z$CWA7d`eZd$3~o5-Oc&+`O+^Qakq^;|Gcg(MZam$MiLJOt{dsM=fiAdX$REaYLtSw zKTzKt4E)*~zLyS63SI=}$(Mj14HJ=<)ZIeUZ@7eM5esF8IVct?2T4j&upEWkmtz+k z1+ulzsUA7w7pYR0I>#|F1E^h-8Ny9!kLxVQ#PDi?nQrgx1AaA4x%f|wOp_w~L3|zI z5y&?uL}+S&a1S^5&}*DKGg=omOHGLTDoCwEqEngO12)zxpD79M!kCQ=X(MtT9E+Wn z@J?xIofh-&OQ;87B37s{STWDMKly})A=3Hmkxi%?tH}`&-%p!lN;$wq!^7k;cVCU) z!mC-Nf9fnws;csdV8*I-bP9gtz`ME?Yr6Dv1F$#%0oB%wtro3~*3Ia79c?!LlBy;f zTypRZoFRd7ja`skeu3C&y;>aAeI#0}=L`s0-?;`jMyHzN)L_#mgE%$;F5-gNh{M(o z3As_EZqaqTP|HNfmCjegbtnH8f+A_V{8wWh237opig}ZevxX)@-?VLR5m3$YcR8;g z)^~pZFtjR&;J`)YXH5tQ__#&0aSFB7UdWI2n~h&Pl#x@!>n)CPSBgo@o$g_8AyjN4 z8&GE@%+WlMXHs3E%G89vm6UqAz!qy8I(bJ)?Pz-gd3Yq^1y5^GvkPVwyNn<Di0C21 zCzDOYsh3ZaICJlrhS}G{>OEwk*VfU#1UZv{O_i>Dq)RWCxLlb+rzE#pH#(OTt_6Dc z+16sceHmhsC<tHAwX{tu<9(HM@P5sN_3KQNU1)8FM;bFqpQRKobgX%cLgFPp(xo8z zsa5U$O#u=6bG)LF-#_*g3!f3^(6N*=1_Xphth0DM3dHb&Wm2MqJrQ#k?y$$iwh@m3 zoiVlTe8s}6g;@Mz&uF_*A^C79M)z6xTx~1z9Ei{}DM&<hSG@z!DRa>@UoPy4G5Fa6 zST=bawp#z=sc)A9@W0LW)T{Tu`KdN6|03@2iknb+bk7lC*xTFU!h@zdPcGaDlS#`t zvGl>Y_)VQM=DfEGUH{{Ie-ntJ1&2!;fGXF98AA?xT?g0FN{I18q&LX)6ZUMfcF)HT zjPhRFE5=WJrqCz&Q-bnTUI%(T@it2g7={Io7tSDZ#DP7lH{6xE$eX2qyGIZ5+cJ!| z<PVHj#lXFr7lL{Ife(K7H0J{D2YOa{j?uBX#sMZ*tiEBYT3-f7diS~tM^ihXQ^daO ziHLb{qg6q_XQ{>d-jwX3)0t*&m3l`<KjdfYZ*J|~F4LKDVsU-objiVceb~O<ty7MF zL~{vKv#htL1?8MQIDU^0vAIjN?~GU6HM@5p8uLTvtn83N93l6bE0s|J=bcM7xT<kc za|kB9w6alx;4rIbnnzB83zrMvi`f3#cdFhu(J$Y>C7lU($5*s|=fTKVeMel{LdZeP z9{U775b)sD{O*{eWZM<_-G93AaF$_sTjHmS6){z`)6+Ug)}nnq6oW0}mV9riKwOP( zH1mw1z~(-=GCvOcr1;5?=>mZ{xSJ-RejdvESRYxK+(1W^HFg1;n-2hp=M#=bEvw}+ zx0Fl89DFjAo@vs2vMJ71rw$fla#k#+@Hn;fi@Q+AXu4^Bf8aMVles>5DV74KtIh@L zmdlP|K^F+h_xGmzFlE`Kd~p5!aQ6RDx^2YcagoV~z8x1AywH#lt!(}ZIwrc*cN(Ej zNJm)c98#A?BODiB%>UwF)vYVh?3=0#*d7M{nL0{76V`-@;r%z@CPkEyifDF!x<j+= z6<5~-OT`Wmnc4TG6Be{?eRdCkiH+x8L+f^ykNpe76^}a)Io+C|>^&jQc*<+okLL+k zs9$wN2h$g45#gf$^Prcna3}_A@YnXZ`T>6YUtL4;Cp>6I|2!1H_~6KBzm$df))c(% zx!#ThS*m2R4#At4W?t^pLNlqNSvaAkI<0P+NHe2;6kO3IkxZ-FE}TsG_kY`m3gpX0 zt$q#fS-<3j2><sXzNDy~o28Sz-G6j!{(F>fD<}9Ldx0uoBv}=_keT8Qteq*R&0*g^ z93~tF>w3_x6EXdgc<^F>gZBl5&@_!)?Ad^@qk~p;>F?v;TNs0=d$hl3U_y5l(d3&u zQ!5?I6IRrVEFPr8CaVP)$>7R&7kme@OmY-Kw2lFoHtowOC7xHFo#Z<IFdu{@s%ekK z(wN37o$f#Mj<@7$Z8q)KWAr}_{?O|lvHCGZU)?!UG~dcer7&611!<qa6(0!DG>SDt z1V#2PmM	&XOZ>ThK^Ci%abP<Pv-KKl`4u%SN+*yUN400ro#`BU|G-!OLL(OYVnf z1e_e=zaryFa4*pRM5(*K!T0{_(a;olGT47bI&<I%V8~}*YY`ZFMpKlp?Xu{XXc|%K z90UX^An0Z*Xky}I>g@dAe=%4^R~c6o%TJDfwGIJ^)F9H|x;*2%33Zc7aUN)SUch`n ze}I?smqJ@h=9Y(>uwmED2oDgj&2<pG?U2vPt&$Gf?KHBp|NW-okuQ^cyr?X3e2tIr z`rdW>aHh03=UZAoA3o!LNJu8k@6)i|6d<IPtt-2ahr7d}5|`VIeRKKjN2wuyfV(T% zZBEQL3<lyDQT%ITNd$K`pT4}?EnRBfSYjt2ApgAONaK@NnAaH~?i@zac5#Og)}M14 z((2YiaY{_J(UQd6Vrgw{Yg**hiF)<M5JbS#)nslz;yxxN#y89i{%e&l$Tm6K2<+Y8 z6{_mNRj|P;>=}<+!-p9Cz24WG)UH_m7#wK7vY_Y`PD^PeE!xmAC4WmGo^r=T^cAG7 zmr?s6^Kuo!NMAZIn>&s{c;{=kzFD3Yt!i|NlDph4iw>6Fa@koNT5vH#Q`2^S4O33y zVVC&;kqJ>-eyBA$<F|CkKG}q)4~#)|(eXAO(bkW4cZ;naS_BL3IGi`oU1`v)_NYOk zMzDno(y>OhfLATqdT$}*r6Nug9HSBkwvXYDK1Mg3d-PPL`Udid!A*MDyn2TS7mG<2 zv*kO4zyJ9sT_Q|>hrWS<Zii`7tcM=6Lt*fdCAo*IBjsnIa-L(K6hw^Z0j#xtN6_X% zPhjRt7U{JL$p`E~im%B-Ch7nDI)N!~ODS?WQ>v+Lht$n$o`K<Ds%gSXF`n(FIv26_ z>@nwcD9&GJzxl`yiA|s~Vh>-r4GC!`b85>RB3s5*%3i_KnNt%F4ElS&`Q~{r`)09# zRjM;tgJ!nXYU8ZA%1%r@52!f6WnMLKaoNxJ?@~GnW^$-NJVKz;CR@9t$Xy2s?4WnS zKDrAkzlo`|JrE}3xx>bfOtQVBD6WGxv>Vrb5T5=JG~vPL6T6@TGFW;P;dD1FFR{WK zbw15q{z-%uwh0|jvEeL)A&dU%TLAYC_8+zQZ4kzvmkff(Xp9BD4J1K_AbZIZ-$g}` z;J8HhS!_qE8w3rMga^+xzJtGUgm*rG$&!FNs@E}NAd%sQ3fV{D=_7#J1%bT5By61J zeEGGy5}B+cJt&ZnZaN3eoVw^RANdKnGXvb1*@W1i7+|mQXX&dyFKCAf#q~wjo@YKI z@Y$8I;iV`fXI(So0pts^@QyW^!d#`!kQFL+(|o~-`BTvcOC)gP^4>tWLNWN@v<DTE z11Sk}L}y!ueOXNGhz><wbPP(BThs^FWya<u%8bV!hl6i3XD%w?Qzt#bylzSV@Na9A zA3yBFYH4O~_2Y~@eqm}+!y|Lm$^E*D-$8I{m3;0LO?`0u$!X3;l<CVH;@Bk_1m@!O z>qG39jL{-qg)SxaB-~U{Ecb68r~LHt-Ngri|GNr(m6BGQMeu3}u>VAO>)@r|{wo^U z1ebtB{tuT_cJsHH_t&x??Mr?P_rD5FDo+js91yLvCHHl=@>#NVIp6T1F(EG+3@g+L zO-r+BAwg%=WZ5S&fS|tMrUki9zh;-|a3XEt1A{7S!>0EF!k={eyADHXC1~sLcRK6+ zU)!gZYeE4~8)Kee#lu92{Je<5i42-&RZY(PoLIu`;yTck0Wi2scvAD8)9(Fj<B(Ow zcz}4%RU3VewGIVeZDYRjCdNR6q4jUbr0IsUH+^cZq`FtY&3P9M_f@0T6lV4`YKRGu zb^+?mGIpTi0Vw52>3m{8;-J@OnMMbq>{*!x7uknhr`QCl=~+16<sX0b3^vKs4b^Sl z!yc7)2Mh4ghCwE8Y6lu0dA2R1MqNH~YoHJTxeGgp{SE@1Q*yAI$&pu|N>^tCe@Jf} zvj%gMVzuAYEwW|C^~3Rxp2vWFR;sBqB)st-iRJT#bz8_2cxN)l0q;%Q+uc?Ai4p64 zd+1yVW%~`?UK%|qbf)4!o3cs{77vTTk+f(>nbou*1YFA{S;OB0q`0y5!%X<E^Z*t` zvtFV%f_twMNkssnd@e7lY0&(GM<eaiIsBT>nSfn<;M@O2)j0)e7A@PlySi-KwyiGP zwr#uWFWa_l+qP}ncHi1(--vr|#99w4=6cMBoFhkm<KHhNhNH;#RK^n3`;rx>)|~kh zB79*f#jLz7&p~sb!%SPfX$?FE)4G_}1eSODOpFG&g)t3p;%>8W^>SA|dFcWCeq#A3 zB=z7?oB1=s<5LiqSh>Q{dE|CQz;WtzV%N{hFX%d+U&duTpP%v2ilX6Ut}M3j7QTAo z7Zr#*ZOQSmFF-zGmm&2HG6K5$T0(FuK<wtH@L6F6O+_c{?-)0JTzhXp{Ay!#JXm|L zLF{Ip+lfGlg`qakpP^bi{Hr6<lfZ>(P$Tdw`mpwK8KsrQx8_u{0`Lpw|JCqMm9}&q z0q^~{US|qc0?B{&4RFzah++S&*M)2l3_y`8ge%et%C|^7Pcyfk`k<c6njgMZ0JILh zU!hw8c;_#dTs|lq$(d(8aUv5DQc`Py_~Ny76Z3S|Jn1umc`F)8e|3#`Cj#qqr!TI| z=9ek%nBivN<Hys?G&ZIe@0X69uAA<XnQzA{P+!(hu24{J(gP=WUT&3W5AJC?S%A{6 zxK{;`?B2%b-5I3QE(XR7D8iuQ=hFjYH_{-Gm+J7#_^kkZl3N`#9mv?LvCXHqk9&}= zPcL1~J;&d}0=y)muoZtN_pHC!dj)t8bF#0SY_A8hZ>Q8fpHjm!Z(nG}ug35vw<hqm zd#c!b);6!vk*>y_WCz|5d0ii#?|}U;o~;qi_KueqXjlogg$k?!2IU2&x-ciHDGLS~ zLk2@3X?mvRI4R6Y<`(hN$(c(mw^D#u|Ni#Dc_5}_#j;wZB39HwrMyY3xw}HsBEKYC zYM`;Z(>E*XP4>4)aM4CnqzMmsAD%XVPas!Pc0O@jhi2g2@l4+Yx@>^L1AuhBgk*8< zYhm`O-U+Q{m`<T@z1_#o#7K>@VYsuuz(2UKhWti3fk`dhD-J<r>Ub_j{HQgYUaXAb zOt|p4Dnm2WY;VKQvrv_G@Zr1_Uu$>l3IPR5!@{s#Vq$H>=tFbBhG;Q=&VGTppfIMM zNNg2Z-II0MaM`8=!IQN-3UEzhLZ2rY%y$jwD<E%DGE78}IM{OU2_bbZejI$IUBbS| zbc!d+BX0Nnij#pr8Tj0I)HG*<5HPqvili!!R_MI2m2_?&k9Km1uRrHjP>*a7hv2<7 z;P2Nf9}+4iuFOEg52?UblxFJ#X1Hk!36o$o<q*aYTV+cNw9^qf0f6<lO_U$#4D(VF z`#Tw%r<PDh(~}@sP>MGA3-t$;=G4?Thl?j0;G%FSl%y*#b&|Hs(B=16=nT92t0s&% zn&95~xf7s*HvS=PPnK{^I8R>Tox57GJ`K_crX-wVT%hY0F#4!F5t5Nk`$zVB93VdY zTbHm`*vP(N;M}-e9FQon)JSyXFvXo)+-M{lj8Tx#g;_P1)jnseN|OXvh1_4E_f~_b zf|4olTWY3UqO63p!vxjGI?=&B1XLx%sJO^jDS}!k*4|MLM<4jFkoE&#PM3hFLt0@3 zmPEY~tv-iVyAv+f5_b=Ux^sODrn89?8Ii&_f)C9`wpiFP4gh=6QN;gFMU1D0?`3QB zNqIIB72;*uXj{~}KlQTkcr5;UI#&;gm1@;DijkJQHB^$3SFRS11B8K@;uSuHWRnf4 zOp{5ipAvXE@(Qvz_JI>kb0{{?M2e-)SwRuj(WWP1zt+;fo(J=SUfRSi0e4`wtY^s) zf+#G}B0a8f8Zap)(n5$iWS(LeXe$}V+%WujC<5yebU#-kx*C=#a*?7*>?Ut)E)yK7 zUT8=t9gG@tO<7Ix3Q0@R8ofjLiep1A_tv5BRk&-2`$6{F1q1_-yA}Nu@00c#$3uer zj_k4P00v_h;@whHF8>6kK)5IwUP?%th2|MZqtru>1JnvyuWxeBGsat4n_F1wUs-F4 zvNko7vBI>}?vW;2*_iKaGS4>$HL)-(Zx=SR!nMSXB~MjZ5zj3ycNS?6n?inv&lr2< z?b^O}_yg?n{|)v)lI!Ghmp<Pcz(M1cFvpKR?BtXRbZs!u3~f}xu1`Q#xj;C(+8{5E z(R4Y^0}M!YXI%uVC|ug}jP*S{F6PbdXjSt})rYl1mip%lf~`q17Pw@^l`az=gFg!7 zojf9TKN23QnJGPdq#MO1it%AzF7a|vq_JrK#v|vcS+U}*o0X(oNt^2hj<j}^TBn`4 z*la_FnR8?r)|dCa@)wtK+FQgsUa*^*m#xw!1I8Hx7!fcH;ovktn@aM|4ym6lPQ4sm z_Kh1R;+C^BHhaYdrzAort!f>i-46wMzMQ7c$v9rz;!5QuP47Wx^`U&IkFFxWC8>{a zBCqwg4^F%NyA=%!Evb0UjLr%?1;3CB-!E=)(*6Gl0-yio+a;8!&R5FECHR=#utP9b z1;}3yfH1zy=_2bfNSwA~{uVj~h|qkUhxT@@8UPyf=c22eYKs1my%=I~98I@CouHW& z=Ui*|yWh$UAX6T@sd}9zVD1P#VJmn?=_vSQEK-g-Rgs;728so6D(K^w*Cy@X5QDI{ zg&wHH9u$Dx;>VQ}<!2Vd>DHqpd`p7*05F~yT}B^u!HpKgmtnt51maG0d~l#(<-wt5 zI>r;%@z$+O!wrcu#ZO&^Y!QC-U6gVo!%F9!7pQ}8aD(%s<$-a5^al`n4<Wd?)gw6l zf!&{2H8v%R-omBsi9n(CT~Y+Lg!8bj<b8yy)Ml!Myj#B#4@9%{oj(%lK>IKY1_*WB z=JNdzmR_`npz;WGLBSqKFdAV%$sas<j4HIil>7Yx3Al;%;2>8q>8Me*q(}W);BR|Q zmXig&M+3AD2v@+3bT~+EX}0^au<cMVh#h;Tw(jVIgrijLV_Z2nHsKIn25{|<9Zm`Y z1hCiLg8~-%`HfVN8=a>433Qhb0bk;9VkcW>nMa<9pE!an-7-|7MMGYb2VDE_(cn7- z8C#E#^1B^D%8iU>&e(Z1x<f0#83oLLdskw~HepojR{xT1BZQkQKC9nM+Ia_Wxeer_ zzn+puz1DMxMB?bqETHi=xTqX{%?CTG)%pqO?o*+^vx6k$&n_Ul1Pe&H1I!lNnJu(2 zoBTyW$sG|s-4Rqygypt8wn!kU4oOs)*p5h@WhWGs2v%H2X;e8)Ar{Gm@-M<kJSb$H z&V;EmaBc<y*~IU%^OgPz%t?7DXrH`SIx6DOCZ5Y+h`&paarB+Yr|TM^WoMj!!>~qt z{J}~F#iTQQtMn$aqVmE-0k~mDkY_Ks&6~{c?@m&1@?sWAPl_^?2i-^1{`%RY<lA9U z%_s?EwS+QP6ko55wF9Aons{Ii`h@0iOD=SWwKU62F|CrcsG^d6vU0b0xW{&lz?WRb z_7>L3<_!@uHMExN3RnK-nD6BC>lQmmsl}%A40i~cFbL9<Cy<yE1uXL;7Ov=E9Vm@Q zzYygkgkW7@(HcG(3+d3!FX=o9X}ZA_Um=Hnqt<_D4j+ul^G<#(D$Em^hO=`zii*B# ziK=J9M?Z%O2GqBICb5OLvR|l6g<lBx>Dwv+v77zLa0T(Q>F(wIev$Zf639#?EH9H| ziW+YEfuBGpQUbhP3|Q<I6n>^n>ixhSQfb#wjW_tXM7*tDU=(Yq&BRk7mTJ}1n!dtF z%xdo*&M39%yu%cb5r5AROfo$Yx_l&M>}}e34pX2MG3DC-rrPZZLRp0PYVIWav1Owr zk*~1HIaj7_{rO9pDa-9+v=Vcv5=>1gm@mUByP5&~afYK$rV4Ud_SWwsQ>(j)@Qi@f z483C~>oxUkr6=xog|g`iazUGSNSL2@Yh2kA$apPs->NR)usoY#yMB%Lh4=sUnleC? ze`TaWfBn*d`*EcHS9+1M{l7TSCKYv6WKraA4_|Y1OyW>IAp|=tJ*P;1!~9%miC$>4 z{`|Pq!r~IY;Oy(V4ln|dhMNzY$?l$Wj=4}c4)^J?n$ek24s{#nm&<{`1jl>&CFPdu z`>mVp@^syQ?O#m3e_nZgxfrwpqR{sSAb7dq`H%~KNA3ZPeu>(Zqr&ayX<!er<B<wk z9q*6*Dz;0%$ucKajalAj;MRdb<%4F_9TX;yf~QBr&!NOS7N!oJ6(R->rN(7LnLyml zhz7si5K%}nUxLpvjiAn~SEj*Mrum5VO}BSeF?AVN0%k_B!pn|+Opq|nT#M|)R&ids zMw>;<j7A1<rbjzT#~tS)ko~>LFRzX)LSz`q)MJPuj*=HNSx074?tTtTdd`%{Wfg*P zWSvwJgrg%bu8tKoOzvkr*`Ov{aH$%)!zrW<4ptw+Wm71wgf0{}gK|L`LRXV=`#`4T zW5u`{*Fz(t(K{L~_pg4yRLHy?wIZ$&c49??;6VgfR#qOp>*W!=Y$=dCQ@=%v$;WW8 zz*jw1h8ZDD(IX0$n@}Cc>LXeLH7AR)h(`G;aZK?YW!e)1HGf9%Z?#Sx2FWKooucJM z&})rHUob9l(dMqR4qGcOLGUT2|B5<m&{vg^@o=!GA?>Fw3s%konxt2+>nD06a7|vB zrpN?nnll!5my&`$->xEZ#wE^%Pa!tvN(+FoMzGWM1(Nh9$M)w3La8CvQSU)$CEgO# zinm6V5OYVF3UO2Iad6Y^fpm)DpfEsh)JrF0D_?RD*;G9o6oGb1<+OuVKSAoC+@%Np zC^EkYi1(!-Z1=1oZg#a9=?Vibw=0G}5P1Q;A&A#Q-iRLX`OW0^u=kU)!^bFVsPNPy zhj%2x#i)!<BkfI6)@4BrFl+)qCRoIk;P=@ZjqiSWL=JwV@Uzph!fy#n*=xpu%B)gD zFl>{RZQ|*%Obo?!AjZ0p8u1^FwA5=#snR4@<QN1!>gNx#Fi(EzV<i9BiyeuH?4tqd zhU4*9g`UZb^I5_^FYR6l%`qR4AS;`FnZ3<RjH-==2KN)}%~n*)fee+@MR2kW2{vH| z2PMynYW_RkZ4hk=)gn29zr6yfK5vmr<QOX`lcg*suCwBvI1=WKw|VynYz5M3Dgue0 z5jzoJK_@uXa2QFH8d=QDFn=?%i|zwD`D0*9j`?g<0vso?Ri{gkwN)hCXIgiYlDC3< zyZRi`Nys604Yg2a#mUa{U-F+J@?2h6t};-kgVx^`R>`yo!x0w;1A=LP1{@YMbCdOH zHj1!h{zE9*5rslTp|3*yC9evDs-MrV>9xkrO5Ss7a{cnjp+gv3N|Bfehy?(k71DYt zgdGJgVHiUK;h+Q&Cv65W787+y{x2G|@{^gahu}2a6=cf}OEp|THUXgJ-$pT3eW$hR z-8KF~qka4D(#MSmR_$7wwBY$it+#&-&x<7!Ig|a+LSAHgBk8qs-ikUg+r3ebwu!Vq zWW13bk$2s*gBhZ+X3htfcV__mJ!ibL0ajJZ!a1Wi)!D}47gFGLHrB0kh)r(3=L?LI z4w*ZM%j{|-B_@`*Bag*wpodkiI+II$<3`g6E%0yC;RC4YNTOQ~Cfp7nZRQk4Pwa$< z>I|btsrgZcs{*=}vg>bzZq3s6<5N3qAJOiyu#5My=Y7c4D4uNZ)doP#{&Uc<HOH_r z#sU4a$M9F-6u}1$W3_4JpXkN}t7Z<|bnPmsbsswG->&jN-GulkL3}PrzxM0x*~UET zb{rij(d2}iNojL&>tU*ZMymkHjy(hJ?L@G%m)Y@|%BTWcn(++*MI05>hm|Q-&p)`W zfrH3za`*0P4;c4Znr^^9)IstRTaYCn5kaa_gwtc2B5}T6x>B5rwJVFQz{i1|o);}D zv-RJLgaUV<VatYzkM>~VTxY=MC$S63$WsGKlGaY>-ho9qDo?jxVK9Wzq3rJ=f?IsS zwXve9vgAe=O0b&U(3%JWyBg=naW_xx7|U&IOHi#*&b^SBUS@#K15`}a+HFw`*1Pc7 zb!$$?(P4*%4C3ly@F#PNK&He!BbiGbHkW~4b)J=_lWcQZ;GNa2lSZ3@i6~etm3^uA z*ba@?BWN;g;IGg-yN>PUxGvR5c+>II%(#6Vle5>C|4Xv_k0?iZLHW@5qyFQ<{tr|k zWdQ^PA!X(Z7ztphh%JoFGu7RH4IJAW?;AVF?F&;IPc9BD?nYS~i>^ZWDi*w@kAd!D zXCfAtvrl(hp;AWCIDX#%82qQFetu?xH_#0^8gkX<*m1*g;<D4-0Qmm6xcN1_D>4^W zg5ZTJjFin<@Mq?*mNLW%+TIJdsEM@dWd2=1<6?e2bPpiug)#u%ojkyX&>5NsLsz7k zGGGFn240VV4#Yn(-3DY`hVzw_&3!9HmBy%TtCj1%G<oEF)4Nw57Je3%cy$=xZhn$b z^Bn))oyPo-y=QfXF(6GliGztj+w?%e#l|aMYQvM^486j#X}q<@QvcA*WT_UcMM&?0 zdRb<O9|Ty>`*h!u;PTjBVnU2Aqs|hyWEm8&SBVQGo9sN3h6Dx$U1`;3a!LM}OJ{r- z9`D;O-Ya~Pj>vva%$loW*<v{I?8A6$mZm(Xp;{%$&?ixys$^(sGB)B)!Or6u7_)y{ z<s&NN4g0n$qdZQ=0eYGCBeb)O5oIz~XK@+KascFDaA}zujU6SC0pn(SI)czvdw7_* zs_&?Fp!6m4Wd<n_g@dfxSz;oN!JL;zO-!{j%c;LPbRB1@A^&x6t$B$6>r}w_VjUt4 z<Dy`;u9(m_DhY29$?ehq9?~R0APnjtwR>PD07}8=b*vfk7|Pj>v{nyBta3|#x^H*8 zG6s-+h`8@BvVF>1e=814Ch3<Op&_E%Xz`J0Btt{1F;d#U=N@Fqmf3K6yR0oqJ6>at zspgli&tT|Wt=r<ESK53`C{MRo=Eip`DVTe%lP9M21~f3dK@~*hek-L-9%4AFUb#9g z@!ByCcT&)~WYlm(i>bk=+DBdGOEDa=zXL4C3i6;*SRP07RADK+%JoqXFj0%1;u?A5 zmdUS0Vi#&*6bE2kSkzXLS^?8vOR*y?Fs#okUQnXBC4H{J928f{*)t?LR^u+T^d84_ zAQi=(AW&MBo~&t_GB#bkAPvlDf9)BE(B1W$on4369~VcQ19B!hJ<iChU&N2jQ2|eW z_ugSZowk>$f0iOh)LvVwSj-HtTBiY$S-AqhuVfJ=VjqzIyjk)xdT@6>IzFG?w?A^{ zOvLRvWo9G^!Fg@Tw)u#C1Q*`1fPF-2t%Ao*UPXiMa7Wk$usz{_JjEF8VO|lfY^O`n z2=)oMv)xhOKpRjZY<KP~_9x&7egIuNevPU5a0x0>nkuOU86bFOiCWnHCxpx3d{uf7 zNOKsySmH~sqD9y@=-aiNSBO0#Eo8B{JmFogLGwn)Prv4)Jfcpb+b_V+&Y*7$b6@3k ztW{{@0Li>g;2Dm>;}^)g{<neIJJ=k!$oFwEIPski|Bw}dv2;1UwI@<`3g984yiGoM z=jo^@>3$wLZ8AbN))CRZ3g}MZH^Z(VHqrhUXXuL(hnE~D`Pms2%4kUAFuG<Huaf8& zdmHJw9Z#=dQFc{f@Vgw&Q%o4UGMGIOo@!}MuaWo{kwy&0&!1|dU)XKO7k1KyB4s&M zW&immL`XTucTq08$@f>ue|OioYyqh*+y|w@pLq+3zpQoOz1wP(^V>x?^6MT-$Ip|9 zb<&O{c~@ZJ_fNvVaOO`w)0AIM_}`iI{~2%+G|ob8{mdQof94LP|LL-ETc&|P?tx(c zbNI(nK<(s*Kgf!aG7SPk1{hMi03fSk_?R?wBy2&$&l@@R^`b7sQ;Vxm;3rG2!T1r= z{!W00EbG5CsmG}AKr$ioOPrrq(X3eZM4@Zav8Y>D=>=6NweD&p-)_Qt4A6aF$ocyZ z;^HGaODcJanE&fn&tazPbmmLv%tz-5w`2bkK-V3n*QArikkQvA696vIFWy|-+NL{9 zfj8LG?SSis@B4=xM~DiSdPI;vvv1(YO=!5_=v57#>h{io8#R{%x2E?OZ{IS~rdNE1 zto?(zP4?k2)20_|=*-@OmA798ox?*B{*YUE2HnGfsy}-~&Hcd&`sPZ&?Ew})ca&}Y z4U=-<RmbPPx=lCP0e}PJ#K=q6-&M~-D<RkE#36kxPX582Mz$asT|0w*@+XTtO3O)# z$x(xpQ?I<OGriudG>oTZeiLC~RN@wTsH_eeqB}G0hu=VMFO{WKpuU6|HT>k{Iz2z1 zE{@wR&ceix1eBb;bf27#lY;*wR_c^(`1YDe;tO4oOqnM@1aLYl3(Q`kIWg}kb4*29 z#xAC1`71$A3K1h@_er6sNRysmn>}H~h^0L8R4QXPiNI2HR8Y64uQ+ki(thBKhZFwt z*hnn`$F!gi&D0y1z2HGsFFu7Wmw<s;CV@`D!c>~h`FA5gn(a><D#;8q%I7g-JBWIX zSUE-#t1mjv96)Nlq6PROo-d_DnW?O}36>~fPNA_T_&Y|so|(=^l4Sui=+1V*vAs#K ze_={8r$$rdh+oA|K5JU<05Oc_qGfS$RmWM&$_Qg-;?*xQitWBl&PVVr?-dYXO+I~! zOz5a5!`C@QQ^}`1R8Z&DKUzeR$kGzqsdP&vAJErj0EjG};3jRXE_0s)nl9HS;dr!c z)1PWgOR8NTVq#6p;;LskY>0qA<48(zz@9MgY{^ZUFr88#{j{mYPgrUoEz1`S4CI9D z@a=U_=?JvDZ_gT9=U~&DD8a8(;+F*FisWvid_(LXA~j!pz!8aNW;b@OU7vp>l<j-a zml$Du2hb><OqQG4CrA_)1j+Y%nKDE~K<L%(q|z&{+hhqh)RU`l)llj$5h^R)3R=n4 z!u<p|^e#uN@_{&UD?w&5N=2Pc8iA|s2#HKkxrLQ{gX8JVJTxkUiZt2Hsgijm{!I}b zpkQg4h9qmUyYUke7zP1ByEAM#`y8T9lfm2I04TJJ$Ie@jniP}OYfPVWr_B#TSvAkV zS!2V*l)u#)Ptu~`GECZu>Y7^RCj9Q9)(@yyiTV|Y=H0eo!m~3YE_)E`-%%^yM{2{$ z1g6=#HXO5I-{OzcsyR#olEP51i;KHGJ_Yu+Mb(<WOIcHg#DGyl`3j+<c*_}#9C?3> z1$Zrs%%OPY@X6bi?i5Ce!U|3zt3B{!v0Ox-uDLzX;P5HiRc)=thfwa5m&>9$d0kK8 zCI#kh9|fgc2ia;UK<t;qaW9{k?}%q@+7Kr$ql+C=^+R82WyF;n^x)!*fqoqF_;7W7 zo;z`nt)#=<Sa2^i)Fqr-W8iaybA7%-0)T}?uSnG01{RrKQd6utD01=UWne;x+yLu> zHZzk+jIDD1i)@<A5n5loWQ^M%PuAD%(~=4CT_`dhW=SAbf;S!zS<b$;yhatEj*#5$ zMu2!CMuwtL7ABCIWL~0xfPtDIpL6hk23D?2I|SHw!5E1NSl?BWkVI6t^RUQH1H3xr zW-fUVDIn0@yV)=g6rr_?If9%h6Ee4b=Zdm2QR-v)7-PEdf6_d}PU#@s!k_dz7BP<l zX1a{+sL%}p=-M{Xwj72k5!HF6&y1EB+G`IL%AMs}M8*hNrtgZ~&P`~M9(rk+_rDov zvsW5kK^sDhK9k|D@x(w*w$Iwc0ftfyCCKq>()2Kn62m#6UxE6O(sE@ps5jCZW%!WV zjR#_B8IVk9GZ&yEAZF<40A`J2x;cKf8=tLt@`}Hbtp~-mb8qTixHcs{qZ(%|FpytK zBIQ)y@x)UoU!)ymM<`#Y9c1n(Un(D8tQ|WxF*mgzo|pFXx)xaQeAqb10Bba!D8)gC z^NJb)PA7!y(8lg2nnRd3x`IpYD9>5(svMI)Nvkv}PY+%KEuxhgG<M?s21dC049Sf8 z445^w=rK9@u6Y~LKH>Rbr8N{nFCye_U2KO?W3+n}!mqTWm%D*A1Rl?!FDyWzqH55E zQfknHRMepRNT`3}DD2>P0Y-o3AiztMHGl0J;v3f_s|v!pYv(RPkYf!wG{GinfJs#S zDO&bZpz8OaumbYDsui_?#tIc$))k5c3eg^@M%5=G%+Yw{)Hj8aqjGpe26?#WDS?;b z6r`s--3l8uR;sK~FsZ%Nvs`kxCNDqXsI>Boc<BolR8C5%yMJmf2T=N;{m||=2&-yx zHS#O?qzkVwjn1%vte?fm6>hRbtP8i<qT0o}0GLm4vD{I?V&wEu%5mej(A!K=!%)cW zQp62dRAoG3yVoRqA#y<jr6cf|_IS$(WLJ+tJx~-9CaJrY?7j#d8Nm`SLCi^%5e!m< zM5C>;mpMqU^zXnJ0J6cexWFOjJMf4L1ybtbaVqNS2wAmG;m`hcT2@d^DcxURMy>-N zw0)4Hu||y(=<t8R24`|pmhbTjRd41spk(es(E{=@!LBSW$@sih6aFc(2oWS7cX#&( zy<+dthrZeqezPOkBHp==+r|H5iV*9_DdiAW;^rf?PoD?G0_6S*39}0dyQPNThT{7G z^NZZT=QxGRLhjvzTPD!_5f6DS6!e-W;5~!SX$&)sFs=|t%!R<E<+n@T;lDe$omdP< za-g#=?(gH|lgE%0JT$KC4o*TiUzx=Bf@qO7WP!_eRa2*h=M}lbBeQlBFe*Xryt;fJ z(kcsX9}p5C00<>29E$Vmybr}Eyc^9?@_M3#v0jK`n~vG*d<cX+sH>jD%#Wqscxjf2 za37pYwHR->2V7~%OtUb#*_Qol5dJ=j2Fx%0J>37-SpgU?ld1#IBo!?FBS8bi!b5X> zXB+%0Ryd|>A70TqSZN#Vxx@qn2AcA|ITuZ<YLDSL1N_mrR#G^7_RcFiY+8fo8+V+X zd5_OK%NO6;D;`p*E{7FXdt9BTTahmjuT%T7iu!h3Xw;I8%6EGLqH*!$07g^H%UX7w z0Rjf!0u28sRlvhcdlVUaTJad{0t%~ZRW@aeFl=?+qupovG>1oOD#2%W!hg|FDg46Y zM&Vlf2VfUB(dUVPe`uWYLVV*JV^U)aFwyaut~{_nlUgniVXVmoNj0qJAKm35_5zex zPXDb%{a4tr+at!iLjly#_N$mtWQ5^w4RZ=S1<lm`TM@xYLG|>7ZGW0s{a__TXUscg z{rZ`=lPQl+$lrOH(vcP+6cVotpPq;RO0z#`L2&S2|G5~`UbGPOf&b73QGUqh|I;<5 z*yMsB0FqS0G_g%F{=tmEQvV?!CXS6Y5;w26@|}`Lgt69$#wG?{7nn;e#YomqGjcWI z;*!jb(x_VUP_pJZmygk?64jJukrXSaa<%Nb>DX@hhT`qMX=!Na7~nf?n0fHN>3ryV z`S|W$JNfsrxbW+FzlZ;FU~5UJiy_qWmb7?#5-`#A`HEV+JwCDF@5Xg)c#b30l~Cr& z;QUt+j{eD}cza;cGtDC>5-dP1LGk+=vlAF^_b8y#D>Nm<?TwxeZ||sp-wPV9VJQae z4xG1dkxczrzL*z<_<Vui3pA&y&(JNijLhu~n=S`#A1FKVE(pInG=i~SITbGdPl}q8 zA0TC-b8}`wu~SRmP#1E4!cAkCiLsmTpaOCHQXL{ASc(c)*?A!}dx=_5QfP#FNvwh) zLyhNWp8j6wVrL-{v1Sh830YzqFV9jW>O8PqNx@QF6agX%B~WOXr0wP?BrTh8mf($u zDJ>auF`W8hj6aex+nm|7Jti(G+3>#P53no^^I?fYpH*Nziv=Bf3Jb*G;>r}4F+;VF zmMBHaWZ)?)vSvw(m!m@mgP97SfjJZ3N|vKi`zp4PDm9PFM4S2ZzL<zRL43&paS=RS z%4ES@w!SdO%fl<Se2^|2tu!PRAP^Oo-22p?w*f|c2}PH&D@^yXsv-N)8W`^52#CLe zI}1*RGLXs=gD=q^jVueZUz&eg(^pwt;6yP~xBNQHX0?PPRfM>2Rsbf||3c&yLx-rT z-HhHJnnMm5X8=(sFTzO7IgMD9k}qNLl&??<mVFM>l8zXGv7b6dBT^*VUe0IQbGdGB zcMHQkKePLQ%)*#-T+32m8F4aH00_9ZX1!O(>A=W^oCS~9WQru!TDuDd1)FSr$xyY; zHx)Eva(QY=bAagLt(00TYic^(uhtt+WN@?#m;#wkr5^q+XO<!o3I2nGk#1$ce0<zH zJ7u<$S@i_LD$ZQ|b+FS-*wdO>pPr&FT*fZ*N3TueJdBA_i&FmxQl}sB2tbfByd)I- zX_Nm*$IzN>09)Ejsc%ie1csPD97sInLucFUBoiyDS*@sw)G!mk>+G(BTd;hHK@z$c zdx9Dur&xwC_p?M@w?1dEc&%d}lCtlDLcy6RcTgzMEM3hY-zU#xIMgV}$+jo*a~Ig> zrb6#uLGDEUF!s{!<H%I71N?y{Z;eAk+L5zE@<4ga(;a3*@q)qt`NLkit6mJt@Pw@x zlJ?F@ij2MYFf*V)4^G)HLbr+BR+9_1LM2wW>Yk}WKT>Yu-WGrspt~6s8F@qgO50ts z`|%nViAVJck)yKKWoUI>;-xW=_}@;D;2;I^dSA~cNSEpr#s^Tb>&i}Y00Po8f=Arg zu2YtHk!o%}r!R0HFdS)<CKA6ZJB)|&2>~dk3=ftn72#gbkg-fYC7EC>Lvw-2Ah>#t z<Sif-BSt>hlUyUPR*~H}i?s>7G^bhQN`RJ5Qwi<si9q^B0|6X;en#PZa&>&e>3om* zeM|QB+~M`-B>}V%n1w)YL`bL2ylR}g$yZSq7_Sve5r1uKZDvpZhOGR>AJvVP7-(@m z^La|n4Odj4%qYYHr1+5HK_loi$6WRF2lV_MBRD#aBK_rdj$}^IWWkZ5JJ2zM(Jc1t zJ5Yk8D9rpJtcLuzHMNk1fGU?Dyj4bkm#2f%z$Sdx83AA+6v^%tpJ9s58*Z+|pQhKg z)*GUA+rwF|n6sFX)zq(ggJf~kiSR@!A)`V#gi?7z;UwJ9*_7DuB8=Sd$XclIZ97fH z)%AwNKk9y}OaF{CZ!xDqsoYb{t<{SyD5TSyh8Abo(_Z<pSkl8+Q&<ZeSL=pt!NtCS z<5V{*ml(jVIvpKP|BzEQr^ys7U3H%Ix*nOguxSpBfaxv$h|0xjq1ldbLlJ&;PU$%- z0U&zj&;N%7IgTEFrYna*RImyWHRMy!fP@{91+RVyJZ$jy@G>OpE7UQXPwjGK@By@* zDe#~Xc45Jhyaewye3V2i4o5vv*k!VSJ0e`{kPtvwUwU}Lhw2cBTTY1cP&!RG>M4y1 zdDEo6c-KuC;jyi1ItSn8&q7A4Z;cuy3%NnG574Hm%8>J$Dji0x6ak+?_M~byz*?UT zvOJq)4R_!gLL~jSZV!a8DW#B?nbHc<@{>SKE$)vwgs#3@YU7yi(HBNZl7GHi%GoKe zJq+N4z($k5C}WUybQ5A@U>j9pQiN2*N-a0EAX0piY7$fHEj}{5%2nf{Sf&kUk*K>K zWLXtn#J&IKEZ1RM(x=lAY04>F&lsc`Om{Gp*3X<4^WQJ!SWZziaj3)1Jz-q`qtzAe z560iu^*hPQzo~j6%b^LB+!0JHBrt*f=oAnhZYt+Nn}6?LL5ZL0Ef5vYwObl@E73VF z+B!+)wW|8M#C`?t%GJ+8%^frKuzKGrIwThtj;R_H4k0>ZBPy&cBRqQ1Pa}M<T<tZP z^jCOj-?fT(=bbv8kb}o>v)TFgijm`unthv`78xZAY})yP015@VnKQIhc!L5I%Wr^p zCVTQUQbqi;owwU>>$r(lm45DK>$H*Qy=iy&25*3{qp<r|dEIe1$R)YZJRh&WLw9if zw!qnj1cXrmLLVFvtw?5>IneKRyIF7?(@`BLj$x1!^cliER@pYQh+Pz?OtaI@z-m)& z^@Z?svQt)Bc=7tdH`dKhl$D<Cj86bl_Y+$RrA{xE?K>*nk)_r%<VN?fZOD-=_jyvz z1538c$S_gQ0@XhaU3V-DQJ<a#_a}}w?KbO{=Rnb!En$yyZ62oT>@6Tnt-sH1M>kJT z2b&7^iJk|oGpatzO?b|ijtIy#cVJ!-^e0r<kC1;)sk}mJ?6Oo}33T_kycevO>0>TB z@8Ldzy|;&|_qQwV^Op;{hjj&c>eO22GHyq_f^<(bw$H%9Fgej?uW+`Ygy(;&zSFjB zPDq-17f#%I)OG*<3mo7ZC~61iW2gA=ztyt;^gX+?^pc-H36{@4h~@urJ>sT(pn)I) zu#GW%OkESiS&`yxZiuWkfDWy4SBM%Zqy;1vNGamyH6)hKu9??NVw_xA;b9{n$%TI( z#XEq&hi4v{NopK{B9s#1y7_^H`a@D?%MHE!Gj(xE*ec9eCYxdUz<U7fe01&H0TR%@ zJ8l?$MI=J#WJYd4$w$^yv<^VEu(T-xhGgjOtH!h`yZ40iSlTqXujTD)QhHuc_I$YI z0;5(*aN)|`O2K)#FAFaD>sn-oz6rgMcJ2)jajE(b+yuCv!$uMWHWY=`f=j&QZX;89 zsfuUbcl%q09^Dzdw@2pm2&RXi46cOfMCrn!(1sp;;c&fVBXv+M={{mZ`2DT`#&_FP z29Nn}*m8FPK%cV_?yvH{eW;VOmG8=5zrxsG74O*basML4<V1|n4IZ$d)Kt6{`G4y0 zm<UOMG+EE7sdnK;ikyG+KY7^J@}e_Dt(t7v4qm8Y)P*8gNYhfQ?DBA-GDn$g?O2nu zlR7txc5{64S+=qb3WD6rX*Nm%_{}#^yJTx*Q~Tt+HR){}T56eAQRb$EA`#hOH+14z zcQQj?rSmE{FvH0DMXbiua_Nq8{3(7D8KsKk&fUb25+0k^ouUdtD$B(emoLEg%3!v~ z7t56J0+>zIJf**i=g=e0zAg+rY-<n_(n;SzM8$=rJ;Y+*QNwf*BY~#?MUdxIBS1YC zRY0<i%zEIteW7!4;I@ngmXFc|)vxRMK0J*oF(5;mI*+MaeQvCHRbgjwt+Z9kg2}jj zQffC0=^X)iz4S|v(G`{B@?zb8F=4?ll&>Xzx8i5gJ4bBQ&tZ=xBm}3=?qo0<F8yNw zLPmB?Z|^D5zDb%_{tU+l&`et92il(3(FWW`d@%@?DAJrjfQl$CqJs}cx@o<OEmej2 zhigTXk0!M7*SK8RYvM7ai%d(*5Zgo}66`Cn%X=afqmV0eK&;LZ8&1&0w6ZeG198+6 zD;<OC3nqlaTmE5jO`l8M9x7nrpln&}@Qn0vm@5fE<h~&JB`W6(s8#_#9a8b(ibjgR z+*GV3X0xpHg}N|t^;WEv9F$GL$yaAn`b_tD>P;5nE-EJ=Q(glZr(aJ@Ocaff%F>sR z<ji+CE7c3U!!UVkM)PVnuE}h+l0MCQMG(-#J`$$hOL!t6sbtwUYsT=%JUqV8k2pH( zd)44UhHey!R8s{2G-J;ixiKx1IR<CTwI~izFv_$=&Lu#Y8gL5`#uA1Y0x0n8I@K}h zfgpO!yLPZq(9v7=MK6{<Hq_OKv?pM2oorEOAo}0(N_$L$aBpnRTO>$fh;R|8gfj+p z9Fp4m=XeJoI@W#@*?<z&d#xEY4RbwBff7{f8*6V=>)Ch!^e2y=GqL9fGJrgW<iJ#@ zm1fg2T87;W4>gz4>ny2o5sOivkbz_HC5FREAq>Q#RVG#Gx3|TXV#3`amyfjghan4@ zi)0tu!jX_O;|l5rn%qZ&6zsXF6it~C9<U|?_Tw|+k2tfa*jHwwoiQ={+;})cOe6)F zWC-%gxD;6dr1?qnv;Ier!$r+P8nrcZv*AgY>$Qv#(bT=b#jq$nbGM+|i?^DcMSDM} z=ZRi&wx-~gJJ>YaaFMCjG2XH*`i=rWH*wx?xp3FlhY<y*(3bDQJ?+ks3%U^-$)OyU zPY?h~?Y8P28w_B?6Z4w@7nD`;{%_DKz#N>Yf2{{FbomE{6fx7gbPxZXB{RyA`BOBL z1MbA}t7pF|hS_R>gXFD}E<C^Wl+2tUp`@al0hi67e=u2%di<TCgGj^1N*dTbh@ocU zi?Ggf>RzvK^OAKO>!aThgik?^rd+h`Z{f(nkzXkSU5wBhrZ*XsK-=m1_&|JSTVlGn z02mRVnPeU<QkZDpH<*Q8a3Dyp@-a-5uGs?IAR<^c;6=n7^8Q>pe~rX{(=gRla=ffc zYf)PNGSKSD7j0qoI4ebEmV>dZzMMu8*)o58xMYBR!~)Y@){W4Ck>Je-(`ioso|UlX zvWAMOrDE9wrDcE5G&a5@*PVLhD>SMs17-^Vz7Fm(oyRgOz(ncKN?R@9{&1+Qp)GqC zdjqvL62>L=T!^3uC$is<9IPu@%`;m%46a}9ujqI)2}@D!%bK<3@NQwiX0$_q3WZYn zH~#n_R-^0F%xHy{S6_I8x5)vRzp7lJkRn~7T>saKc`HVXSt|mYc`LtE%?K>a%04Hc zo#ALxuaIutaIs~qeu}UNiSbX;mbpATiy{i1v`vJFCt_D6Y!yN?@z2>0q(}k|RWn=p zTIDp3Z!}a@4GgWMYuS8wwUZk6EcX?kvr}5~=+v}=(KX{qt~6n-Lrr=3frydJb9FB2 z8`q=&olLNg@vx7~hQ+t1v;L6oh753>HC_ir#eULsIH_vC&1$4G%|J)UtLk{<Xy=Hc zrqqQd;XGy2TK+Q7<wM8a5jUdG8**-gY^OGw=gJnYAb7=3@xaV$Dv%@#XOb7oNecf! zJ;~1!JuINKd$5^hWZr*v3VbN0)}O7CXn-~2wJ-mw*@`!G5q4Ip9Ubn~CJ&$xHeZ%( zCI_*sn?|+c_~ARX6u%=NL+1<eYfj-ypom;C!wyrp0d+rXT(^TVYzJlGbrDqFilOZ9 zi1k8{4{&3PQZK3P;?o77<@9=(3r1DFN@0aJwfK3O+82RW2i%sgzW%0=yA=<)w0!lK zlRMM1tZAx~u9h~qD6OdT>jJP~uOd=f;HODKTpK}DW;Q3sdHkNFSrd=f43iaiP=r^8 znB&3Thqk#NZXn?06Bd{)PxpQ#AeMJWjOmq%MbcIq(Vz2?(vZzfK)D=B<d<qnZqlDv zXr->SlcXx13(;FGhhDXZ9>w?x6@i{kf}SX3QMy{Kh0NF`qfhd~jRh={Rn@5g?}eMG zSlz0w?V`0~)$?Uu_rpq|JuhDNMFwMV*(HVob;}<M3`BZ{^l8xy*l_wO!^@EnOqr$@ z>Crmr(KhP0zlxx>Uy<vz;tqNtY*|rS`J%1;rf(>A!Ak(aaTYwSYB-ffFlqDt1Boug z%h?#2jR;;&Ljmib#tHypIOh-5fJATjje#saUYP{L*t0sf7Fyjzp;_1%BeVvcQLla_ zag_yYdPEC4Qmvl}Qo4q3nlYx1Rz5lev@dR-oc$p_je0v3JEiWoYvmVC?z$SYhTb#Y z;QpDVsHoWz;|xOPUhEMJ&$g71)uwZ7jE%I`FfAgmucjyNU<a7APt6EO9V8I3kKW|w z5_UMXXKz$twG&40u8p$A+Cf<#CzfyWq%RRH>p&ujrzoDBv*Gkup*K5`UwtF*11YrB zOjn%1HZz88WW-8GJ0MZNMpj>-sei90wHrc5bkleS&Q2%sqceE-_JjBoCcQ0+e7lm` zoruQwdxOyr=mCs@?%cvhV=&;FZrr(9dD7@^1RPYm`qwjR%IWausm2e~?5YLN-&G<; zU_4W_!oY5VVpW5Y#h-jX=4w^$YhB+Gjrf-{-_q>W_)!;%r<z6v0yH44LYRwe;L2Sj zu<k7ig_Vhv<`K@T$w`+AP>?QQ(?s|CSZye6_S}4zw*ljvURWDJ8}7e%`h>b7u6nne zPP|(&UmmK!G84^rwLO1hAb7#B|2znszB6Lo&8CT2S2Ak|TIF_tP?Z~E8!ImEniV6D zF4l|{yy98W)2FS+cU%u|x~}dU`YxZm1nn;uSNY+~ax99DVAZ@4^Jm4$l#(>MnL_?i zDd(kP03DAkDTn*Ldi#h!!6`DLSJToDbq;s&#`k;;B8JO3Lj7v~!TjATnm6Zw9gp`W zH4{wrjL}|SJF$4QeueB2duCn*FaRD7i>)v>lon2_k5h+SRd2|bq3rfFMO5lWQWdd? zje>bGTeR5%;#stNWr+A`aqUkhiTbNJ>gZ1sKqK)JNujD%b=*CwURU-&$oa4wmaKd# z%%ARv@);)F`ytk#$10+WTeZ$s@scY9*)xddSz=j1@=`&w()I6g)yvdQ1W1gbS<kl( zW{Zw!c}mi&!rHs;5wj>&@Tf+r@bZT__<}JBg2$Dog8rApb07ql|4Asg&Z5o-Ht8}i zz=cPm>U}?3SA#JY;b+khk03`t5tAt+*o)LDW}y?g&l{e}4X-1Gq9}^GY-JVxyeJPk z|D5M__4X!h%zTH*JvmsR=C!>Uj_zOsNSa^z317xZzx5<LS2tVgpq0-c0NY#I>$e={ z-_<OuT6S8aJ@i5GYiRqI+tn^}5j4(y0HkqP3vWlD9va9_EY9)1R4N+MF-eH8JCQAr z<hsU(!_%^I1gJ$Anc%icerQpXw2o*t3a|?M;8pZAm2+#^fr*agTB+G+;Xbv%20IW6 zPjfgj%Ad6|wRG_zio$$jX3Ekl758J-BB5-BL<8eY2V~o8QKRXj2NlN$m5m5dysNo| z=~E8VM;y~fZPQ0@?)U#?r25as<2NzLHZaV8ECpZ$6R5jTzkdBrIimpK2aFj)k<t^= z<4dZDQ23LE27)6>`4i72>Q%+0iJFWW>9{?Bib-m|6fGBZ%nqR#Y*9k$H~8l^S1&DB zRc%==(YoxE^8NEPMI_btT|J!WaJ}Yw$^4i3kmLA+NEqnI`9-hU`gLm%5G?x^7XtH^ z8X-F|V#=&VFhX^7K>2oU0f1R~-5Y80sn$9XC8<u)w2cK~8;{9(8W@yzcy9CBjMO~5 zRC#Na?tb+mnDSnWsoAaY>F(0*UL{Adn!jbi>%JagyOoApjJenn0N<n#zK&nMrY-#9 zI@<R9T8*SpXcjeSmmO$<lneiWf*<*Ff0r8^a@ZXLwbxI?Yn9ZD2UxmI#bc_I_F1K6 zc^=>5D%=I-*eKIa!8_3lP-@m3!2Lb*28E{O<>zaLG}<y%m{dPtqM+yWWnox_Ai+`R zW>yy$$%?MMlxP;+L<UV}oB?D#A~9Lja`_yK(14gnJHYzGUBxXA6Dnn0v%u2UF4p$; z3M-DhJ9lS3Y*{9e0&rnOIU`7VnI&sj5ydh0!=h<M8i_hDSDP$w{BEY6m~ZA<%L0c& zE`Aw_67n=V8(%V6-RscTuV`R8UeqWHG+c+)%<^@$p+;~#hG><Lpxb-apz6PkER7+V z99BA!Sg>JG8I~@ag~Ym(gytgsYl^xh!z_X}AroEQmya?*3ILa7x4~rQtz2X1$B7J{ zFWR&JJq2DPK4jz}A)!KzE)Yv>V7nMDTFCYW;n>xnB$AyiFG^1Z>^!f}wRDsk7j!XT zlCsE0JrMQ#4JFmf0T%rAJ(bk%9sXXxNPMI=D_M=qHI(FWK}u*orxE&>UndHb5%-z0 zO;v#Qik{hPG@uuK!-zY<B&Y3=aKc7jqc{ng!Jl`;f|0ALiSp);nds%vpNH+OrIZi# ze{g~<+<oU#3%3FbGO2eytqUPQEmet0*D>-%g^d^l*47IM<YsJH#ov`$v64%<%*n76 zjy7p{!9%PA$vEoks>?vmWb|`AfY7@VC}Q6cDR!5ZUI6=u)O^YT@e&&w_6%}mk9wCT za>5#VO2@XzDE3+DA9c3#RNle^?#UJTh&Lc2uooyCs5-F`5Vgt!!nVI|^{EnEp3arO zO+Ri6qrcSh540v1?0?Cgxna(nzS3sT-}2zh>~rAE9Dw`~1W0gpMkEJod+Rd`1+8I< zk(vm{5CA%zmjhIsKE8MsZptH;ZrUSQZi*u@g?@OFT1YZl$3P95QK?9hjbKwxChS9{ zz*(i%m~r?G7Q@Twae9@uct;ifEKL)%)s`Bidt{v3{j%P#X~4IRI6ZtFuwWE;K4k~0 zYWaIBoIU|9&Y!3^r*3FBbyYB>kT^X9H_o5@?SNO&PCELt!pgZjvc`{FQ}YKpAX9I# z+a^gY%ySK=PZWS5(OJuKUN}vLj|jK6FXuPy!Xw<98VSxP+4jO6-RB4TFgf5mpk*yP zWc^0{QnX<Kv8l@W>^P1HI)E~OY+C-p`Ege6oSPfnXe!b`zgaD9m0B{5!m$)aRtE(! z8Gxj@q>U8Q5WdP`q%DeC`&^uSXaN<VTnpJ&MvK6(Qel`-QaT+pTreW5!ocE6hSQMX zpc3v~PQJx#sB%h};cn@YIzDZ&<j+w;1ylkrxa`s-0YQ_jM4u%5o;!lbLPe872|e<7 zfd5TpCzH@dI&_VI{FyXjU}wx``gKBh0<fpr<ev`Jdw%`_PR^S<r8cP$TCrC#kd0%& zRRs<VE9oe>Qs=g}H#cXpi=NoUo^~9Tut(ID9<FMi!%Lt;P<fNj^Cvs=Q>UTGzi<|( ztGzcER$f6<94CzCk?NSWHj$<=Tc;HVHPehK#7fM1v8NwvNz+a|;Rz6QVd*Ax1M*72 zYa|<F{f|q&HWKc)<B3!x9YDuZm_IVHY7`Klab+t^mAR85VRMxuEPx8@r5xi;okntJ zO=!K(JDC%)Xqu@JUBxrnS=Q2!02<ZOZL~4nY5)Y(7kMW>dx>}vm?Y+O@lt*Kv=3*L zk}A6Ts%bwGZc@kFGBKmOXZE-%zy!vcKDCH?>Df0aJyAz_nsEVD^7JERfT$%o%}*u) zAISD2ND#2WoJ&!r@Y!w0`jPU$5((7XCAL160lv1CHYTqg;KUkUF<N2}(XtAb=|Hp{ z8LjS0&83C(KD2zho>QUI^VAsvw1|~<LBeG-8-}ES$lHgO6)~<gEi--_pyq8=8QmhU z^WYF#!(JDHfzouDK1D3qM5<*~CfLuqi#$UTBK|3GdbA<~=XEd9r=H!}MeomI(d`Rp zQqP$Y&Fkzf<+!28et3&UBjBYwEJ5$_fQ&ve=E5I;#<zWgZEYU#b~@i%oG4p`t%}U? z)=aAR;FVRDBkhmE7lB^~l%+<NvEvx0`h#}QEtJoN7GVc<{xzQ57J#9RR-9<&*VO6B z3Ftt3!{&B~H5rF{>|K%b$u+o9fwW|g3NEZ0vv4y0UAns=0X+hZOfcBQWcEEt{Y#PQ zSMpsAxVva6zU+WjR7Oy6aljzmY-BQXb7x1H4f<YoNiECu&YyqxfO{PBQil|fO|D#3 zccAQW9q0}4p2X9k+fR<|PmXR;1_L4uVPD+H&B#5A%b`--)(4Z1B?o<zwp!b^UHQo3 za(RclL_&bPUna7BksI6auP*csBtH(Frgn(-de3vxL?xq0sl@yYde$gIt(iEsiqD_} zR|5gdnK-nCn{t#&K$$`e-G)Y8&8lU#e^^#W8oRkTMUK|6KA6l6|J&G$wERb+7O$`e zLsLA*gX&ol7!YT}asSt9xb<WvsYw_I^cl81&4|=<bwcu(T2v&Ca53oU?AW0h#ey7v z82p0^DwC@0eqdS`a=IPKh~-bb-<|ws#8G4I&n3MeNfP21@H5eU=%T-cR@n!+_5eZX zjB(Mq&S#HY>W_kri)l>_W-%bOVt^ALN>sjGm~LxLeg4{HgGsSd;mgu#oKMCp7$jb| zBvJ<=<f}-j2c-&3VWlgO5^KG+pZ1Dp$LJngI~pWhv?6Ibrhr}68`MvX_rd%$x+aiv z>NtsjlOsL`L<FSrI&h3#=P($&Bdohp<=4xn$5*rK-YrQAcNP@CNw95}Ls!RBuZEd( zF=#K#yVFTL=QZ*xS&38{pp64#Qn}`7=@BJ6DiFM071odA*eN=Qxcfe_IY8b2vEj!o zEzBl3%qOhkifF_=Zr7UcqY=MYo8lJ7eZ+24bpdSwNL)!J<KCJFUqK=>nIG7pm>gKJ z)>y7aSCd>{V1#3(CSp-IBazssn5Pc?pT4dGo~!r!``{v386jnbkd@LR8Cls|DYBBi zLWEDEp%76I4Was0w6seZEtQr^OQHRxB-&~G&+~jPUzdJ=uUD_H?>+DHe$I2wdCoKL zb#JNVX>-1|3**+imp;8yT~ivXW`6aqM9j6zGV7a*T^tWB4LpDSb+kZ%fo#V&es#ZH zJo+^OBE9EKCWse*+ip8EwD9D@k8YPdPX`(H+DLcTUF<cL`oebDwC6+i(pBd+rA_NN zW-oK8qqtA+sI<bfaF0i^n$f;DbzYZvUT%%?_{W*|iB{f9P2GkSiQV7$L%vV>(ioC* zjs4XkOL=Z!({J&4+}{sfF<kpiXYLd8(>DrhHyEBM<((Ct^tGya){3J&iUECMa=$J- z`6#gbk;mm|xt%97njUBAu;0Dk5M{IaPOm~y0^6<2&h6Fp=k*&K+H`*394{_=W72Jr z`deuo3za%%oLE#`UT!%l?e2|ZpS?A-T9TJ=NvYN^E(qK8e6(bjsj6ea4B<|y<=UeI zQX7Oq|8>0mAocp|f|obaek@<n^($o6>>`i04KpwHMz|J>ioM-ke7&zOw4#u`#;oLL z$rjdw#&a8Oj`H8PS;g)7SY1=<RGh%U3g>fs3)?jt6U%0b9<Vw+%e3m%JpYuE1YgC= z(pf#K^OqNQbxN%|sui(M;Z68{)tP0BJ1m{-eDCjj@l}2KtGQNct*OR^u{AY^?#IX_ z&b=Wa=_>anxS-NZz9>hEUvo15;!&4f*s)qYIb9o~s;>R0Oz9gFQIp%Wj0fI+e}2LB zg?FlruS@7>d;N3l!^5#7QK#kNFIPLv6aVZp_DQ<ZnDus2_QQ(Yl#qLso2|spPq^3o ztYG^2rd!SCXPe{~ch;1;bYGS}{YCZjS@T)l&bOilf4T?n1LPiCXM1p;WKE^=xXH$# z@hs1YUU<t=A}X0q>9P9~C7Z|ndGa(|t-3f*Owp)U@5+V4>EWy}h4i8|i^@lMiI<s$ z89sWVVm@{9+jm-d#^Di_+Vd5T>sGlpc`TWjDlf6A<Zb}FYsbB~cO7q9WfRssWxcri z$Z+!4wA!{?Z~C@fW&ck6=*uNv=R5fnzsH!TMeepf+Hs-ze6J@j5xU79Eu^(LPelHm z(9Hmi91lr-uhju{5wFdg>RdY?x~|-vFu!*1d!5}?E29ivw0v{mYrAx}ORTfKZ<(d` zY^O$1;h*Z}H-d$Yg4-Lo4PUDICg-{r^=R!0e3jY~e|bT}n-eCHcRr7f9epxZdUKpd z)6GXlJ+BX()q2$_#YgGi-~aYI`)<uETULxm=?&jcy9^S0%L5EYPWA|~DLoY0Ts|*i zBX6vPgu~UZ*BbAX&2$Vo__QEJIq%PH>niJ`NuAy27S8TY)=#W($bGEQQe1$p1gyN6 z;y-(?;ykg3>}j>!Wm(>G#|jb}e=kW&^GW7zQM=_Wy?;S{?C99LHCB68N~|-GXIG7D zQ4pX0<g4t9sa4&_OfD1|Hk2NBc${nG>{GI-HQT_u-Jx&S){}yJv~PLbdbT*Db_wE5 zaL-@2Iao^Yh#2L>ns79*HCSfm4>jeN!ApJkq>g-$vVJ-$1KvPzcGHc(9UHr<QY;gz z)Z7~u>N<%pX=>VWWSvq|$&BwBf|)W|ZtR>8!zQcqN3yq!-ZgtvV1eBAH=`r(P1t^^ z`DE0&S5@29axb~nu4}H}TO5DCCN@ybCFpeZX443G@6HLWCy{>b@3lH79xvWBU#o9J zl}_<p?bP^76@Ef(!m5woQdgAyceT4^%j*`}t`A>*w)(nD!DJo7s;F_V_8i$1`^%zv zmRUUeg`l^EvHJ<Z-DB2G2)@;LcJ|)h?Ms7iwK?b-sVuKvY8TRGTD+tqWO;qn&m#@J z#TC+7@3KRD-OA+Wt(jn6q599$BM3D_xVLN#Q{gKNl9(a1<zd!x`*-H|FY3ITTE)Md zN;tGf-~P!O<AkuE+a|?u@$jYjKi4mr*2A3^+HN7i)_oTAPe5v1^TmwP3Duf+Y|rGD zzn{OV^n~4~fZ8n250f2wogaJ-xuWS{!*1F>@%Yr|&1yfNT-5zwD^YvCDBXAKGGR}l zvysoIeLIjo&F*a0$t}^%buDVG^@a1(f}ej~S9jAf=TsJaS}-<3Q1<(&v4Wo(tlHfk z^>hWzaM84Je8e_>opf-Ik4JLA<%yN)j}tvUTPEtYy<DJI5^6T#@bO58&JMf86Bp$V z7e2bDa@`|W<F#d_SE0zB_TQ&`&fTq6%Seo|S5(qhITo&MBT{zu)sCuP7Ok67HoMv+ zzL{yKDNc20eOrH$N>1MWvN5Q6ZIFQtw?WH|j(sNfA=<7V>w?)^^23)Hv{&@K*t|tu zIJNI)POpV?+t{EsotKw`%G785xI4A_dE=dILmge}Uh~ARCrkF*wm5bcHyd`JZakMJ zRpluVzOH9g!0u&Xt(S78Hd}`J*qiV?aKBXB5P5L!qnlKs&FT-P`neLfKYIxMEM}D~ zeY!g~zx%|9hWu79Qz?1&tYqzVl8*(pgguFUEqLUZq0`v?i9HVMCQr1G9iOIid3Ulz zz`OGyEhvNUxANiOg-?o3RX4_nCw#o%St`vZ9;(y3Jl@UT!bc)huXEy>5=ha`iToRC zVxRO>7zf3D8~Y$5<xIB9pA`$f@T?90qbdIApu5V^tL{?*FKN0wVwd-@vqTr|FV8v| z(z@8A>1SfZ2eAjsn=Z!lkGXxd`?XHE;wAfk>!*r8wJ7J=l~g2tjwSTI;SDSGD_8ug z8rL}zTkcDn%|Dvr(Yw{)fM&5nY^8sq;-z@$sziz9qE%nT`)UMQUkkP_&QbpKRJuns z=WCGA>Sb@#58O;R*w({k)OcJ$mTkFg<crED?(K^gY4J)=cVQ))^6;1|_Zho9STtpU zm3gmu(Ia+gkg%Mw<;1Gb&#Ln;d~J%h@O4orvp1=-4jpay?Uu;eJC~yqCa3WK`}o(= zcj}WW?N*zqS$(%1A0)E=reB$$-(ItKl}c4NS6FEqO66;I@{N1K-|<mTHhWIq%<*h% z4V9@PA17LzQ!y{Rt90#tIM0i*XJp@a8Wsp<-@QD3N&U|3FK)a+VZ5ud-nPHi3*nWS zA)sC8;#}=~)pKWSijlvn`_)_Om;DP1j$d=UtT{U~VA3^vcBQk=T6g2zH35YZ$FE)T zdKF`rc}=3w;ltxQf1<+OzWYx&=^18l<$;9*JI8j)k@B2)UcpU#Ax&{+G0QT04!*hP z)7Vk$<xpVfR%$dR_L|e_(`@0bH{C`AeY$#7>+&1rKz%8F*==<;!oR&PwO%MY=MmAZ z)+ZBVse8BO&3H@iiI(2xJL^0(sd*wJd-L?qtk#ZNne(vB<-@2Gf+=ewrd2IubqSo% zxFFKu#Wwdm?vY{hLAl4*|5okw={068hb+&>{re?())o7-+*Oh#YLxrCSrspHzr@LQ zcBb)LKA$=N%$Y;yt&fa|SF+mLt-aDm^<~Q#H94=nrygg>w)R-q_HKCFvBmGK!$+Nc zHi3CRdADXRx$`S&^;3oDr13BH^SWxAe_2)gSm`!a^a-+0d}U|9mst0j_4}X)Z{)qq z=VP|SZ2!t`pZ;z9`p$EL4G!V=t{3w2NS)wW#vOdMwD{=HvB61ZaZlE|C3~f;T%MW! z-mk~EG$PZlZ@%^CXfKhD{r<;SmpqP)Sg)cu&flQ@?fg!g6m#F&tl1q)1Y*yb9&CB` zR$zjc47c>BALst?z>6(e+>$)-f+={)6rQ|5hfJvHWS#I!bfm5~yxTGueKMg8@LSZ7 zk0~WL_7q$TTILrK9Uc^?J6Ibdv;ZO9M!ykrun<#Ybx#(&GBYYRSl2HkDsrg011Jvs z2oL-w#?@u2K3L5!A%dZb$!`NWw=1jQ8IR<!K8cxuEQ839m>VU69L*>p@=i)0EUD*6 zHkeUk$;H{0aFNB0sZg64C94Sh4Aby;yEs4CG-HAs9T1xoy)<f=U6bYjSv)sNPh=^g z>E@KsFgBHbGA_(kMc4{fa7&_WbBer86BU~S$48DqhZj&Ihjoxxtiyv?b0}@n1=4C5 z56U;;QACk*=-#GcTlSEv8pAloT7d8!59#&IbV?8jThMAmqQ(mnMPX*L5XUaU5~%_` zUQrE(l;JXXdl~(MJ6VEmTTl~-r9W`9_jpl{2d^yBvmD&y2#Q>lT;dnEWSCz`m(3|+ zZnPyOYdkba%=}pae|9&Jb^+lb!}zjs&?_ml&ypHzI+RPS-*^(D2ZOo7f?^|s!WndU zAmDVIN5NY)1{#g_i}ec+XFvtG@Cw~E_|Y39_~Yk97=dF48sg&Gm2Lp~14GR$XsMsQ zU$jL6yjB*~hrkO`v9f%aNBl+@8;jsi3;#<?tXv6WdO+DaC_FsMGb%Pbkn=9p|Ar>V zhK~|5O&fsbGti7;*sv6y!itCrj1Lde4vAbGHAr2MR1Kn!TCBi(`XUACqok3jHKnA8 zZQJp2+g&^Gl_^k6Vvz0@1*9Abv!+xCvY;VZ`XZ!=uOa7sy8Yl?=_XQuR6@6{Au+ID z+pM7>Q=NhsP1Qje%rQdG24tDKI3)_MOcX~~LSVFELuB=mD}Td7^-oN=xU~DZ;lI1m zuj&)Du&x}sX+t;h6w`>$Md`MbEHbgBM3M4bN|LlbV=jpJZ3l_c=2G%Rv;&J&9RCv) zT(Ol#eRFA2L0b_2>oOqbcxaQN)t#MS$aDCo&M>TgK88*u)Mv16g*U!19{Jc&`~^;4 zB1qJZrk1w@@h6`Fu`E$a##giaIaklvQ4@$K0=+c_>LJD-AuMW)25kZn%buD-P-hjD zUzkBgR>Gg8GEok}m{1l)+Ea3*nQZul`(7AgIZ&$TradJ=2)MT&ac+QgS_mW-jAkx| zW2h@ZRZ1QFGXjh`4k%{t-$!DUN<m&2A1ZO6qxi*YUuzV6qH`Z?UR4>SvtmdYXmy|z z2yxKr`x>9&z09GmTwLQB$nK^6$dSm%krF5HRIQ*aLlG{nbrZO_R2Xp8O(Z>x3dGSo zU)l_t>nWSYfW^;XxGFQy<vR&_Te%q0il)&!)P(cqLLZAkV;Tefa}O~k%ZU;x5Zfb- zY@BG7jz<l3g+P821|rk@TPyli6p8cqJAw1#j*^Q{ohUVO(a#w!Dx4x0H#$?3NEh#^ zj}+SuCVYmWsKGE}M>Rn@xKN@S&k7(B7ut$V-pw0A$8d2)n{jc0Uj}}5rDxRjYjBeI zy59H^J0PFJfSYNrehWeFbfFZ970w5nPrnCeeB|TenlZqL$D!0Sg7k5v#0w5QmqPNc zv>7H#MmCFr;pyNIO@_++m;EX#WJ)y|bF%I-qy`_jO^bo7`j;S2xPpi9QlZTiJXHUI zT!iJo406%V4K6l*>c2Pv?Q)}}h~zAm)P3!jD55lXY6KeX4mAO@X}g2i`xiqjM<m;@ zL_-Djcu|sg-64nmb*IJ=W8Zq*3BoDYCeOuX!Z0>deGvZPbe1sE(PZ(VS#IDEJS}Z2 z9<QJY<MJ{5MUXi|bKEqh=4f=FUnzNQ>}WY??xP|X*K~%`UK5T|n-fb6se4k&#BzI2 zuw2%0Kr|lZxYO-VmU<R65897}_9ru_UbxeEGfEh}_oO&EI?4;Ea(oAg9K9$t(iQ%E z<9RlaIC?NsF_ZFC2*I1vYbtZHv~ph>5<Y19p=TfLlLT)O$5W6X*TshtSw|5fF`CFC zP~X5{(hS=mWa<E=3hO?N>x@i-1T%s@=`#9bwT$RWFm+u+f+PLVY$s|oQNNchA+;aW zp2I(8AB0nhx-hM7Gej{t_w20Y(59pW7ncsBU9L2&sECexQDe!lS!}4VMI46B7bt!( zK@R-vN^e+$p*931p&7WT51ilvPGC-Q`s*-C17*yEf`|L8m8p@u7sg;Cq{OTNIy?@S z6%eCg`6-TWtz^lfiiLFC&A25Ls(^kS*vm9x`IbRCSuAO^a6ZgT&<?!o9Adv}CmOX7 zy3@LmC5ldgh?uZODZ`}#O!xpxE2arI_HyuQMJxs6lu6^2ewKC3gVFsI@Wu?2?;PUb zqr53C(rSq}w7TXbzB(Db_ogICgHr{>PHIA&cR(NP7zD3sIM7=QU|9PsfcYHT&Rzg4 zNf!o0%7o=dd*9D_z+wlHW0vzh4H#-cP#14(JNzAjwH{FVjJDjbqfx$;3i?&c5<uk( z=@N2!A+%L;dq5<D1bpaz&j@kJb^@wv(05Y?Rloy`*G7$(S));II*ot!y{7sO;HJau zYr?>bw{Y;ceJCYjrR8GZjOPGzfc{uApk_}n)QzBC&u+9RgyiFgVCgYXYg;i&3w7Us z`t-fOK2&GDFvtSN{jmOK1~&8=_K*UKSOg1*r?*-B=&>K2D3ca}0~}rt$T@-BhOe?a z2CmAY-iMHqg8f>uejrC(19BM@;YVlq+ScIuy<qh}VD*rh<}1loBqqr%jOzU8DWL;X zN&aApB+yKMN`hpAlRwDA{xId@sMMcs@6~0Ma6HXBLC=|^!;u%U0x2c5|08QG5)Gh) z6z~WXTYK!$cnGT(gmo&ztGy^DkWxl<0kH1$`2tyjokSv$oqB(28f4T|Sjoc2qX&L= zrH2S|+5ja^Xl=Dm<cI*hJ<yvE0Pz5yA&H^Rgs{fuMQ}U}hf)~Q%-q^5jZttI5J>m$ zLe0l#SAftF;0qXpDT?S>AQeiy<PZd2s!_og%#g7pw*;ySq9c3@i{7bWktxz%Ow*pq z$jH%!8Q=)KdXVXcRT`WwtyoNrC&p%?^wK%d`ZOqS%-##=4nRdwa~VzDTP<OTKPJ%z z%K>J=D4E8=TLx1T33YC!p2Ia52;R^^CiU#uC_k7oBdFtkRr$ZbfNPRN3B64@gzb9V z3jc*`ecA7;dC<4pFc6FwE;}}tHh2l8L8vwIT`%ZB<`lv6TFmSzaY8*|P&{=^xX1lR zeOa@wEgz^uVa3JFZdI6rX}lC#n@)+7xTJ)D^M$=RqLR7XqMWS}PQ=NJMEA5n;+_FI z=B!b?0OOU=L_2N~G(U<?!pS1x{v!eZ3Vg*Z2emNAhrtFVHk95P<iRg28T26x>fP;7 zSeoPZmW@<+CJVZWFfgqdUYW9(qYqF4blV+VIWLTEv^6Zd=QFt8W&BXrABBYiZj>k; z26M?&$np+et^<vHg+`dJ=Z(Yoxx_2R;oz0*1bjgeja$qu!zp5T!+1No?z1BV#0eT@ z=4Bx)3NXGcoZh+`1#cK<4sDeW8*0;F0YF2=k!U<O1xnyb+a%}}Ze+c!ps^k_QVdZy zXSkwe#Zb*zNz@nxtW@&+3I90A)$tGmW~zjv`e+Cj7$%MmB~j93GvFKveSE%}Lzf## zNt2f6)~X5J0FqJAx&gCgSWsY_!xOos(a%VFhdD9|So*R!qU<DC8{m8(4TNBk)OwES zpD20{atmLL&E{Mcil!%K!)Umgl!JO=z|N7WkkMp%$KBHFyuk{GF1bS*GSf<XGsZ(f zT*WPh9%c0VPC*^TKpngb>Cap}y(t>N3!u3%Fam%d`NlxgyAEN|G<0?c^k923O(i4C znxh8JxB$&tFr48~h4HS$?@bkX=glFV4#MbXo@G2h=}VzMj%6^P97}@ggZr~ZBrMAn zNZo*xx!?{t&GAa;KJGE-S1er~h2p?I@oL11hjrT6%6<_x9bUR7$9?217EMF9;$S(0 z8)%6gf8h;;4?%TtW>|gc90&UJ0QcDcdRf(KU1|sTNsw$zyxav2{`68xolw{Lw4eQ` z4y%`tp(%F10YhzwNA2UmEae`2K^bMoLzTv6H7u|xKMLl^K;M~FSM3uAx-fx~MkX)0 z`H^S>okQ{oKxOcGKt7#N75zBDGKF+F1&dS{Mh{1Q9e_%s_X+gThVfgKhxpK9CiIYr zKmT(OJ~ENUS6n!A<^^oO0$@I47Kx=S)RY2)pgW$DL0yS-NDu4`VBw{bAk+jVz6w5A zlmaPWvW(Ioz6oCj9h#>)a23wPh7bSg_8+Q0LgDCxB5uRzU#<F3O+5Sw^ZX=Wmen34 z9~kOav8YIgBl2Aid$9G8;z%)>8b?%3l0j~+$CPu<uJ&V*+>`;4G@7EqGYWa7(E(Cg z4)PZU19A;gKi9)mRv<D>h5CDKIc)dvvbhUGJ7;02GV0Ri8N)d^!KHtx%iYcv=<si- zl*}RY%Z%VRPliUxLK3G^&hqdBM*yBXtgue7!f<DjB}*Q3nQaHuZitFG!;3R*F={$W zPK6VEOt{^+TvY+`a(L;S?uen&kkf1)KJ+1#9+Q7kp(}@-un2M?fM*1{Va+3e?9-r% z;{NR6{dG7N5@I84UzxM;1+PI$2hu2cg0CsL;35Py>o*iQ6NahT3kTszBPI6W3T-xR zi4hwt`4Tq(8uQ=$t-AfD3!X$)!@`hRXc7`JUJ}7sz8IRbg5KGOtN^N?%Lhaf=)ekk zg|#4~>U1QOI5DxIdmFX1L3rtOx<8Euc^ieHzCDBHnR}s*%t82+e*D_d-_1UwV8FpS z4%1ZIH3N8k;wq=lR~B3X+V|jEeTJ+46rpFCloZn24e>BuNpZGiDZM6z(U1Y<P@S36 zhh)hhe91~WOY2vH7w-HsAQD76r92{NWCp!iT=VK`=5fd>KHy}gs__0nN<JAhUwQ`c zjXFFaQb2e8=<|g|TPw>R0L?EjjXBI^s|N5>$xKKFkZ&eXd7c>%$)iJ=^pdtZN?Yg} zIQ$|6iJ3q#H5l(ed?&L{{CE|hL}1)7OK@>LM$JT7tLSxM$tq~;pNsga4(eD%DUd=j zay4At+K8G~Q)AGXS{~712knb6V)hM;P(mfE=|f)!^L2vbA!zG{9p{MO#?WaT_BEiJ za*v~HvxXj<O2$<wy)ZU~px{qt1iY=81EsFP-8gazMy5YSpKfDFd28vRN4CljF<uez ztfjr?dblWiJ5b(%e^VIL<<Bs5402oxGanw&KT^sU$^ok(Y>1d?l>eLqJ$#=>23=iC zFCEtYy8_|G>}LoFGfjTH<={oLC{<E?c)xw`tbz_+1h_K83jb~nG%^d0lyKiFvP*@s z0j3WDoXCK>_HdvzS#+q{v%s0^pE*}$I(X!WZN$%}>p*1ol{2tbeB+@|;yStpY{R1S z-v^2AfQWct)5Z^9EFpPzLmZjws-3?E@cc-3J;m8*{tWziJO;XD0roO$fB_fk$%c)- z&L_A7xo<sq412iYiJ|rs;7@}%&t~+kkHx{iUr$XTTV&qX|L%VYj$H}!KXYf2jXq>U z*nn9Kg=N!W&a2$`Vi{OXLHyJh%+(_~%qOxbIbwvdTEi54Yi=7bGczJl3`05ly{+5i zJv5<PonR`H@aAYjC}0iuCGnvoxf@5AA06_{_zB6g5aOo7Fv4gI$A~;ZUSX7!Ll^e8 z9Ee1+4o7rbk#{tz>)&V@O<($DJ$ULQG|xPjT&ah83Mox=Cxb3A?^Id84FT#5P-fcB zpUi=_=2DU)u>Y&IoAT3;ZayW2=W{}1w1F;q#%%=$RshYeVKj%PbD&!QN@&96XPKv> zraYiYHRQ<jba(|e=TSlexS3Cj%Ds_JJ~f(UjaVDujOdphFF*1DF>zCM<OkU#XsL8q zOX8*+wqtp8OV6w##bLI>Ej=>l$a1Ih3ZOChbW<xu4s0b&ahF?i<k9(bheqst<j96* zb`EPM+=c^HG~yM|8hkGl8~;I)y#vo_C!mfo-8sXKqt<2o7q#UdwD%N2Yd@ehX26y> zW3`D$ehx3+u-1-$duVzMT8kLgn!OuG?ca@boIQ-Q&GA4}hr+-#ENC7FYFR)@kO1hk zi@rd91=MKnZ?KKWjdwXh1S$(?uL-A?$e>y*&tJrma~IOB<a>S(Z3Kh7h8Z*=kON)R z53P92>v9>O?!%xaurJ50)&P`bjAw{khCQk)fH=BEVwo|?gr(CON*({&rS2FGL-Hot zt}69hTMX42*8A*u4wStFy4bdfPO`m|j5IbuJbH)4Ln8^}HIcz)nmQ`iD0&M(e+`3< zOyNNPL;`j3W}5o`Azu4pz<(Kr?}f8_Ox@j2-P?8l^X9)=@i3hOwL`QOd*0a|!dAQ= z##)`pf$r;vUN>9ghM~g^D2EL^Hh`~)&V;+>^R^+SB8YSr+y=m+01y%9%U^Y#palEJ z8Hz(ItOab+3t=Z(TSRYus*lHBm<W-6GOW5*!Rdby+&9J}=gc^1qbq>e5BCkV80AlK z2PPbYe2d{$Acl@`v(Ty(<Knt8Vd#B5{e1&aA#|yjF7IM)1z%Gj@~_2*;#JRZ%BR>C z`iQCG_9vMhn3MK{z06_KQBR;^<be{iaKEr1eSeiXA;Fv1Qc}dK^oVET8^EuY5CZ0O zrhbvA+A&luB7;LBgH%l8;(}u7PyG%pCP6G`<*C4ihd1*iNvMG^%KVLSPLO{*V)@W< zid0T}zZW{Q0+x}?BlXSK35UYpIP5}}E)5T|hztzzLrPgRmDO8iz84UgNbnEy<fpia zP)YxdYH_&Vf7YZdHn&>|%dQ;g4%5B2?hp!#zfk=BzMTO3&t7*J()f-(bBf>5NvK2_ zRQ7(zIgRd!Q7x5a1)Ye2XfY!-`#n)*M#>2uzKKwo6SUYbKKy?~O5=#lk%hpx5g3`P zf|hQ=IGSM$htVr)F>3OlZJHuHFEs;p^C9Dg&H(Fr2*aG=4FAJ1!FuORA>w$e%v^@e z|NR%oxJDzVG)htl53E{=Q=#~Vk@7IrH*f-}#LFwgw8AYmDCqA-cunW6GXjTXfkQMH zqd9|*!!&zvfBsH#*rBz&7R(VF7GgdDs$de_B7ldt;17MazySxCk8-xa>9Lp)QzU>A zPV$Z-4^^0uK00N-##@~6FtF4amidcO|2=VqAM-Me4UWej;)Za+AXPC9Ku#;D(QM38 zbEHgL4p`QMdCdOJllcEx%%dVA{38GIC>5ryx3>Bf30V*cXG@w4<Eo|lX~)5{kAJbw zCNeT8)-;xkHFM0>?(P2o-}%MoAk2aqqukH+zo-(T0#LmbB~6H$J{_-Wg34SD=MPMe z-B9`eiQJ;1X^RZ8Mazt3#+5=XI|D>YjNZM}`G1H|4?HemfC)}TR+jC6F1>{GmSYfH zm@$+fA;R3w1~tL1oCK`ycg#y&P^|!OFjco2{Z&;(|L;lpPyJQ{UnpViFwSKeHt@{; zE0GR6Alxr5ZfJ2I`Wg6ss$J>D9z%GBe-wTw8L#7o-#*~kvG8C8bNzVGlR~4mQbuHe zIBkUiGS8n}kw@A1|G=%N0{@a8Ora}VDIId<JHGOJ357mf=T#tQ`J_I~kXZQJ0UQ_! z1Cp6i&qFA5ZW}zM(tU$h9OZ4J_b5By5>Ijjkxf;?8&t-(+WY$q|J{|oE}B9y+bKyt z&WGE{#)zm0u9lk=0HgIP9L_R3_joB$(L~1EDRF`=UY2V)55jyKGK6^+ur8TkO$f|; zbz~k7?Bd?QE{-|Qrlb;>D*B2kandn9`pmGHodKon1@Bn$WCFozaP5D)8+hl#%7~fa zlUEb0Bx--fD?$z`i2~yby$o{c910zN$E%6nl~6(i%lOtB1G^egNbD=`7;@{MV26AZ zX8^q`U9f~gi*`VqAsQ%u2kaq#l_Td;Y8--3N{nP|F>t*8`?|!?9?Ad1QBx?TmsyPW z4G*-4Lp$d%hR!6*B*xqM7%0cn971?mpNRJ0499k~%dhm`20=Xog=hjpZR2GMb!)(h zv7Rc$hbESRx3I#%J1k^|PJ?lcL_rsAho|NUmh*RRoNCV8uJ3q2uw&58GWxV>;7{OS dG08M!#$&`^PE8U~VX3gLg~8Kr&mn2K{trF@t;YZW diff --git a/lib/org-aion-avm-rt.jar b/lib/org-aion-avm-rt.jar index 1cfd6884273e370ee9ae3f038197ee370a5d5b3c..2f3b015f757676d635b999e15a4cd3c40f45e86d 100644 GIT binary patch delta 66932 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 delta 73686 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 Q?S_BhP^^3|P`I@J0Y4h!MF0Q* diff --git a/lib/org-aion-avm-userlib.jar b/lib/org-aion-avm-userlib.jar new file mode 100644 index 0000000000000000000000000000000000000000..f8fb86d0fc1309e8b9a6aadfe4d9a2acbfd25aa8 GIT binary patch literal 36184 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 literal 0 HcmV?d00001 diff --git a/modAion/build.gradle b/modAion/build.gradle index 69a76e0e69..ce5b745522 100644 --- a/modAion/build.gradle +++ b/modAion/build.gradle @@ -3,11 +3,12 @@ ext.moduleName = 'aion.zero' test.dependsOn copyNativeLibsForModuleTests dependencies { - compile project(':modAionBase') + compile 'network.aion:vm-api4j:0.4.0' + compile 'network.aion:util4j:0.4.0' + compile project(':modRlp') compile project(':modCrypto') compile project(':modMcf') - //compile files('../lib/libJson.jar') compile 'org.json:json:20180813' compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.4' compile group: 'org.apache.commons', name: 'commons-collections4', version: '4.0' diff --git a/modAion/src/module-info.java b/modAion/src/module-info.java index e940d257f9..08d434a546 100644 --- a/modAion/src/module-info.java +++ b/modAion/src/module-info.java @@ -1,12 +1,12 @@ module aion.zero { - requires aion.base; + requires aion.vm.api; + requires aion.util; requires aion.rlp; requires aion.crypto; requires aion.mcf; requires slf4j.api; requires org.json; requires commons.lang3; - requires aion.vm.api; exports org.aion.zero.api; exports org.aion.zero.db; diff --git a/modAion/src/org/aion/zero/api/BlockConstants.java b/modAion/src/org/aion/zero/api/BlockConstants.java index 1e9fd087c9..b81e5a49e0 100644 --- a/modAion/src/org/aion/zero/api/BlockConstants.java +++ b/modAion/src/org/aion/zero/api/BlockConstants.java @@ -1,10 +1,9 @@ package org.aion.zero.api; import java.math.BigInteger; -import org.aion.base.type.AionAddress; import org.aion.mcf.blockchain.IBlockConstants; import org.aion.mcf.types.AbstractBlockHeader; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Address; public class BlockConstants implements IBlockConstants { @@ -180,7 +179,7 @@ public long getClockDriftBufferTime() { * @return {@code address} of the sk owning this pair. Also referred to as the owner's address. */ public Address getTokenBridgingAddress() { - return AionAddress.ZERO_ADDRESS(); + return Address.ZERO_ADDRESS(); } @Override diff --git a/modAion/src/org/aion/zero/db/AionContractDetailsImpl.java b/modAion/src/org/aion/zero/db/AionContractDetailsImpl.java index 7d62f22a1c..902a08cfda 100644 --- a/modAion/src/org/aion/zero/db/AionContractDetailsImpl.java +++ b/modAion/src/org/aion/zero/db/AionContractDetailsImpl.java @@ -1,19 +1,18 @@ package org.aion.zero.db; -import static org.aion.base.util.ByteArrayWrapper.wrap; -import static org.aion.base.util.ByteUtil.EMPTY_BYTE_ARRAY; import static org.aion.crypto.HashUtil.EMPTY_TRIE_HASH; import static org.aion.crypto.HashUtil.h256; +import static org.aion.types.ByteArrayWrapper.wrap; +import static org.aion.util.bytes.ByteUtil.EMPTY_BYTE_ARRAY; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; -import org.aion.base.db.IByteArrayKeyValueStore; -import org.aion.base.db.IContractDetails; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.interfaces.db.ByteArrayKeyValueStore; +import org.aion.interfaces.db.ContractDetails; +import org.aion.types.Address; import org.aion.mcf.db.AbstractContractDetails; import org.aion.mcf.ds.XorDataSource; import org.aion.mcf.trie.SecureTrie; @@ -21,20 +20,20 @@ import org.aion.rlp.RLPElement; import org.aion.rlp.RLPItem; import org.aion.rlp.RLPList; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.ByteArrayWrapper; public class AionContractDetailsImpl extends AbstractContractDetails { - private IByteArrayKeyValueStore dataSource; + private ByteArrayKeyValueStore dataSource; private byte[] rlpEncoded; - private Address address = AionAddress.EMPTY_ADDRESS(); + private Address address; private SecureTrie storageTrie = new SecureTrie(null); public boolean externalStorage; - private IByteArrayKeyValueStore externalStorageDataSource; + private ByteArrayKeyValueStore externalStorageDataSource; public AionContractDetailsImpl() {} @@ -71,7 +70,7 @@ public void put(ByteArrayWrapper key, ByteArrayWrapper value) { Objects.requireNonNull(value); // The following must be done before making this call: - // We strip leading zeros of a DataWord but not a DoubleDataWord so that when we call get + // We strip leading zeros of a DataWordImpl but not a DoubleDataWord so that when we call get // we can differentiate between the two. byte[] data = RLP.encodeElement(value.getData()); @@ -92,11 +91,11 @@ public void delete(ByteArrayWrapper key) { } /** - * Returns the value associated with key if it exists, otherwise returns a DataWord consisting + * Returns the value associated with key if it exists, otherwise returns a DataWordImpl consisting * entirely of zero bytes. * * @param key The key to query. - * @return the corresponding value or a zero-byte DataWord if no such value. + * @return the corresponding value or a zero-byte DataWordImpl if no such value. */ @Override public ByteArrayWrapper get(ByteArrayWrapper key) { @@ -152,10 +151,10 @@ public void decode(byte[] rlpCode, boolean fastCheck) { RLPItem storageRoot = (RLPItem) rlpList.get(2); RLPElement code = rlpList.get(4); - if (address.getRLPData() == null) { - this.address = AionAddress.EMPTY_ADDRESS(); + if (address.getRLPData() == null || address.getRLPData().length == 0) { + this.address = null; } else { - this.address = AionAddress.wrap(address.getRLPData()); + this.address = Address.wrap(address.getRLPData()); } if (code instanceof RLPList) { @@ -192,7 +191,7 @@ public void decode(byte[] rlpCode, boolean fastCheck) { public byte[] getEncoded() { if (rlpEncoded == null) { - byte[] rlpAddress = RLP.encodeElement(address.toBytes()); + byte[] rlpAddress = RLP.encodeElement(address == null ? null : address.toBytes()); byte[] rlpIsExternalStorage = RLP.encodeByte((byte) (externalStorage ? 1 : 0)); byte[] rlpStorageRoot = RLP.encodeElement( @@ -248,7 +247,7 @@ public void syncStorage() { * * @param dataSource The new dataSource. */ - public void setDataSource(IByteArrayKeyValueStore dataSource) { + public void setDataSource(ByteArrayKeyValueStore dataSource) { this.dataSource = dataSource; } @@ -257,7 +256,7 @@ public void setDataSource(IByteArrayKeyValueStore dataSource) { * * @return the external storage data source. */ - private IByteArrayKeyValueStore getExternalStorageDataSource() { + private ByteArrayKeyValueStore getExternalStorageDataSource() { if (externalStorageDataSource == null) { externalStorageDataSource = new XorDataSource( @@ -271,7 +270,7 @@ private IByteArrayKeyValueStore getExternalStorageDataSource() { * * @param dataSource The new data source. */ - public void setExternalStorageDataSource(IByteArrayKeyValueStore dataSource) { + public void setExternalStorageDataSource(ByteArrayKeyValueStore dataSource) { this.externalStorageDataSource = dataSource; this.externalStorage = true; this.storageTrie = new SecureTrie(getExternalStorageDataSource()); @@ -285,7 +284,7 @@ public void setExternalStorageDataSource(IByteArrayKeyValueStore dataSource) { * @return the specified AionContractDetailsImpl. */ @Override - public IContractDetails getSnapshotTo(byte[] hash) { + public ContractDetails getSnapshotTo(byte[] hash) { SecureTrie snapStorage = wrap(hash).equals(wrap(EMPTY_TRIE_HASH)) @@ -328,7 +327,7 @@ public AionContractDetailsImpl copy() { aionContractDetailsCopy.setDirty(this.isDirty()); aionContractDetailsCopy.setDeleted(this.isDeleted()); aionContractDetailsCopy.address = - (this.address == null) ? null : new AionAddress(this.address.toBytes()); + (this.address == null) ? null : new Address(this.address.toBytes()); aionContractDetailsCopy.rlpEncoded = (this.rlpEncoded == null) ? null diff --git a/modAion/src/org/aion/zero/db/AionRepositoryCache.java b/modAion/src/org/aion/zero/db/AionRepositoryCache.java index 0c1da6270c..0be3de89ce 100644 --- a/modAion/src/org/aion/zero/db/AionRepositoryCache.java +++ b/modAion/src/org/aion/zero/db/AionRepositoryCache.java @@ -4,32 +4,32 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.aion.base.db.IContractDetails; -import org.aion.base.db.IRepository; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; +import org.aion.interfaces.db.ByteArrayKeyValueStore; +import org.aion.interfaces.db.ContractDetails; +import org.aion.interfaces.db.Repository; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.types.Address; import org.aion.mcf.core.AccountState; import org.aion.mcf.db.AbstractRepositoryCache; import org.aion.mcf.db.ContractDetailsCacheImpl; import org.aion.mcf.db.IBlockStoreBase; -import org.aion.vm.api.interfaces.Address; public class AionRepositoryCache extends AbstractRepositoryCache<IBlockStoreBase<?, ?>> { - public AionRepositoryCache(final IRepository trackedRepository) { + public AionRepositoryCache(final Repository trackedRepository) { this.repository = trackedRepository; this.cachedAccounts = new HashMap<>(); this.cachedDetails = new HashMap<>(); } @Override - public IRepositoryCache startTracking() { + public RepositoryCache startTracking() { return new AionRepositoryCache(this); } /** * Flushes its state to other in such a manner that other receives sufficiently deep copies of - * its {@link AccountState} and {@link IContractDetails} objects. + * its {@link AccountState} and {@link ContractDetails} objects. * * If {@code clearStateAfterFlush == true} then this repository's state will be completely * cleared after this method returns, otherwise it will retain all of its state. @@ -41,13 +41,13 @@ public IRepositoryCache startTracking() { * as type {@link Object} and are cast to their expected types and copied, but will not be copied * if they are not in fact their expected types. This is something to be aware of. Most of the * imperfection results from the inability to copy - * {@link org.aion.base.db.IByteArrayKeyValueStore} and {@link org.aion.mcf.trie.SecureTrie} + * {@link ByteArrayKeyValueStore} and {@link org.aion.mcf.trie.SecureTrie} * perfectly or at all (in the case of the former), for the above reasons. * * @param other The repository that will consume the state of this repository. * @param clearStateAfterFlush True if this repository should clear its state after flushing. */ - public void flushCopiesTo(IRepository other, boolean clearStateAfterFlush) { + public void flushCopiesTo(Repository other, boolean clearStateAfterFlush) { fullyWriteLock(); try { // determine which accounts should get stored @@ -58,12 +58,12 @@ public void flushCopiesTo(IRepository other, boolean clearStateAfterFlush) { // ignore contract state for empty accounts at storage cachedDetails.remove(entry.getKey()); } else { - cleanedCacheAccounts.put(new AionAddress(entry.getKey().toBytes()), account); + cleanedCacheAccounts.put(new Address(entry.getKey().toBytes()), account); } } // determine which contracts should get stored - for (Map.Entry<Address, IContractDetails> entry : cachedDetails.entrySet()) { - IContractDetails ctd = entry.getValue().copy(); + for (Map.Entry<Address, ContractDetails> entry : cachedDetails.entrySet()) { + ContractDetails ctd = entry.getValue().copy(); // TODO: this functionality will be improved with the switch to a // different ContractDetails implementation if (ctd != null && ctd instanceof ContractDetailsCacheImpl) { @@ -96,7 +96,7 @@ public void flushCopiesTo(IRepository other, boolean clearStateAfterFlush) { } @Override - public void flushTo(IRepository other, boolean clearStateAfterFlush) { + public void flushTo(Repository other, boolean clearStateAfterFlush) { fullyWriteLock(); try { // determine which accounts should get stored @@ -111,8 +111,8 @@ public void flushTo(IRepository other, boolean clearStateAfterFlush) { } } // determine which contracts should get stored - for (Map.Entry<Address, IContractDetails> entry : cachedDetails.entrySet()) { - IContractDetails ctd = entry.getValue(); + for (Map.Entry<Address, ContractDetails> entry : cachedDetails.entrySet()) { + ContractDetails ctd = entry.getValue(); // TODO: this functionality will be improved with the switch to a // different ContractDetails implementation if (ctd != null && ctd instanceof ContractDetailsCacheImpl) { @@ -156,7 +156,7 @@ public void flush() { @Override public void updateBatch( - Map<Address, AccountState> accounts, final Map<Address, IContractDetails> details) { + Map<Address, AccountState> accounts, final Map<Address, ContractDetails> details) { fullyWriteLock(); try { @@ -164,7 +164,7 @@ public void updateBatch( this.cachedAccounts.put(accEntry.getKey(), accEntry.getValue()); } - for (Map.Entry<Address, IContractDetails> ctdEntry : details.entrySet()) { + for (Map.Entry<Address, ContractDetails> ctdEntry : details.entrySet()) { ContractDetailsCacheImpl contractDetailsCache = (ContractDetailsCacheImpl) ctdEntry.getValue().copy(); if (contractDetailsCache.origContract != null diff --git a/modAion/src/org/aion/zero/types/A0BlockHeader.java b/modAion/src/org/aion/zero/types/A0BlockHeader.java index 6dc9da8b3e..d15e971cd1 100644 --- a/modAion/src/org/aion/zero/types/A0BlockHeader.java +++ b/modAion/src/org/aion/zero/types/A0BlockHeader.java @@ -1,22 +1,21 @@ package org.aion.zero.types; -import static org.aion.base.util.ByteUtil.longToBytes; -import static org.aion.base.util.ByteUtil.merge; -import static org.aion.base.util.ByteUtil.oneByteToHexString; -import static org.aion.base.util.ByteUtil.toHexString; import static org.aion.crypto.HashUtil.EMPTY_TRIE_HASH; +import static org.aion.util.bytes.ByteUtil.longToBytes; +import static org.aion.util.bytes.ByteUtil.merge; +import static org.aion.util.bytes.ByteUtil.oneByteToHexString; +import static org.aion.util.bytes.ByteUtil.toHexString; +import static org.aion.util.time.TimeUtils.longToDateTime; import java.math.BigInteger; import java.util.Objects; -import org.aion.base.type.AionAddress; -import org.aion.base.type.IPowBlockHeader; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.Utils; +import org.aion.interfaces.block.PowBlockHeader; +import org.aion.types.Address; import org.aion.crypto.HashUtil; import org.aion.mcf.types.AbstractBlockHeader; import org.aion.rlp.RLP; import org.aion.rlp.RLPList; -import org.aion.vm.api.interfaces.Address; +import org.aion.util.bytes.ByteUtil; import org.aion.zero.exceptions.HeaderStructureException; import org.json.JSONObject; @@ -25,7 +24,7 @@ * * @author Ross */ -public class A0BlockHeader extends AbstractBlockHeader implements IPowBlockHeader { +public class A0BlockHeader extends AbstractBlockHeader implements PowBlockHeader { static final int RPL_BH_VERSION = 0, RPL_BH_NUMBER = 1, @@ -86,8 +85,8 @@ public A0BlockHeader(RLPList rlpHeader) { byte[] data = rlpHeader.get(RPL_BH_COINBASE).getRLPData(); this.coinbase = (data == null) - ? AionAddress.EMPTY_ADDRESS() - : AionAddress.wrap(rlpHeader.get(RPL_BH_COINBASE).getRLPData()); + ? null + : Address.wrap(rlpHeader.get(RPL_BH_COINBASE).getRLPData()); // StateRoot this.stateRoot = rlpHeader.get(RPL_BH_STATEROOT).getRLPData(); @@ -216,7 +215,7 @@ public A0BlockHeader( byte[] nonce, byte[] solution) { this.version = version; - this.coinbase = (AionAddress) coinbase; + this.coinbase = (Address) coinbase; this.parentHash = parentHash; this.logsBloom = logsBloom; this.difficulty = difficulty; @@ -372,7 +371,7 @@ private String toStringWithSuffix(final String suffix) { .append(" timestamp=") .append(timestamp) .append(" (") - .append(Utils.longToDateTime(timestamp)) + .append(longToDateTime(timestamp)) .append(")") .append(suffix); toStringBuff.append(" nonce=").append(toHexString(nonce)).append(suffix); @@ -499,7 +498,7 @@ public static A0BlockHeader fromRLP(RLPList rlpHeader, boolean isUnsafe) throws builder.withParentHash(rlpHeader.get(RPL_BH_PARENTHASH).getRLPData()); // Coinbase (miner) - builder.withCoinbase(new AionAddress(rlpHeader.get(RPL_BH_COINBASE).getRLPData())); + builder.withCoinbase(new Address(rlpHeader.get(RPL_BH_COINBASE).getRLPData())); // State root builder.withStateRoot(rlpHeader.get(RPL_BH_STATEROOT).getRLPData()); @@ -559,7 +558,6 @@ public static class Builder { * Some constants for fallbacks, these are not rigorously defined this; * TODO: define these with explanations in the future */ - protected Address EMPTY_ADDRESS = AionAddress.EMPTY_ADDRESS(); protected byte version; protected byte[] parentHash; @@ -864,7 +862,7 @@ public A0BlockHeader build() { this.version = this.version == 0 ? 1 : this.version; this.parentHash = this.parentHash == null ? HashUtil.EMPTY_DATA_HASH : this.parentHash; - this.coinbase = this.coinbase == null ? AionAddress.ZERO_ADDRESS() : this.coinbase; + this.coinbase = this.coinbase == null ? Address.ZERO_ADDRESS() : this.coinbase; this.stateRoot = this.stateRoot == null ? HashUtil.EMPTY_TRIE_HASH : this.stateRoot; this.txTrieRoot = this.txTrieRoot == null ? HashUtil.EMPTY_TRIE_HASH : this.txTrieRoot; this.receiptTrieRoot = diff --git a/modAion/src/org/aion/zero/types/AionInternalTx.java b/modAion/src/org/aion/zero/types/AionInternalTx.java index 8b28b12dc9..cdcc071d9d 100644 --- a/modAion/src/org/aion/zero/types/AionInternalTx.java +++ b/modAion/src/org/aion/zero/types/AionInternalTx.java @@ -1,19 +1,18 @@ package org.aion.zero.types; -import static org.aion.base.util.ByteUtil.toHexString; +import static org.aion.util.conversions.Hex.toHexString; import static org.apache.commons.lang3.ArrayUtils.getLength; import static org.apache.commons.lang3.ArrayUtils.isEmpty; import static org.apache.commons.lang3.ArrayUtils.nullToEmpty; import java.math.BigInteger; import java.nio.ByteBuffer; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteUtil; +import org.aion.types.Address; import org.aion.crypto.ECKey; -import org.aion.mcf.vm.types.DataWord; +import org.aion.mcf.vm.types.DataWordImpl; import org.aion.rlp.RLP; import org.aion.rlp.RLPList; -import org.aion.vm.api.interfaces.Address; +import org.aion.util.bytes.ByteUtil; import org.aion.vm.api.interfaces.InternalTransactionInterface; /** aion internal transaction class. */ @@ -53,7 +52,7 @@ public AionInternalTx( } // @TODO: check this functions used by whom - private static byte[] getData(DataWord nrgPrice) { + private static byte[] getData(DataWordImpl nrgPrice) { return (nrgPrice == null) ? ByteUtil.EMPTY_BYTE_ARRAY : nrgPrice.getData(); } @@ -147,8 +146,8 @@ public void rlpParse() { int rlpIdx = 0; this.nonce = transaction.get(rlpIdx++).getRLPData(); this.parentHash = transaction.get(rlpIdx++).getRLPData(); - this.from = AionAddress.wrap(transaction.get(rlpIdx++).getRLPData()); - this.to = AionAddress.wrap(transaction.get(rlpIdx++).getRLPData()); + this.from = Address.wrap(transaction.get(rlpIdx++).getRLPData()); + this.to = Address.wrap(transaction.get(rlpIdx++).getRLPData()); this.value = transaction.get(rlpIdx++).getRLPData(); // TODO: check the order diff --git a/modAion/src/org/aion/zero/types/AionTransaction.java b/modAion/src/org/aion/zero/types/AionTransaction.java index 896c8f307c..9c38a2f380 100644 --- a/modAion/src/org/aion/zero/types/AionTransaction.java +++ b/modAion/src/org/aion/zero/types/AionTransaction.java @@ -1,12 +1,11 @@ package org.aion.zero.types; -import static org.aion.base.util.ByteUtil.ZERO_BYTE_ARRAY; +import static org.aion.util.bytes.ByteUtil.ZERO_BYTE_ARRAY; import java.math.BigInteger; import java.util.Arrays; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.TimeInstant; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.crypto.ECKey.MissingPrivateKeyException; import org.aion.crypto.HashUtil; @@ -14,10 +13,10 @@ import org.aion.crypto.SignatureFac; import org.aion.mcf.types.AbstractTransaction; import org.aion.mcf.vm.Constants; -import org.aion.mcf.vm.types.DataWord; import org.aion.rlp.RLP; import org.aion.rlp.RLPList; -import org.aion.vm.api.interfaces.Address; +import org.aion.util.bytes.ByteUtil; +import org.aion.util.time.TimeInstant; /** Aion transaction class. */ public class AionTransaction extends AbstractTransaction { @@ -138,7 +137,7 @@ public void rlpParse() { if (tx.get(RLP_TX_TO).getRLPData() == null) { this.to = null; } else { - this.to = AionAddress.wrap(tx.get(RLP_TX_TO).getRLPData()); + this.to = Address.wrap(tx.get(RLP_TX_TO).getRLPData()); } this.timeStamp = tx.get(RLP_TX_TIMESTAMP).getRLPData(); @@ -287,14 +286,14 @@ public Address getContractAddress() { return null; } - org.aion.vm.api.interfaces.Address from = this.getSenderAddress(); + Address from = this.getSenderAddress(); if (from == null) { return null; } try { - return AionAddress.wrap(HashUtil.calcNewAddr(from.toBytes(), this.getNonce())); + return Address.wrap(HashUtil.calcNewAddr(from.toBytes(), this.getNonce())); } catch (Exception e) { LOG.error(e.getMessage(), e); return null; @@ -307,13 +306,7 @@ public boolean isContractCreationTransaction() { rlpParse(); } - // TODO: all this is a temporary solution. - if (this.to == null) { - return true; - } - byte[] toBytes = this.to.toBytes(); - byte[] emptyBytes = AionAddress.EMPTY_ADDRESS().toBytes(); - return Arrays.equals(toBytes, emptyBytes); + return this.to == null; } @Override @@ -332,7 +325,7 @@ public synchronized Address getSenderAddress() { } try { - from = AionAddress.wrap(this.signature.getAddress()); + from = Address.wrap(this.signature.getAddress()); return from; } catch (Exception e) { LOG.error(e.getMessage(), e); @@ -494,7 +487,7 @@ public static AionTransaction create( throws Exception { return new AionTransaction( nonce.toByteArray(), - AionAddress.wrap(to), + Address.wrap(to), amount.toByteArray(), null, nrg, @@ -507,12 +500,12 @@ public void setEncoded(byte[] _encodedData) { parsed = false; } - public DataWord nrgPrice() { - return new DataWord(this.nrgPrice); + public DataWordImpl nrgPrice() { + return new DataWordImpl(this.nrgPrice); } public long nrgLimit() { - return new DataWord(this.nrg).longValue(); + return new DataWordImpl(this.nrg).longValue(); } @Override diff --git a/modAion/src/org/aion/zero/types/AionTxExecSummary.java b/modAion/src/org/aion/zero/types/AionTxExecSummary.java index 7791413846..4eacbf2d41 100644 --- a/modAion/src/org/aion/zero/types/AionTxExecSummary.java +++ b/modAion/src/org/aion/zero/types/AionTxExecSummary.java @@ -4,7 +4,7 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.unmodifiableList; import static java.util.Collections.unmodifiableMap; -import static org.aion.base.util.BIUtil.toBI; +import static org.aion.util.biginteger.BIUtil.toBI; import static org.apache.commons.lang3.ArrayUtils.isEmpty; import static org.apache.commons.lang3.ArrayUtils.isNotEmpty; @@ -16,21 +16,20 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import org.aion.base.type.AionAddress; -import org.aion.base.type.ITxExecSummary; -import org.aion.base.type.ITxReceipt; +import org.aion.interfaces.tx.TxExecSummary; +import org.aion.interfaces.tx.TxReceipt; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; import org.aion.mcf.core.TxTouchedStorage; import org.aion.mcf.db.DetailsDataStore; -import org.aion.mcf.vm.types.DataWord; import org.aion.mcf.vm.types.Log; import org.aion.rlp.RLP; import org.aion.rlp.RLPElement; import org.aion.rlp.RLPList; -import org.aion.vm.api.interfaces.Address; import org.aion.vm.api.interfaces.IExecutionLog; import org.aion.vm.api.interfaces.InternalTransactionInterface; -public class AionTxExecSummary implements ITxExecSummary { +public class AionTxExecSummary implements TxExecSummary { /** * The receipt associated with {@link AionTransaction} that indicates the results of the @@ -42,7 +41,7 @@ public class AionTxExecSummary implements ITxExecSummary { private List<Address> deletedAccounts = emptyList(); private List<InternalTransactionInterface> internalTransactions = emptyList(); - private Map<DataWord, DataWord> storageDiff = emptyMap(); + private Map<DataWordImpl, DataWordImpl> storageDiff = emptyMap(); private TxTouchedStorage touchedStorage = new TxTouchedStorage(); private byte[] result; @@ -134,8 +133,8 @@ protected static TxTouchedStorage decodeTouchedStorage(RLPElement encoded) { for (RLPElement entry : (RLPList) encoded) { RLPList asList = (RLPList) entry; - DataWord key = new DataWord(asList.get(0).getRLPData()); - DataWord value = new DataWord(asList.get(1).getRLPData()); + DataWordImpl key = new DataWordImpl(asList.get(0).getRLPData()); + DataWordImpl value = new DataWordImpl(asList.get(1).getRLPData()); byte[] changedBytes = asList.get(2).getRLPData(); boolean changed = isNotEmpty(changedBytes) && RLP.decodeInt(changedBytes, 0) == 1; @@ -163,10 +162,10 @@ private static byte[] encodeLogs(List<IExecutionLog> logs) { return RLP.encodeList(result); } - private static byte[] encodeStorageDiff(Map<DataWord, DataWord> storageDiff) { + private static byte[] encodeStorageDiff(Map<DataWordImpl, DataWordImpl> storageDiff) { byte[][] result = new byte[storageDiff.size()][]; int i = 0; - for (Map.Entry<DataWord, DataWord> entry : storageDiff.entrySet()) { + for (Map.Entry<DataWordImpl, DataWordImpl> entry : storageDiff.entrySet()) { byte[] key = RLP.encodeElement(entry.getKey().getData()); byte[] value = RLP.encodeElement(entry.getValue().getData()); result[i++] = RLP.encodeList(key, value); @@ -174,11 +173,11 @@ private static byte[] encodeStorageDiff(Map<DataWord, DataWord> storageDiff) { return RLP.encodeList(result); } - private static Map<DataWord, DataWord> decodeStorageDiff(RLPList storageDiff) { - Map<DataWord, DataWord> result = new HashMap<>(); + private static Map<DataWordImpl, DataWordImpl> decodeStorageDiff(RLPList storageDiff) { + Map<DataWordImpl, DataWordImpl> result = new HashMap<>(); for (RLPElement entry : storageDiff) { - DataWord key = new DataWord(((RLPList) entry).get(0).getRLPData()); - DataWord value = new DataWord(((RLPList) entry).get(1).getRLPData()); + DataWordImpl key = new DataWordImpl(((RLPList) entry).get(0).getRLPData()); + DataWordImpl value = new DataWordImpl(((RLPList) entry).get(1).getRLPData()); result.put(key, value); } return result; @@ -213,7 +212,7 @@ private static byte[] encodeDeletedAccounts(List<Address> deletedAccounts) { private static List<Address> decodeDeletedAccounts(RLPList deletedAccounts) { List<Address> result = new ArrayList<>(); for (RLPElement deletedAccount : deletedAccounts) { - result.add(AionAddress.wrap(deletedAccount.getRLPData())); + result.add(Address.wrap(deletedAccount.getRLPData())); } return result; } @@ -252,7 +251,7 @@ public List<InternalTransactionInterface> getInternalTransactions() { @Deprecated /* Use getTouchedStorage().getAll() instead */ - public Map<DataWord, DataWord> getStorageDiff() { + public Map<DataWordImpl, DataWordImpl> getStorageDiff() { if (!parsed) { rlpParse(); } @@ -341,7 +340,7 @@ public static Builder builderFor(AionTxReceipt receipt) { } @Override - public Object getBuilder(ITxReceipt receipt) { + public Object getBuilder(TxReceipt receipt) { return builderFor((AionTxReceipt) receipt); } @@ -374,13 +373,13 @@ public Builder deletedAccounts(List<Address> list) { return this; } - public Builder storageDiff(Map<DataWord, DataWord> storageDiff) { + public Builder storageDiff(Map<DataWordImpl, DataWordImpl> storageDiff) { summary.storageDiff = unmodifiableMap(storageDiff); return this; } public Builder touchedStorage( - Map<DataWord, DataWord> touched, Map<DataWord, DataWord> changed) { + Map<DataWordImpl, DataWordImpl> touched, Map<DataWordImpl, DataWordImpl> changed) { summary.touchedStorage.addReading(touched); summary.touchedStorage.addWriting(changed); return this; diff --git a/modAion/src/org/aion/zero/types/AionTxReceipt.java b/modAion/src/org/aion/zero/types/AionTxReceipt.java index 5d5a255792..44e981e7cd 100644 --- a/modAion/src/org/aion/zero/types/AionTxReceipt.java +++ b/modAion/src/org/aion/zero/types/AionTxReceipt.java @@ -1,14 +1,12 @@ package org.aion.zero.types; -import static org.aion.base.util.ByteUtil.EMPTY_BYTE_ARRAY; +import static org.aion.util.bytes.ByteUtil.EMPTY_BYTE_ARRAY; import static org.apache.commons.lang3.ArrayUtils.nullToEmpty; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; import java.util.Objects; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.Hex; import org.aion.mcf.types.AbstractTxReceipt; import org.aion.mcf.vm.types.Bloom; import org.aion.mcf.vm.types.Log; @@ -16,6 +14,8 @@ import org.aion.rlp.RLPElement; import org.aion.rlp.RLPItem; import org.aion.rlp.RLPList; +import org.aion.util.bytes.ByteUtil; +import org.aion.util.conversions.Hex; import org.aion.vm.api.interfaces.IExecutionLog; /** aion transaction receipt class. */ diff --git a/modAion/src/org/aion/zero/types/IAionBlock.java b/modAion/src/org/aion/zero/types/IAionBlock.java index cb462db278..43a0d06a45 100644 --- a/modAion/src/org/aion/zero/types/IAionBlock.java +++ b/modAion/src/org/aion/zero/types/IAionBlock.java @@ -2,11 +2,11 @@ import java.math.BigInteger; import java.util.List; -import org.aion.base.type.IBlock; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Address; +import org.aion.interfaces.block.Block; /** aion block interface. */ -public interface IAionBlock extends IBlock<AionTransaction, A0BlockHeader> { +public interface IAionBlock extends Block<AionTransaction, A0BlockHeader> { Address getCoinbase(); diff --git a/modAion/test/org/aion/types/AionTransactionTest.java b/modAion/test/org/aion/types/AionTransactionTest.java index f460721d7f..5176d0579a 100644 --- a/modAion/test/org/aion/types/AionTransactionTest.java +++ b/modAion/test/org/aion/types/AionTransactionTest.java @@ -3,10 +3,8 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; -import org.aion.base.type.AionAddress; import org.aion.crypto.ECKeyFac; -import org.aion.mcf.vm.types.DataWord; -import org.aion.vm.api.interfaces.Address; +import org.aion.mcf.vm.types.DataWordImpl; import org.aion.zero.types.AionTransaction; import org.apache.commons.lang3.RandomUtils; import org.junit.Test; @@ -31,7 +29,7 @@ private void assertTransactionEquals(AionTransaction tx, AionTransaction tx2) { @Test public void testSerializationZero() { byte[] nonce = RandomUtils.nextBytes(16); - Address to = AionAddress.wrap(RandomUtils.nextBytes(32)); + Address to = Address.wrap(RandomUtils.nextBytes(32)); byte[] value = RandomUtils.nextBytes(16); byte[] data = RandomUtils.nextBytes(64); long nrg = 0; @@ -49,7 +47,7 @@ public void testSerializationZero() { @Test public void testClone() { byte[] nonce = RandomUtils.nextBytes(16); - Address to = AionAddress.wrap(RandomUtils.nextBytes(32)); + Address to = Address.wrap(RandomUtils.nextBytes(32)); byte[] value = RandomUtils.nextBytes(16); byte[] data = RandomUtils.nextBytes(64); long nrg = RandomUtils.nextLong(0, Long.MAX_VALUE); @@ -66,16 +64,16 @@ public void testClone() { @Test public void testTransactionCost() { - byte[] nonce = DataWord.ONE.getData(); + byte[] nonce = DataWordImpl.ONE.getData(); byte[] from = RandomUtils.nextBytes(20); byte[] to = RandomUtils.nextBytes(Address.SIZE); - byte[] value = DataWord.ONE.getData(); + byte[] value = DataWordImpl.ONE.getData(); byte[] data = RandomUtils.nextBytes(128); - long nrg = new DataWord(1000L).longValue(); - long nrgPrice = DataWord.ONE.longValue(); + long nrg = new DataWordImpl(1000L).longValue(); + long nrgPrice = DataWordImpl.ONE.longValue(); AionTransaction tx = - new AionTransaction(nonce, AionAddress.wrap(to), value, data, nrg, nrgPrice); + new AionTransaction(nonce, Address.wrap(to), value, data, nrg, nrgPrice); long expected = 21000; for (byte b : data) { @@ -86,13 +84,13 @@ public void testTransactionCost() { @Test public void testTransactionCost2() { - byte[] nonce = DataWord.ONE.getData(); + byte[] nonce = DataWordImpl.ONE.getData(); byte[] from = RandomUtils.nextBytes(Address.SIZE); - Address to = AionAddress.EMPTY_ADDRESS(); - byte[] value = DataWord.ONE.getData(); + Address to = null; + byte[] value = DataWordImpl.ONE.getData(); byte[] data = RandomUtils.nextBytes(128); - long nrg = new DataWord(1000L).longValue(); - long nrgPrice = DataWord.ONE.longValue(); + long nrg = new DataWordImpl(1000L).longValue(); + long nrgPrice = DataWordImpl.ONE.longValue(); AionTransaction tx = new AionTransaction(nonce, to, value, data, nrg, nrgPrice); diff --git a/modAionBase/.classpath b/modAionBase/.classpath deleted file mode 100644 index fb5011632c..0000000000 --- a/modAionBase/.classpath +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<classpath> - <classpathentry kind="src" path="src"/> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> - <classpathentry kind="output" path="bin"/> -</classpath> diff --git a/modAionBase/.gitignore b/modAionBase/.gitignore deleted file mode 100644 index e0cee8d414..0000000000 --- a/modAionBase/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.jar -build/ \ No newline at end of file diff --git a/modAionBase/.project b/modAionBase/.project deleted file mode 100644 index 0402dc7daa..0000000000 --- a/modAionBase/.project +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<projectDescription> - <name>modAionBase</name> - <comment></comment> - <projects> - </projects> - <buildSpec> - <buildCommand> - <name>org.eclipse.jdt.core.javabuilder</name> - <arguments> - </arguments> - </buildCommand> - </buildSpec> - <natures> - <nature>org.eclipse.jdt.core.javanature</nature> - </natures> -</projectDescription> diff --git a/modAionBase/build.gradle b/modAionBase/build.gradle deleted file mode 100644 index 8b11eed86f..0000000000 --- a/modAionBase/build.gradle +++ /dev/null @@ -1,10 +0,0 @@ -ext.moduleName = 'aion.base' - -dependencies { - compile 'com.google.guava:guava:25.1-jre' - compile project(':aion_vm_api') - testCompile 'junit:junit:4.12' - testCompile 'pl.pragmatists:JUnitParams:1.1.1' - testCompile 'org.hamcrest:hamcrest-core:1.3' - testCompile 'com.google.truth:truth:0.42' -} diff --git a/modAionBase/src/module-info.java b/modAionBase/src/module-info.java deleted file mode 100644 index 80fe7831f6..0000000000 --- a/modAionBase/src/module-info.java +++ /dev/null @@ -1,10 +0,0 @@ -module aion.base { - requires aion.vm.api; - exports org.aion.base.timer; - exports org.aion.base.type; - exports org.aion.base.util; - exports org.aion.base.db; - exports org.aion.base.io; - exports org.aion.base.vm; - exports org.aion.base; -} diff --git a/modAionBase/src/org/aion/base/Constant.java b/modAionBase/src/org/aion/base/Constant.java deleted file mode 100644 index 1f71af6a23..0000000000 --- a/modAionBase/src/org/aion/base/Constant.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.aion.base; - -public class Constant { - - public static final int MAX_BLK_SIZE = 2 * 1024 * 1024; -} diff --git a/modAionBase/src/org/aion/base/db/DetailsProvider.java b/modAionBase/src/org/aion/base/db/DetailsProvider.java deleted file mode 100644 index 76d7cdc14f..0000000000 --- a/modAionBase/src/org/aion/base/db/DetailsProvider.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.aion.base.db; - -/** - * Interface for a details provider, provides instances of contract details - * - * @author yao - */ -public interface DetailsProvider { - IContractDetails getDetails(); -} diff --git a/modAionBase/src/org/aion/base/db/Flushable.java b/modAionBase/src/org/aion/base/db/Flushable.java deleted file mode 100644 index 5460582228..0000000000 --- a/modAionBase/src/org/aion/base/db/Flushable.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.aion.base.db; - -public interface Flushable { - void flush(); -} diff --git a/modAionBase/src/org/aion/base/db/IByteArrayKeyValueDatabase.java b/modAionBase/src/org/aion/base/db/IByteArrayKeyValueDatabase.java deleted file mode 100644 index 7acc2eb139..0000000000 --- a/modAionBase/src/org/aion/base/db/IByteArrayKeyValueDatabase.java +++ /dev/null @@ -1,3 +0,0 @@ -package org.aion.base.db; - -public interface IByteArrayKeyValueDatabase extends IByteArrayKeyValueStore, IDatabase {} diff --git a/modAionBase/src/org/aion/base/db/IByteArrayKeyValueStore.java b/modAionBase/src/org/aion/base/db/IByteArrayKeyValueStore.java deleted file mode 100644 index acabb1fab0..0000000000 --- a/modAionBase/src/org/aion/base/db/IByteArrayKeyValueStore.java +++ /dev/null @@ -1,3 +0,0 @@ -package org.aion.base.db; - -public interface IByteArrayKeyValueStore extends IKeyValueStore<byte[], byte[]> {} diff --git a/modAionBase/src/org/aion/base/db/IContractDetails.java b/modAionBase/src/org/aion/base/db/IContractDetails.java deleted file mode 100644 index fe21e2ffa4..0000000000 --- a/modAionBase/src/org/aion/base/db/IContractDetails.java +++ /dev/null @@ -1,186 +0,0 @@ -package org.aion.base.db; - -import java.util.Collection; -import java.util.Map; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.vm.api.interfaces.Address; - -public interface IContractDetails { - - /** - * Inserts a key-value pair containing the given key and the given value. - * - * @param key the key to be inserted - * @param value the value to be inserted - */ - void put(ByteArrayWrapper key, ByteArrayWrapper value); - - /** - * Deletes any key-value pair that matches the given key. - * - * @param key the key to be deleted - */ - void delete(ByteArrayWrapper key); - - /** - * Returns the value associated with key. - * - * @implNote Some implementations may handle a non-existent key-value pair differently. - * @param key The key to query. - * @return The associated value or some non-value indicator in the case of no such key-value - * pair. - */ - ByteArrayWrapper get(ByteArrayWrapper key); - - /** - * Returns the code of the address associated with this IContractDetails class. This is for - * addresses that are smart contracts. - * - * @return the code of the associated address. - */ - byte[] getCode(); - - /** - * Returns the code whose hash is codeHash. - * - * @param codeHash The hashed code. - * @return the code. - */ - byte[] getCode(byte[] codeHash); - - /** - * Sets the code of the associated address to code. - * - * @param code The code to set. - */ - void setCode(byte[] code); - - /** - * Returns the storage hash. - * - * @return the storage hash. - */ - byte[] getStorageHash(); - - /** - * Decodes an IContractDetails object from the RLP encoding rlpCode. - * - * @implNote Implementing classes may not necessarily support this method. - * @param rlpCode The encoding to decode. - */ - void decode(byte[] rlpCode); - - /** - * Decodes an IContractDetails object from the RLP encoding rlpCode including the fast check - * optional. - * - * @implNote Implementing classes may not necessarily support this method. - * @param rlpCode The encoding to decode. - * @param fastCheck fast check does the contractDetails needs syncing with external storage - */ - void decode(byte[] rlpCode, boolean fastCheck); - - /** - * Sets the dirty value to dirty. - * - * @param dirty The dirty value. - */ - void setDirty(boolean dirty); - - /** - * Sets the deleted value to deleted. - * - * @param deleted the deleted value. - */ - void setDeleted(boolean deleted); - - /** - * Returns true iff the IContractDetails is dirty. - * - * @return only if this is dirty. - */ - boolean isDirty(); - - /** - * Returns true iff the IContractDetails is deleted. - * - * @return only if this is deleted. - */ - boolean isDeleted(); - - /** - * Returns an rlp encoding of this IContractDetails object. - * - * @implNote Implementing classes may not necessarily support this method. - * @return an rlp encoding of this. - */ - byte[] getEncoded(); - - /** - * Returns a mapping of all the key-value pairs that have keys in the given collection keys. - * - * @param keys the keys to query for - * @return the associated mappings - */ - Map<ByteArrayWrapper, ByteArrayWrapper> getStorage(Collection<ByteArrayWrapper> keys); - - /** - * Sets the storage to contain the specified key-value mappings. - * - * @param storage the specified mappings - * @apiNote Used for testing. - * @implNote A {@code null} value is interpreted as deletion. - */ - void setStorage(Map<ByteArrayWrapper, ByteArrayWrapper> storage); - - /** - * Get the address associated with this IContractDetails. - * - * @return the associated address. - */ - Address getAddress(); - - /** - * Sets the associated address to address. - * - * @param address The address to set. - */ - void setAddress(Address address); - - /** - * Returns a string representation of this IContractDetails. - * - * @return a string representation. - */ - String toString(); - - /** Syncs the storage trie. */ - void syncStorage(); - - /** - * Returns an IContractDetails object pertaining to a specific point in time given by the root - * hash hash. - * - * @implNote Implementing classes may not necessarily support this method. - * @param hash The root hash to search for. - * @return the specified IContractDetails. - */ - IContractDetails getSnapshotTo(byte[] hash); - - /** - * Sets the data source to dataSource. - * - * @implNote Implementing classes may not necessarily support this method. - * @param dataSource The new dataSource. - */ - void setDataSource(IByteArrayKeyValueStore dataSource); - - /** - * Returns a sufficiently deep copy of this object. It is up to all implementations of this - * method to declare which original object references are in fact leaked by this copy, if any, - * and to provide justification of why, despite this, the copy is nonetheless sufficiently deep. - * - * @return A copy of this object. - */ - IContractDetails copy(); -} diff --git a/modAionBase/src/org/aion/base/db/IDatabase.java b/modAionBase/src/org/aion/base/db/IDatabase.java deleted file mode 100644 index c5d545ef5d..0000000000 --- a/modAionBase/src/org/aion/base/db/IDatabase.java +++ /dev/null @@ -1,144 +0,0 @@ -package org.aion.base.db; - -import java.util.Optional; - -/** - * Interface for database connection functionality, to be implemented by all database - * implementations. - * - * @author Alexandra Roatis - * @implNote Note on how a DB is defined and discussed in this driver: a DB is simply a persistent - * key value store with a unique filesystem path (ie. implying that two stores should not exist - * within the same file-system address. - * @apiNote For the underlying DB connection, if [isClosed() == true], then all function calls which - * are documented to throw RuntimeException, must in fact throw a RuntimeException. - */ -public interface IDatabase { - - // Actions that change the state of the database - // ------------------------------------------------------------------- - - /** - * Opens and creates the database connection. If database is already open, returns true. - * - * <p>Implements the "CREATE_IF_NOT_EXISTS" functionality by default (non-configurable in this - * spec). - * - * <p>DB file(s) on disk policy: all DBs shall be enclosed in a directory, regardless of the - * underlying implementation being a single file store or a multi-file store, to make DB cleanup - * easiers - * - * <p>Does NOT throw an exception on failure, simply returns false and logs reason for failure. - * Rationale: don't need complex connection open policy in the application at the moment - * - * @return True if successful, false if not. - */ - boolean open(); - - /** - * Closes private "connection" to DB, relinquishing any underlying resources. Calling {@link - * #close()} on a closed DB has no effect. - * - * <p>NOTE: All interface calls on an instance for which [isClosed() == true] will throw a - * Runtime Exception - */ - void close(); - - /** - * Makes all changes made since the previous commit/rollback permanent and releases any database - * locks currently held by this Connection object. This method should be used only when - * auto-commit mode has been disabled. - * - * @return {@code true} if the changes were successfully committed to storage, {@code false} if - * the changes could not be committed to storage - * @throws RuntimeException if the data store is closed - * @implNote Returns {@code true} with no other effect when auto-commit is already enabled. - */ - boolean commit(); - - /** Reduce the size of the database when possible. */ - void compact(); - - /** Drop database. Removes all data from source. */ - void drop(); - - // Get information about the database state - // ------------------------------------------------------------------------ - - /** - * Returns the name of the DB - * - * @return name of DB provided at initialization. Optional.empty() if none provided - */ - Optional<String> getName(); - - /** - * Returns DB directory where all DB files are stored. DBFilePath configuration delegated to - * driver instantiating a IDatabase - * - * @return path (as String) to top-level DB directory containing a multi-file or single file - * persistent DB (assuming all driver implementations create paths on filesystem such that - * all DB files, whether single file or a multi-file implementation) - * <p>Returns empty optional for non-persistent DB. - */ - Optional<String> getPath(); - - /** - * Returns a flag that indicates if the database is open. - * - * @return {@code true} if open, {@code false} otherwise - */ - boolean isOpen(); - - /** - * Returns a flag that indicates if the database is closed. - * - * @return {@code true} if closed, {@code false} otherwise - */ - boolean isClosed(); - - /** - * Returns a flag that indicates if the database is currently locked for reading or writing. - * - * @return {@code true} if locked, {@code false} otherwise - */ - boolean isLocked(); - - /** - * @return {@code true} if changes are automatically committed to storage, {@code false} - * otherwise - */ - boolean isAutoCommitEnabled(); - - /** - * Indicates the method of persistence this database uses. Whether it's written to disk, only - * held in memory, or stored inside a database engine's proprietary format. - * - * @return The method of persistence this database uses. - */ - PersistenceMethod getPersistenceMethod(); - - /** - * Used to validate if the DB file(s) has been created on disk. Can be used any time during this - * object's lifecycle (before or after open() call). Typical use case: used before open() to - * check if the open will need to create new files on the filesystem - * - * <p>Implementation note: can't just try to open a connection to see if it's successful since a - * second open() on the same db filesystem resource will fail and the whole point of this - * function is to reliably determine (almost like a static function) if the DB file with the - * configured name and path have been created on disk - * - * @return {@code true} if DB file(s) exists at path configured on construction, {@code false} - * otherwise - */ - boolean isCreatedOnDisk(); - - /** - * Returns size of the database in bytes. - * - * @return A {@code long} value representing the approximate size of DB. Returns -1 if the size - * cannot be computed or if the database is not persistent. - * @throws RuntimeException if the data store is closed - */ - long approximateSize(); -} diff --git a/modAionBase/src/org/aion/base/db/IKeyValueStore.java b/modAionBase/src/org/aion/base/db/IKeyValueStore.java deleted file mode 100644 index 4ef37d9e15..0000000000 --- a/modAionBase/src/org/aion/base/db/IKeyValueStore.java +++ /dev/null @@ -1,159 +0,0 @@ -package org.aion.base.db; - -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.util.Optional; - -/** - * Functionality for a key-value store allowing itemized updates. - * - * @param <KeyT> the data type of the keys - * @param <ValueT> the data type of the values - * @author Alexandra Roatis - * @implNote For the underlying database connection, if {@code isClosed() == true}, then all - * function calls which require the database connection will throw a {@link RuntimeException}, - * as documented by this interface. - */ -public interface IKeyValueStore<KeyT, ValueT> extends AutoCloseable { - - /** - * Returns {@code true} if this data store contains no elements. - * - * @return {@code true} if this data store contains no elements - * @throws RuntimeException if the data store is closed - */ - boolean isEmpty(); - - /** - * Returns an {@link Iterator} over the set of keys stored in the database at the time when the - * keys were requested. A snapshot can be used to ensure that the entries do not change while - * iterating through the keys. - * - * @return an iterator over the set of stored keys - * @throws RuntimeException if the data store is closed - * @apiNote Returns an empty iterator if the database keys could not be retrieved. - */ - Iterator<KeyT> keys(); - - /** - * Retrieves a value from the data store, wrapped in an {@link Optional} object. It is fulfilled - * if a value was retrieved from the data store, otherwise the optional is empty. - * - * @param key the key for the new entry - * @throws RuntimeException if the data store is closed - * @throws IllegalArgumentException if the key is {@code null} - */ - Optional<ValueT> get(KeyT key); - - /** - * Stores or updates a value at the corresponding key. Makes no guarantees about when the value - * is actually inserted into the underlying data store. - * - * @param key the key for the new entry - * @param value the value for the new entry - * @throws RuntimeException if the underlying data store is closed - * @throws IllegalArgumentException if either the key or the value is {@code null} - * @implNote The choice of when to push the changes to the data store is left up to the - * implementation. - * @apiNote Put must have the following properties: - * <ol> - * <li>Creates a new entry in the cache, if the key-value pair does not exist in the cache - * or underlying data store. - * <li>Updates the entry in the cache when the key-value pair already exists. - * </ol> - * To delete a key one must explicitly call {@link #delete(Object)}. - */ - void put(KeyT key, ValueT value); - - /** - * Deletes a key from the data store. Makes no guarantees about when the value is actually - * deleted from the underlying data store. - * - * @param key the key of the entry to be deleted - * @throws RuntimeException if the underlying data store is closed - * @throws IllegalArgumentException if the key is {@code null} - * @implNote The choice of when to push the changes to the data store is left up to the - * implementation. - */ - void delete(KeyT key); - - /** - * Stores or updates a value at the corresponding key. The changes are cached until {@link - * #commitBatch()} is called. - * - * <p>May delegate to calling {@link #put(Object, Object)} if batch functionality fails or is - * not implemented. - * - * @param key the key for the new entry - * @param value the value for the new entry - * @throws RuntimeException if the underlying data store is closed - * @throws IllegalArgumentException if either the key or the value is {@code null} - * @implNote The choice of when to push the changes to the data store is left up to the - * implementation. - * @apiNote Put must have the following properties: - * <ol> - * <li>Creates a new entry in the cache, if the key-value pair does not exist in the cache - * or underlying data store. - * <li>Updates the entry in the cache when the key-value pair already exists. - * </ol> - * To delete a key one must explicitly call {@link #deleteInBatch(Object)}. - */ - void putToBatch(KeyT key, ValueT value); - - /** - * Deletes a key from the data store. The changes are cached until {@link #commitBatch()} is - * called. - * - * <p>May delegate to calling {@link #delete(Object)} if batch functionality fails or is not - * implemented. - * - * @param key the key of the entry to be deleted - * @throws RuntimeException if the underlying data store is closed - * @throws IllegalArgumentException if the key is {@code null} - * @implNote The choice of when to push the changes to the data store is left up to the - * implementation. - */ - void deleteInBatch(KeyT key); - - /** - * Pushes updates made using {@link #putToBatch(Object, Object)} and {@link - * #deleteInBatch(Object)} to the underlying data source. - */ - void commitBatch(); - - /** - * Puts or updates the data store with the given <i>key-value</i> pairs, as follows: - * - * <ul> - * <li>if the <i>key</i> is present in the data store, the stored <i>value</i> is overwritten - * <li>if the <i>key</i> is not present in the data store, the new <i>key-value</i> pair is - * stored - * </ul> - * - * @param input a {@link Map} of key-value pairs to be updated in the database - * @throws RuntimeException if the data store is closed - * @throws IllegalArgumentException if the map contains a {@code null} key - * @apiNote To delete a set of keys one must explicitly call {@link #deleteBatch(Collection)}. - */ - void putBatch(Map<KeyT, ValueT> input); - - /** - * Deletes the given keys from the data store. Makes no guarantees about when the entries are - * actually deleted from the underlying data store. - * - * @param keys a {@link Collection} of keys to be deleted form storage - * @throws RuntimeException if the data store is closed - * @throws IllegalArgumentException if the collection contains a {@code null} key - */ - void deleteBatch(Collection<KeyT> keys); - - /** - * Checks that the data source connection is open. Throws a {@link RuntimeException} if the data - * source connection is closed. - * - * @implNote Always do this check after acquiring a lock on the class/data. Otherwise it might - * produce inconsistent results due to lack of synchronization. - */ - void check(); -} diff --git a/modAionBase/src/org/aion/base/db/IPruneConfig.java b/modAionBase/src/org/aion/base/db/IPruneConfig.java deleted file mode 100644 index 3b93b1fb50..0000000000 --- a/modAionBase/src/org/aion/base/db/IPruneConfig.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.aion.base.db; - -/** - * Interface for pruning configuration parameters. - * - * @author Alexandra Roatis - */ -public interface IPruneConfig { - - /** - * Indicates if pruning should be enabled or disabled. - * - * @return {@code true} when pruning enabled, {@code false} when pruning disabled. - */ - boolean isEnabled(); - - /** - * Indicates if archiving should be enabled or disabled. - * - * @return {@code true} when archiving enabled, {@code false} when archiving disabled. - */ - boolean isArchived(); - - /** - * @return the number of topmost blocks for which the full data should be maintained on disk. - */ - int getCurrentCount(); - - /** - * Gets the rate at which blocks should be archived (for which the full data should be - * maintained on disk). Blocks that are exact multiples of the returned value should be - * persisted on disk, regardless of other pruning. - * - * @return integer value representing the archive rate - */ - int getArchiveRate(); -} diff --git a/modAionBase/src/org/aion/base/db/IRepository.java b/modAionBase/src/org/aion/base/db/IRepository.java deleted file mode 100644 index 98d806910c..0000000000 --- a/modAionBase/src/org/aion/base/db/IRepository.java +++ /dev/null @@ -1,99 +0,0 @@ -package org.aion.base.db; - -import java.util.Map; -import java.util.Set; -import org.aion.vm.api.interfaces.Address; - -/** - * Database-like functionality. - * - * @apiNote Allows only batch operations on data. - */ -public interface IRepository<AS, BSB> extends IRepositoryQuery<AS> { - - /** - * Creates a tracker repository for caching future changes. - * - * @return the new tracker repository - */ - IRepositoryCache startTracking(); - - /** Commits all the changes made in this repository to the database storage. */ - void flush(); - - /** - * Performs batch updates on the data. - * - * @param accountStates cached account states - * @param contractDetails cached contract details - */ - void updateBatch( - Map<Address, AS> accountStates, Map<Address, IContractDetails> contractDetails); - - /** Reverts all the changes performed by this repository. */ - void rollback(); - - /** - * Checks if the current repository has an open connection to the database. - * - * @return {@code true} if the database connection is open, {@code false} otherwise - */ - boolean isClosed(); - - /** Closes the connection to the database. */ - void close(); - - /** Reduce the size of the database when possible. */ - void compact(); - - // navigate through snapshots - // -------------------------------------------------------------------------------------- - - /** - * Used to check for corruption in the state database. - * - * @param root a world state trie root - * @return {@code true} if the root is valid, {@code false} otherwise - */ - boolean isValidRoot(byte[] root); - - /** - * Used to check for corruption in the index database. - * - * @param hash a block hash - * @return {@code true} if the block hash has a corresponding index, {@code false} otherwise - */ - boolean isIndexed(byte[] hash, long level); - - byte[] getRoot(); - - /** - * Return to one of the previous snapshots by moving the root. - * - * @param root - new root - */ - void syncToRoot(byte[] root); - - /** - * TODO: differentiate between the sync to root and snapshot functionality - * - * @param root - * @return - */ - IRepository getSnapshotTo(byte[] root); - - /** - * @return {@code true} if the repository is a snapshot (with limited functionality), {@code - * false} otherwise - */ - boolean isSnapshot(); - - // TODO: perhaps remove - BSB getBlockStore(); - - /** Performs batch transactions add. */ - void addTxBatch(Map<byte[], byte[]> pendingTx, boolean isPool); - - /** Performs batch transactions remove. */ - void removeTxBatch(Set<byte[]> pendingTx, boolean isPool); -} diff --git a/modAionBase/src/org/aion/base/db/IRepositoryCache.java b/modAionBase/src/org/aion/base/db/IRepositoryCache.java deleted file mode 100644 index ce8cc4f0c8..0000000000 --- a/modAionBase/src/org/aion/base/db/IRepositoryCache.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.aion.base.db; - -import java.math.BigInteger; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.vm.api.interfaces.Address; - -/** - * Repository interface for individual account additions and updates. - * - * @implNote Tracking a repository should be done through implementing this interface. - */ -public interface IRepositoryCache<AS, BSB> extends IRepository<AS, BSB> { - - // setters relating to user accounts - // ------------------------------------------------------------------------------- - - /** - * Creates a new account state in the database or cache. - * - * @param address the address of the account to be created - * @return a {@link AS} object storing the newly created account state - */ - AS createAccount(Address address); - - /** - * Deletes the account from the cache and database. - * - * @param address the address of the account to be deleted - * @implNote This method only marks the account for deletion. Removing the account from the - * database is done at the next flush operation. - */ - void deleteAccount(Address address); - - /** - * Increases by one the account associated with the given address. - * - * @param address the address of the account of interest - * @return the updated value of the nonce - */ - BigInteger incrementNonce(Address address); - - /** - * Sets to a specific value the nonce of the account associated with the given address. - * - * @param address the address of the account of interest - * @return the updated nonce value for the account - */ - BigInteger setNonce(Address address, BigInteger nonce); - - /** - * Adds the given value to the balance of the account associated with the given address. - * - * @param address the address of the account of interest - * @param value to be added to the balance - * @return the updated balance for the account - */ - BigInteger addBalance(Address address, BigInteger value); - - // setters relating to contracts - // ----------------------------------------------------------------------------------- - - /** - * Stores code associated with an account. - * - * @param address the address of the account of interest - * @param code the code that will be associated with this account - * @implNote Calling this method on already initialized code should leave the account and - * contract state unaltered. - */ - void saveCode(Address address, byte[] code); - - // setters relating to storage - // ------------------------------------------------------------------------------------- - - /** - * Store the given data at the given key in the account associated with the given address. - * - * @param address the address of the account of interest - * @param key the key at which the data will be stored - * @param value the data to be stored - */ - void addStorageRow(Address address, ByteArrayWrapper key, ByteArrayWrapper value); - - /** - * Remove the given storage key from the account associated with the given address. - * - * @param address the address of the account of interest - * @param key the key for which the data will be removed - */ - void removeStorageRow(Address address, ByteArrayWrapper key); - - void flushTo(IRepository repo, boolean clearStateAfterFlush); -} diff --git a/modAionBase/src/org/aion/base/db/IRepositoryConfig.java b/modAionBase/src/org/aion/base/db/IRepositoryConfig.java deleted file mode 100644 index 2a6566a31c..0000000000 --- a/modAionBase/src/org/aion/base/db/IRepositoryConfig.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.aion.base.db; - -import java.util.Properties; - -/** - * Represents a configuration interface accepted that should be accepted by the repository to - * implement necessary configs - * - * @author yao - */ -public interface IRepositoryConfig { - - /** @return absolute path to the DB folder containing files */ - String getDbPath(); - - IPruneConfig getPruneConfig(); - - IContractDetails contractDetailsImpl(); - - Properties getDatabaseConfig(String db_name); -} diff --git a/modAionBase/src/org/aion/base/db/IRepositoryQuery.java b/modAionBase/src/org/aion/base/db/IRepositoryQuery.java deleted file mode 100644 index c1757de740..0000000000 --- a/modAionBase/src/org/aion/base/db/IRepositoryQuery.java +++ /dev/null @@ -1,150 +0,0 @@ -package org.aion.base.db; - -import java.math.BigInteger; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.vm.api.interfaces.Address; - -/** Repository interface for information retrieval. */ -public interface IRepositoryQuery<AS> { - - // getters relating to user accounts - // ------------------------------------------------------------------------------- - - /** - * Checks if the database contains an account state associated with the given address. - * - * @param address the address of the account of interest - * @return {@code true} if the account exists, {@code false} otherwise - */ - boolean hasAccountState(Address address); - - /** - * Loads the account (and contract) state associated with the given address into the given hash - * maps. - * - * @param address the address of the account of interest - * @param accounts a map representing a cache of {@link AS} where the account state will be - * loaded - * @param details a map representing a cache of {@link IContractDetails>} where the contract - * details will be loaded - */ - void loadAccountState( - Address address, Map<Address, AS> accounts, Map<Address, IContractDetails> details); - - /** - * Retrieves the current state of the account associated with the given address. - * - * @param address the address of the account of interest - * @return a {@link AS} object representing the account state as is stored in the database or - * cache - */ - AS getAccountState(Address address); - - /** - * Retrieves the current balance of the account associated with the given address. - * - * @param address the address of the account of interest - * @return a {@link BigInteger} value representing the current account balance - */ - BigInteger getBalance(Address address); - - /** - * Retrieves the current nonce of the account associated with the given address. - * - * @param address the address of the account of interest - * @return a {@link BigInteger} value representing the current account nonce - */ - BigInteger getNonce(Address address); - - // getters relating to contracts - // ----------------------------------------------------------------------------------- - - /** - * Checks if the database contains contract details associated with the given address. - * - * @param addr the address of the account of interest - * @return {@code true} if there are contract details associated with the account, {@code false} - * otherwise - */ - boolean hasContractDetails(Address addr); - - /** - * Retrieves the contract details of the account associated with the given address. - * - * @param addr the address of the account of interest - * @return a {@link IContractDetails<ByteArrayWrapper>} object representing the contract details - * as are stored in the database or cache - */ - IContractDetails getContractDetails(Address addr); - - /** - * Retrieves the code for the account associated with the given address. - * - * @param address the address of the account of interest - * @return the code associated to the account in {@code byte} array format - */ - byte[] getCode(Address address); - - // getters relating to storage - // ------------------------------------------------------------------------------------- - - /** - * Retrieves the entries for the specified key values stored at the account associated with the - * given address. - * - * @param address the address of the account of interest - * @param keys the collection of keys of interest (which may be {@code null}) - * @return the storage entries for the specified keys, or the full storage if the key collection - * is {@code null} - * @apiNote When called with a null key collection, the method retrieves all the storage keys. - */ - Map<ByteArrayWrapper, ByteArrayWrapper> getStorage( - Address address, Collection<ByteArrayWrapper> keys); - - // /** - // * Retrieves the storage size the account associated with the given address. - // * - // * @param address - // * the address of the account of interest - // * @return the number of storage entries for the given account - // */ - // int getStorageSize(Address address); - // - // /** - // * Retrieves all the storage keys for the account associated with the given - // * address. - // * - // * @param address - // * the address of the account of interest - // * @return the set of storage keys, or an empty set if the given account - // * address does not exist - // */ - // Set<ByteArrayWrapper> getStorageKeys(Address address); - - /** - * Retrieves the stored value for the specified key stored at the account associated with the - * given address. - * - * @param address the address of the account of interest - * @param key the key of interest - * @return a {@link ByteArrayWrapper} representing the data associated with the given key - */ - ByteArrayWrapper getStorageValue(Address address, ByteArrayWrapper key); - - /** - * Retrieves the stored transactions for recovering pool tx. - * - * @return the list of transactions encoded bytes. - */ - List<byte[]> getPoolTx(); - - /** - * Retrieves the stored transactions for recovering caching tx. - * - * @return the list of transactions encoded bytes. - */ - List<byte[]> getCacheTx(); -} diff --git a/modAionBase/src/org/aion/base/db/PersistenceMethod.java b/modAionBase/src/org/aion/base/db/PersistenceMethod.java deleted file mode 100644 index 9c0f2cefe8..0000000000 --- a/modAionBase/src/org/aion/base/db/PersistenceMethod.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.aion.base.db; - -public enum PersistenceMethod { - UNKNOWN, - - // The data isn't actually persisted but just stored temporarily in memory - IN_MEMORY, - - // The data is stored in a file directory - FILE_BASED, - - // The data is stored in the proprietary format of a database management system - DBMS -} diff --git a/modAionBase/src/org/aion/base/timer/ITimer.java b/modAionBase/src/org/aion/base/timer/ITimer.java deleted file mode 100644 index 400864c9c9..0000000000 --- a/modAionBase/src/org/aion/base/timer/ITimer.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.aion.base.timer; - -/** - * Interface so we can create a mock - * - * @author yao - */ -public interface ITimer { - void shutdown(); - - void sched(TimerTask timer); -} diff --git a/modAionBase/src/org/aion/base/timer/PoisonPillTask.java b/modAionBase/src/org/aion/base/timer/PoisonPillTask.java deleted file mode 100644 index 8415a1a14e..0000000000 --- a/modAionBase/src/org/aion/base/timer/PoisonPillTask.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.aion.base.timer; - -/** - * Used to destroy the timers - * - * @author yao - */ -public class PoisonPillTask extends TimerTask { - - public PoisonPillTask() { - super(0L); - } -} diff --git a/modAionBase/src/org/aion/base/timer/StackTimer.java b/modAionBase/src/org/aion/base/timer/StackTimer.java deleted file mode 100644 index 506e7f1e7d..0000000000 --- a/modAionBase/src/org/aion/base/timer/StackTimer.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.aion.base.timer; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; - -/** - * Modified to use caching thread pools instead - * - * <p>Note: cannot be shared between threads - * - * @author yao - */ -public class StackTimer implements ITimer { - - public static int NANO_INTERVAL = 10; - - // private final TimerThread thread = new TimerThread(stack); - private final StackTimerRunnable timerRunnable; - - /** - * We won't shutdown the thread pool until the very end, (when we need to shutdown the program) - */ - private static final ExecutorService timers = - Executors.newCachedThreadPool( - new ThreadFactory() { - - @Override - public Thread newThread(Runnable arg0) { - Thread thread = new Thread(arg0, "StackTimer"); - thread.setPriority(Thread.MAX_PRIORITY); - return thread; - } - }); - - /** Called upon program exit, to shutdown the thread pool */ - public static void shutdownPool() { - timers.shutdown(); - } - - public StackTimer() { - timerRunnable = new StackTimerRunnable(); - timers.execute(timerRunnable); - } - - @Override - public void sched(TimerTask task) { - if (task == null) { - throw new RuntimeException("task cannot be null"); - } - - if (!(task.getTimeout() > 0)) { - throw new RuntimeException("timeout has to be > 0"); - } - task.start(); - timerRunnable.submit(task); - } - - @Override - public void shutdown() { - timerRunnable.shutdown(); - } - - public boolean completed() { - return timerRunnable.done(); - } - - protected StackTimerRunnable getTimerRunnable() { - return this.timerRunnable; - } -} diff --git a/modAionBase/src/org/aion/base/timer/StackTimerRunnable.java b/modAionBase/src/org/aion/base/timer/StackTimerRunnable.java deleted file mode 100644 index 047e497c4e..0000000000 --- a/modAionBase/src/org/aion/base/timer/StackTimerRunnable.java +++ /dev/null @@ -1,128 +0,0 @@ -package org.aion.base.timer; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Deque; -import java.util.List; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; - -/** - * Runnable for VMTimer, allowing us to leverage CachedThreadPools for fast thread allocation - * - * @author yao - */ -public class StackTimerRunnable implements Runnable { - private final Deque<TimerTask> stack = new ArrayDeque<>(); - private final BlockingQueue<TimerTask> inputQueue = new LinkedBlockingQueue<>(); - - private volatile boolean done; - - public StackTimerRunnable() {} - - public void submit(TimerTask task) { - synchronized (inputQueue) { - this.inputQueue.add(task); - } - } - - @Override - public void run() { - try { - loop(); - } finally { - this.stack.clear(); - } - this.done = true; - } - - public void shutdown() { - inputQueue.add(new PoisonPillTask()); - } - - private void loop() { - MAIN_LOOP: - while (!Thread.currentThread().isInterrupted()) { - List<TimerTask> tasks = new ArrayList<>(); - if (stack.isEmpty() && inputQueue.isEmpty()) { - try { - // works under the assumption that task should never be null - // please do not feed it nulls - tasks.add(inputQueue.take()); - } catch (InterruptedException e) { - break MAIN_LOOP; // if any interrupted exceptions, shut down - // the timer - } - } - - /** - * The stack might not be empty, in this case, we need to check for any new items that - * may have gathered inside our queue - */ - synchronized (inputQueue) { - if (!inputQueue.isEmpty()) { - inputQueue.drainTo(tasks); - } - } - - // at this point we should have all tasks, check if any of them are - // poison pills - for (int i = 0; i < tasks.size(); i++) { - if (tasks.get(i) instanceof PoisonPillTask) { - break MAIN_LOOP; - } - } - - /** - * At this point, stack may be empty, but we should always have a task we perform the - * following operations at this point: - * - * <p>1) If stack is not empty, check for done() at the top of the stack, and iterate - * down the stack until we reach a task that is not done - * - * <p>2) We check for timeouts on any task that is not done, working under the - * assumption of a stack model, tasks that are not yet done should not have any tasks - * that are done below them in the stack - * - * <p>3) Remove any tasks (iteratively) that are timed out - * - * <p>4) Insert any new tasks for the queue - */ - if (!stack.isEmpty()) { - // check tasks that are done first - { - TimerTask peeked = stack.peek(); - while (peeked != null && peeked.getDone()) { - stack.pop(); - peeked = stack.peek(); - } - } - - // check tasks that are timed out - { - TimerTask peeked = stack.peek(); - long currentTime = System.nanoTime(); - while (peeked != null && (currentTime > peeked.getEndTime())) { - peeked.setTimeOut(); - stack.pop(); - peeked = stack.peek(); - } - } - } - - /** Finally add the new tasks */ - for (int i = 0; i < tasks.size(); i++) { - stack.push(tasks.get(i)); - } - } - } - - /** - * Checks if this runnable is done - * - * @return - */ - public boolean done() { - return this.done; - } -} diff --git a/modAionBase/src/org/aion/base/timer/TimerDummy.java b/modAionBase/src/org/aion/base/timer/TimerDummy.java deleted file mode 100644 index dc41dff7c2..0000000000 --- a/modAionBase/src/org/aion/base/timer/TimerDummy.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.aion.base.timer; - -/** - * Dummy timer for call methods (since I don't have a good way to allocate resources for them yet) - * - * @author yao - */ -public class TimerDummy implements ITimer { - - @Override - public void shutdown() { - // TODO Auto-generated method stub - - } - - @Override - public void sched(TimerTask timer) { - // TODO Auto-generated method stub - - } -} diff --git a/modAionBase/src/org/aion/base/timer/TimerTask.java b/modAionBase/src/org/aion/base/timer/TimerTask.java deleted file mode 100644 index 113903ad4c..0000000000 --- a/modAionBase/src/org/aion/base/timer/TimerTask.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.aion.base.timer; - -/** @author jin */ -public class TimerTask { - - public TimerTask(long timeOut) { - this.timeOut = timeOut; - } - - public Object lock = new Object(); - - private long timeOut; - private long startTime; - private long timeoutAt; - private long endTime; - - private boolean isTimeOut; - private boolean isDone; - - public long getEndTime() { - return this.endTime; - } - - public long getTimeout() { - return this.timeOut; - } - - public void start() { - this.startTime = System.nanoTime(); - this.endTime = this.startTime + this.timeOut; - } - - public long getRemaining() { - return Math.max(getEndTime() - System.nanoTime(), 1L); - } - - public boolean isTimeOut() { - return isTimeOut; - } - - public synchronized void setDone() { - this.isDone = true; - } - - public boolean getDone() { - return this.isDone; - } - - protected synchronized void setTimeOut() { - if (isTimeOut == false) { - isTimeOut = true; - timeoutAt = System.nanoTime(); - } - } - - public long getTimeoutDuration() { - return this.timeoutAt - this.startTime; - } -} diff --git a/modAionBase/src/org/aion/base/type/AionAddress.java b/modAionBase/src/org/aion/base/type/AionAddress.java deleted file mode 100644 index 18fdf5c570..0000000000 --- a/modAionBase/src/org/aion/base/type/AionAddress.java +++ /dev/null @@ -1,149 +0,0 @@ -package org.aion.base.type; - -import java.util.Arrays; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.Bytesable; -import org.aion.vm.api.interfaces.Address; - -/** - * The address class is a byte array wrapper represent fixed-32bytes array for the kernel account - * (public key) has more security compare with 20bytes address blockchain system. - * - * @author jay - */ -public final class AionAddress - implements org.aion.vm.api.interfaces.Address, - Comparable<AionAddress>, - Bytesable<AionAddress>, - Cloneable { - private static final AionAddress zeroAddr = AionAddress.wrap(new byte[SIZE]); - private static final AionAddress emptyAddr = AionAddress.wrap(new byte[0]); - - private byte[] address; - private int hashCode = 0; - - public AionAddress(final byte[] in) { - - if (in == null) { - throw new IllegalArgumentException("Null input!"); - } - - if (in.length != SIZE && in.length != 0) { - throw new IllegalArgumentException(); - } - - setupData(in); - } - - public AionAddress(final ByteArrayWrapper in) { - - if (in == null) { - throw new IllegalArgumentException("Null input!"); - } - - byte[] data = in.getData(); - if (data == null || (data.length != SIZE && data.length != 0)) { - throw new IllegalArgumentException(); - } - - setupData(data); - } - - public AionAddress(final String in) { - - if (in == null) { - throw new IllegalArgumentException(); - } - - byte[] hexByte = ByteUtil.hexStringToBytes(in); - - if (hexByte.length != SIZE && hexByte.length != 0) { - throw new IllegalArgumentException(); - } - - setupData(hexByte); - } - - private void setupData(final byte[] in) { - this.address = in; - this.hashCode = Arrays.hashCode(in); - } - - public static AionAddress wrap(final byte[] addr) { - return new AionAddress(addr); - } - - public static AionAddress wrap(final String addr) { - return new AionAddress(addr); - } - - public static AionAddress wrap(final ByteArrayWrapper addr) { - return new AionAddress(addr); - } - - public final String toString() { - return ByteUtil.toHexString(address); - } - - public final ByteArrayWrapper toByteArrayWrapper() { - return ByteArrayWrapper.wrap(this.address); - } - - @Override - public final byte[] toBytes() { - return this.address; - } - - @Override - public final AionAddress clone() { - if (this.address.length == 0) { - return emptyAddr; - } else { - return new AionAddress(Arrays.copyOf(this.address, SIZE)); - } - } - - public boolean equals(Object other) { - if (!(other instanceof Address)) { - return false; - } else { - byte[] otherAddress = ((Address) other).toBytes(); - return Arrays.equals(this.address, otherAddress); - } - } - - public int hashCode() { - return this.hashCode; - } - - @Override - public int compareTo(AionAddress o) { - return Arrays.compare(this.address, o.toBytes()); - } - - public int compareTo(byte[] o) { - return Arrays.compare(this.address, o); - } - - @Override - public final AionAddress fromBytes(byte[] bs) { - return new AionAddress(bs); - } - - public static AionAddress ZERO_ADDRESS() { - return zeroAddr; - } - - public static AionAddress EMPTY_ADDRESS() { - return emptyAddr; - } - - public boolean isEmptyAddress() { - return Arrays.equals(address, emptyAddr.toBytes()); - } - - public boolean isZeroAddress() { - return Arrays.equals(address, zeroAddr.toBytes()); - } -} diff --git a/modAionBase/src/org/aion/base/type/Hash256.java b/modAionBase/src/org/aion/base/type/Hash256.java deleted file mode 100644 index 59f3c73ea8..0000000000 --- a/modAionBase/src/org/aion/base/type/Hash256.java +++ /dev/null @@ -1,166 +0,0 @@ -package org.aion.base.type; - -import java.util.Arrays; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.Bytesable; - -public final class Hash256 implements Comparable<Hash256>, Bytesable<Hash256>, Cloneable { - - public static final int BYTES = 32; - private static final Hash256 zeroHash = Hash256.wrap(new byte[BYTES]); - - private byte[] hash = new byte[BYTES]; - private int hashCode = 0; - - public Hash256(byte[] in) { - - if (in == null) { - throw new IllegalArgumentException("Null input!"); - } - - if (in.length != BYTES) { - throw new IllegalArgumentException(); - } - - setupData(in); - } - - public Hash256(String in) { - - if (in == null) { - throw new IllegalArgumentException("Null input!"); - } - - byte[] out = ByteUtil.hexStringToBytes(in); - - if (out.length != BYTES) { - throw new IllegalArgumentException(); - } - - setupData(out); - } - - public Hash256(final ByteArrayWrapper in) { - - if (in == null) { - throw new IllegalArgumentException("Null input!"); - } - - byte[] data = in.getData(); - if (data == null || data.length != BYTES) { - throw new IllegalArgumentException(); - } - - setupData(data); - } - - private void setupData(final byte[] in) { - this.hash = in; - this.hashCode = Arrays.hashCode(in); - } - - public static Hash256 wrap(final byte[] hash) { - return new Hash256(hash); - } - - public static Hash256 wrap(final String hash) { - return new Hash256(hash); - } - - public static Hash256 wrap(final ByteArrayWrapper hash) { - return new Hash256(hash); - } - - public final String toString() { - return ByteUtil.toHexString(hash); - } - - public final ByteArrayWrapper toByteArrayWrapper() { - return ByteArrayWrapper.wrap(this.hash); - } - - @Override - public final byte[] toBytes() { - return this.hash; - } - - @Override - public Hash256 clone() throws CloneNotSupportedException { - try { - return new Hash256(Arrays.copyOf(this.hash, BYTES)); - } catch (Exception e) { - throw new CloneNotSupportedException(e.toString()); - } - } - - public boolean equals(Object other) { - if (!(other instanceof Hash256)) { - return false; - } else { - byte[] otherAddress = ((Hash256) other).toBytes(); - return Arrays.compare(this.hash, otherAddress) == 0; - } - } - - public int hashCode() { - return this.hashCode; - } - - /** - * Compares this object with the specified object for order. Returns a negative integer, zero, - * or a positive integer as this object is less than, equal to, or greater than the specified - * object. - * - * <p> - * - * <p>The implementor must ensure {@code sgn(x.compareTo(y)) == -sgn(y.compareTo(x))} for all - * {@code x} and {@code y}. (This implies that {@code x.compareTo(y)} must throw an exception - * iff {@code y.compareTo(x)} throws an exception.) - * - * <p> - * - * <p>The implementor must also ensure that the relation is transitive: {@code (x.compareTo(y) > - * 0 && y.compareTo(z) > 0)} implies {@code x.compareTo(z) > 0}. - * - * <p> - * - * <p>Finally, the implementor must ensure that {@code x.compareTo(y)==0} implies that {@code - * sgn(x.compareTo(z)) == sgn(y.compareTo(z))}, for all {@code z}. - * - * <p> - * - * <p>It is strongly recommended, but <i>not</i> strictly required that {@code - * (x.compareTo(y)==0) == (x.equals(y))}. Generally speaking, any class that implements the - * {@code Comparable} interface and violates this condition should clearly indicate this fact. - * The recommended language is "Note: this class has a natural ordering that is inconsistent - * with equals." - * - * <p> - * - * <p>In the foregoing description, the notation {@code sgn(}<i>expression</i>{@code )} - * designates the mathematical <i>signum</i> function, which is defined to return one of {@code - * -1}, {@code 0}, or {@code 1} according to whether the value of <i>expression</i> is negative, - * zero, or positive, respectively. - * - * @param o the object to be compared. - * @return a negative integer, zero, or a positive integer as this object is less than, equal - * to, or greater than the specified object. - * @throws NullPointerException if the specified object is null - * @throws ClassCastException if the specified object's type prevents it from being compared to - * this object. - */ - @Override - public int compareTo(Hash256 o) { - return Arrays.compare(this.hash, o.toBytes()); - } - - @Override - public final Hash256 fromBytes(byte[] bs) { - return new Hash256(bs); - } - - public static final Hash256 ZERO_HASH() { - return zeroHash; - } -} diff --git a/modAionBase/src/org/aion/base/type/IBlock.java b/modAionBase/src/org/aion/base/type/IBlock.java deleted file mode 100644 index 63e71be8b9..0000000000 --- a/modAionBase/src/org/aion/base/type/IBlock.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * @Copyright Nuco Inc. 2016 - * @Author jin@nuco.io * - */ -package org.aion.base.type; - -import java.util.List; - -/** @author jin */ -public interface IBlock<TX extends ITransaction, BH extends IBlockHeader> { - - long getNumber(); - - byte[] getParentHash(); - - byte[] getHash(); - - byte[] getEncoded(); - - String getShortHash(); - - boolean isEqual(IBlock<TX, BH> block); - - String getShortDescr(); - - List<TX> getTransactionsList(); - - BH getHeader(); - - /** - * Newly added with the refactory of API for libNc, both chains should have implemented this - * - * @return - */ - byte[] getReceiptsRoot(); - - long getTimestamp(); -} diff --git a/modAionBase/src/org/aion/base/type/IBlockHeader.java b/modAionBase/src/org/aion/base/type/IBlockHeader.java deleted file mode 100644 index 7b7092737f..0000000000 --- a/modAionBase/src/org/aion/base/type/IBlockHeader.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.aion.base.type; - -import org.aion.vm.api.interfaces.Address; - -/** @author jay */ -public interface IBlockHeader { - - // Getter - byte[] getParentHash(); - - byte[] getStateRoot(); - - byte[] getTxTrieRoot(); - - byte[] getReceiptsRoot(); - - byte[] getLogsBloom(); - - byte[] getExtraData(); - - byte[] getNonce(); - - byte[] getHash(); - - byte[] getEncoded(); - - Address getCoinbase(); - - long getTimestamp(); - - long getNumber(); - - // Setter - void setCoinbase(Address _cb); - - void setStateRoot(byte[] _strt); - - void setReceiptsRoot(byte[] _rcrt); - - void setTransactionsRoot(byte[] _txrt); - - void setTimestamp(long _ts); - - void setNumber(long _nb); - - void setNonce(byte[] _nc); - - void setLogsBloom(byte[] _lb); - - void setExtraData(byte[] _ed); - - boolean isGenesis(); -} diff --git a/modAionBase/src/org/aion/base/type/IBlockIdentifier.java b/modAionBase/src/org/aion/base/type/IBlockIdentifier.java deleted file mode 100644 index bea6979de9..0000000000 --- a/modAionBase/src/org/aion/base/type/IBlockIdentifier.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.aion.base.type; - -/** @author jay */ -public interface IBlockIdentifier { - - byte[] getHash(); - - long getNumber(); -} diff --git a/modAionBase/src/org/aion/base/type/IBlockSummary.java b/modAionBase/src/org/aion/base/type/IBlockSummary.java deleted file mode 100644 index 8d300e3e92..0000000000 --- a/modAionBase/src/org/aion/base/type/IBlockSummary.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.aion.base.type; - -import java.util.List; - -/** @author jay */ -public interface IBlockSummary { - List<?> getReceipts(); - - IBlock getBlock(); -} diff --git a/modAionBase/src/org/aion/base/type/IPowBlockHeader.java b/modAionBase/src/org/aion/base/type/IPowBlockHeader.java deleted file mode 100644 index 85fbaf10e1..0000000000 --- a/modAionBase/src/org/aion/base/type/IPowBlockHeader.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.aion.base.type; - -import java.math.BigInteger; - -/** @author jay */ -public interface IPowBlockHeader extends IBlockHeader { - - byte[] getDifficulty(); - - BigInteger getDifficultyBI(); - - void setDifficulty(byte[] _diff); - - byte[] getPowBoundary(); -} diff --git a/modAionBase/src/org/aion/base/type/ISolution.java b/modAionBase/src/org/aion/base/type/ISolution.java deleted file mode 100644 index 04fd8797a1..0000000000 --- a/modAionBase/src/org/aion/base/type/ISolution.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.aion.base.type; - -/** Interface for any PoW and PoI miner solution. */ -public interface ISolution {} diff --git a/modAionBase/src/org/aion/base/type/ITransaction.java b/modAionBase/src/org/aion/base/type/ITransaction.java deleted file mode 100644 index 360993d84c..0000000000 --- a/modAionBase/src/org/aion/base/type/ITransaction.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.aion.base.type; - -import java.math.BigInteger; -import org.aion.vm.api.interfaces.TransactionInterface; - -/** @author jin */ -public interface ITransaction extends Cloneable, TransactionInterface { - - byte[] getEncoded(); - - BigInteger getNonceBI(); - - BigInteger getTimeStampBI(); - - ITransaction clone(); - - long getNrgConsume(); - - void setEncoded(byte[] _encodedData); - - void setNrgConsume(long _nrg); -} diff --git a/modAionBase/src/org/aion/base/type/ITxExecSummary.java b/modAionBase/src/org/aion/base/type/ITxExecSummary.java deleted file mode 100644 index 1589e0722b..0000000000 --- a/modAionBase/src/org/aion/base/type/ITxExecSummary.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.aion.base.type; - -import java.math.BigInteger; -import java.util.List; - -public interface ITxExecSummary { - - Object getBuilder(ITxReceipt receipt); - - boolean isRejected(); - - BigInteger getRefund(); - - BigInteger getFee(); - - ITxReceipt getReceipt(); - - List getLogs(); -} diff --git a/modAionBase/src/org/aion/base/type/ITxReceipt.java b/modAionBase/src/org/aion/base/type/ITxReceipt.java deleted file mode 100644 index 728d3a4c66..0000000000 --- a/modAionBase/src/org/aion/base/type/ITxReceipt.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.aion.base.type; - -import java.util.List; - -/** @author jay */ -public interface ITxReceipt<TX extends ITransaction, LOG> { - void setTransaction(TX tx); - - void setLogs(List<LOG> logs); - - void setNrgUsed(long nrg); - - void setExecutionResult(byte[] result); - - void setError(String error); -} diff --git a/modAionBase/src/org/aion/base/util/ByteArrayMap.java b/modAionBase/src/org/aion/base/util/ByteArrayMap.java deleted file mode 100644 index f707910339..0000000000 --- a/modAionBase/src/org/aion/base/util/ByteArrayMap.java +++ /dev/null @@ -1,132 +0,0 @@ -package org.aion.base.util; - -import java.util.Arrays; -import java.util.Map; - -/** - * hashmap class for byte[] -> byte[] mapping. - * - * @author jin - */ -public class ByteArrayMap extends HashMap<byte[], byte[]> { - - private static final long serialVersionUID = -5203317888679449508L; - transient ByteArrayNode[] table; - - public ByteArrayMap() { - super(); - } - - public ByteArrayMap(int cap) { - super(cap); - } - - @Override - protected int hash(Object key) { - if (key == null || !(key instanceof byte[])) { - return 0; - } - byte[] bs = (byte[]) key; - return Arrays.hashCode(bs); - } - - @Override - protected boolean keyEquals(Object key, byte[] k) { - if (!(key instanceof byte[])) { - return false; - } - return Arrays.equals((byte[]) key, k); - } - - @Override - protected boolean valEquals(Object val, byte[] v) { - if (!(val instanceof byte[])) { - return false; - } - return Arrays.equals((byte[]) val, v); - } - - @Override - protected Node<byte[], byte[]> newNode( - int hash, byte[] key, byte[] value, Node<byte[], byte[]> next) { - return new ByteArrayNode(hash, key, value, next); - } - - @Override - protected Node<byte[], byte[]> replacementNode( - Node<byte[], byte[]> p, Node<byte[], byte[]> next) { - return new ByteArrayNode(p.hash, p.key, p.value, next); - } - - @Override - protected TreeNode<byte[], byte[]> newTreeNode( - int hash, byte[] key, byte[] value, Node<byte[], byte[]> next) { - return new ByteArrayTreeNode(hash, key, value, next); - } - - @Override - protected TreeNode<byte[], byte[]> replacementTreeNode( - Node<byte[], byte[]> p, Node<byte[], byte[]> next) { - return new ByteArrayTreeNode(p.hash, p.key, p.value, next); - } - - static class ByteArrayNode extends Node<byte[], byte[]> { - - ByteArrayNode(int hash, byte[] key, byte[] value, Node<byte[], byte[]> next) { - super(hash, key, value, next); - } - - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - if (o instanceof Map.Entry) { - Map.Entry<?, ?> e = (Map.Entry<?, ?>) o; - if (Arrays.equals(key, (byte[]) e.getKey()) - && Arrays.equals(value, (byte[]) e.getValue())) { - return true; - } - } - return false; - } - } - - static class ByteArrayTreeNode extends TreeNode<byte[], byte[]> { - - ByteArrayTreeNode(int hash, byte[] key, byte[] value, Node<byte[], byte[]> next) { - super(hash, key, value, next); - } - - @Override - protected boolean keyEquals(Object key, byte[] k) { - if (!(key instanceof byte[])) { - return false; - } - return Arrays.equals((byte[]) key, k); - } - - @Override - protected boolean valEquals(Object val, byte[] v) { - if (!(val instanceof byte[])) { - return false; - } - return Arrays.equals((byte[]) val, v); - } - - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - if (o instanceof Map.Entry) { - Map.Entry<?, ?> e = (Map.Entry<?, ?>) o; - if (Arrays.equals(key, (byte[]) e.getKey()) - && Arrays.equals(value, (byte[]) e.getValue())) { - return true; - } - } - return false; - } - } -} diff --git a/modAionBase/src/org/aion/base/util/ByteArrayWrapper.java b/modAionBase/src/org/aion/base/util/ByteArrayWrapper.java deleted file mode 100644 index 441d55213d..0000000000 --- a/modAionBase/src/org/aion/base/util/ByteArrayWrapper.java +++ /dev/null @@ -1,108 +0,0 @@ -package org.aion.base.util; - -import java.io.Serializable; -import java.util.Arrays; - -public class ByteArrayWrapper - implements Comparable<ByteArrayWrapper>, Serializable, Bytesable<ByteArrayWrapper> { - - private static final long serialVersionUID = -2937011296133778157L; - private final byte[] data; - private int hashCode = 0; - - public ByteArrayWrapper(byte[] data) { - if (data == null) { - throw new NullPointerException("Data must not be null"); - } - this.data = data; - this.hashCode = Arrays.hashCode(data); - } - - /** Constructor based on the hex representation of the byte array. */ - public ByteArrayWrapper(String data) { - if (data == null) { - throw new NullPointerException("Data must not be null"); - } - this.data = Hex.decode(data); - this.hashCode = Arrays.hashCode(this.data); - } - - public boolean equals(Object other) { - if (!(other instanceof ByteArrayWrapper)) { - return false; - } - - return Arrays.equals(data, ((ByteArrayWrapper) other).getData()); - } - - @Override - public int hashCode() { - return hashCode; - } - - @Override - public int compareTo(ByteArrayWrapper o) { - return Arrays.compare(data, o.getData()); - } - - public byte[] getData() { - return data; - } - - @Override - public String toString() { - return Hex.toHexString(data); - } - - // toBytes() and getData() have identical functionality - @Override - public byte[] toBytes() { - return data; - } - - @Override - public ByteArrayWrapper fromBytes(byte[] bs) { - return new ByteArrayWrapper(bs); - } - - public static ByteArrayWrapper wrap(byte[] data) { - return new ByteArrayWrapper(data); - } - - /** - * Checks if every byte in the array has the value 0. - * - * @return {@code true} if every byte in the array has the value 0, {@code false} otherwise - */ - public boolean isZero() { - int length = data.length; - for (int i = 0; i < length; i++) { - if (data[length - 1 - i] != 0) { - return false; - } - } - return true; - } - - /** - * Checks if the stored byte array is empty. - * - * @return {@code true} if empty, {@code false} otherwise - */ - public boolean isEmpty() { - return data.length == 0; - } - - public ByteArrayWrapper copy() { - int length = data.length; - byte[] bs = new byte[length]; - System.arraycopy(data, 0, bs, 0, length); - return new ByteArrayWrapper(bs); - } - - public static final ByteArrayWrapper ZERO = ByteArrayWrapper.wrap(new byte[] {0}); - - public byte[] getNoLeadZeroesData() { - return ByteUtil.stripLeadingZeroes(data); - } -} diff --git a/modAionBase/src/org/aion/base/util/ByteUtil.java b/modAionBase/src/org/aion/base/util/ByteUtil.java deleted file mode 100644 index ddfa984950..0000000000 --- a/modAionBase/src/org/aion/base/util/ByteUtil.java +++ /dev/null @@ -1,704 +0,0 @@ -package org.aion.base.util; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -/** @apiNote This functionality is migrated to modUtil. Use that class instead. */ -@Deprecated -public class ByteUtil { - - public static final byte[] EMPTY_WORD = new byte[32]; - public static final byte[] EMPTY_HALFWORD = new byte[16]; - public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; - public static final byte[] ZERO_BYTE_ARRAY = new byte[] {0}; - public static final String EMPTY_STRING = ""; - - /** Creates a copy of bytes and appends b to the end of it */ - public static byte[] appendByte(byte[] bytes, byte b) { - byte[] result = Arrays.copyOf(bytes, bytes.length + 1); - result[result.length - 1] = b; - return result; - } - - /** - * The regular {@link java.math.BigInteger#toByteArray()} method isn't quite what we often need: - * it appends a leading zero to indicate that the number is positive and may need padding. - * - * @param b the integer to format into a byte array - * @param numBytes the desired size of the resulting byte array - * @return numBytes byte long array. - */ - public static byte[] bigIntegerToBytes(BigInteger b, int numBytes) { - if (b == null) { - return null; - } - byte[] bytes = new byte[numBytes]; - byte[] biBytes = b.toByteArray(); - int start = (biBytes.length == numBytes + 1) ? 1 : 0; - int length = Math.min(biBytes.length, numBytes); - System.arraycopy(biBytes, start, bytes, numBytes - length, length); - return bytes; - } - - public static byte[] bigIntegerToBytesSigned(BigInteger b, int numBytes) { - if (b == null) { - return null; - } - byte[] bytes = new byte[numBytes]; - Arrays.fill(bytes, b.signum() < 0 ? (byte) 0xFF : 0x00); - byte[] biBytes = b.toByteArray(); - int start = (biBytes.length == numBytes + 1) ? 1 : 0; - int length = Math.min(biBytes.length, numBytes); - System.arraycopy(biBytes, start, bytes, numBytes - length, length); - return bytes; - } - - /** - * Omitting sign indication byte. <br> - * <br> - * Instead of {@link org.spongycastle.util.BigIntegers#asUnsignedByteArray(BigInteger)} <br> - * we use this custom method to avoid an empty array in case of BigInteger.ZERO - * - * @param value - any big integer number. A <code>null</code>-value will return <code>null - * </code> - * @return A byte array without a leading zero byte if present in the signed encoding. - * BigInteger.ZERO will return an array with length 1 and byte-value 0. - */ - public static byte[] bigIntegerToBytes(BigInteger value) { - if (value == null) { - return null; - } - - byte[] data = value.toByteArray(); - - if (data.length != 1 && data[0] == 0) { - byte[] tmp = new byte[data.length - 1]; - System.arraycopy(data, 1, tmp, 0, tmp.length); - data = tmp; - } - return data; - } - - public static BigInteger bytesToBigInteger(byte[] bb) { - return bb.length == 0 ? BigInteger.ZERO : new BigInteger(1, bb); - } - - /** - * Returns the amount of nibbles that match each other from 0 ... amount will never be larger - * than smallest input - * - * @param a - first input - * @param b - second input - * @return Number of bytes that match - */ - public static int matchingNibbleLength(byte[] a, byte[] b) { - int i = 0; - int length = a.length < b.length ? a.length : b.length; - while (i < length) { - if (a[i] != b[i]) { - return i; - } - i++; - } - return i; - } - - /** Perform an in-place conversion of a byte array from big endian to little endian. */ - public static void toLEByteArray(byte[] toConvert) { - - if (toConvert == null) { - return; - } - - for (int i = 0; i < toConvert.length / 2; i++) { - byte temp = toConvert[i]; - toConvert[i] = toConvert[toConvert.length - 1 - i]; - toConvert[toConvert.length - 1 - i] = temp; - } - } - - /** - * Converts a long value into a byte array. - * - * @param val - long value to convert - * @return <code>byte[]</code> of length 8, representing the long value - */ - public static byte[] longToBytes(long val) { - return ByteBuffer.allocate(8).putLong(val).array(); - } - - public static byte[] longToBytesLE(long val) { - ByteBuffer bb = ByteBuffer.allocate(8); - bb.order(ByteOrder.LITTLE_ENDIAN); - return bb.putLong(val).array(); - } - - /** - * Converts a long value into a byte array. - * - * @param val - long value to convert - * @return decimal value with leading byte that are zeroes striped - */ - public static byte[] longToBytesNoLeadZeroes(long val) { - - // todo: improve performance by while strip numbers until (long >> 8 == - // 0) - if (val == 0) { - return EMPTY_BYTE_ARRAY; - } - - byte[] data = ByteBuffer.allocate(8).putLong(val).array(); - - return stripLeadingZeroes(data); - } - - /** - * Converts int value into a byte array. - * - * @param val - int value to convert - * @return <code>byte[]</code> of length 4, representing the int value - */ - public static byte[] intToBytes(int val) { - return ByteBuffer.allocate(4).putInt(val).array(); - } - - /** - * Converts and int value to a byte array (Little Endian) @ param val - * - * @return <code>byte[]</code> of length 4; representing the int value in LE order - */ - public static byte[] intToBytesLE(int val) { - ByteBuffer bb = ByteBuffer.allocate(4); - bb.order(ByteOrder.LITTLE_ENDIAN); - return bb.putInt(val).array(); - } - - /** - * Converts and int value to a byte array (Big Endian) @ param val - * - * @return <code>byte[]</code> of length 4; representing the int value in LE order - */ - public static byte[] intToBytesBE(int val) { - ByteBuffer bb = ByteBuffer.allocate(4); - bb.order(ByteOrder.BIG_ENDIAN); - return bb.putInt(val).array(); - } - - /** - * Converts a int value into a byte array. - * - * @param val - int value to convert - * @return value with leading byte that are zeroes striped - */ - public static byte[] intToBytesNoLeadZeroes(int val) { - - if (val == 0) { - return EMPTY_BYTE_ARRAY; - } - - int lenght = 0; - - int tmpVal = val; - while (tmpVal != 0) { - tmpVal = tmpVal >>> 8; - ++lenght; - } - - byte[] result = new byte[lenght]; - - int index = result.length - 1; - while (val != 0) { - - result[index] = (byte) (val & 0xFF); - val = val >>> 8; - index -= 1; - } - - return result; - } - - /** - * Convert a byte-array into a hex String.<br> - * Works similar to {@link Hex#toHexString} but allows for <code>null</code> - * - * @param data - byte-array to convert to a hex-string - * @return hex representation of the data.<br> - * Returns an empty String if the input is <code>null</code> TODO: swap out with more - * efficient implementation, for now seems like we are stuck with this - * @see Hex#toHexString - */ - public static String toHexString(byte[] data) { - return data == null ? "" : Hex.toHexString(data); - } - - public static String toHexStringWithPrefix(byte[] data) { - return "0x" + toHexString(data); - } - - /** - * Calculate packet length - * - * @param msg byte[] - * @return byte-array with 4 elements - */ - public static byte[] calcPacketLength(byte[] msg) { - int msgLen = msg.length; - return new byte[] { - (byte) ((msgLen >> 24) & 0xFF), - (byte) ((msgLen >> 16) & 0xFF), - (byte) ((msgLen >> 8) & 0xFF), - (byte) ((msgLen) & 0xFF) - }; - } - - /** - * Cast hex encoded value from byte[] to int - * - * <p>Limited to Integer.MAX_VALUE: 2^31-1 (4 bytes) - * - * @param b array contains the values - * @return unsigned positive int value. - */ - public static int byteArrayToInt(byte[] b) { - if (b == null || b.length == 0) { - return 0; - } - return new BigInteger(1, b).intValue(); - } - - /** - * Cast hex encoded value from byte[] to long - * - * <p>Limited to Long.MAX_VALUE: 2^63-1 (8 bytes) - * - * @param b array contains the values - * @return unsigned positive long value. - */ - public static long byteArrayToLong(byte[] b) { - if (b == null || b.length == 0) { - return 0; - } - return new BigInteger(1, b).longValue(); - } - - /** Used in conjunction w/ RLP, casts to byte */ - public static byte toByte(byte[] b) { - if (b == null || b.length == 0) { - return 0; - } - return b[0]; - } - - /** - * Turn nibbles to a pretty looking output string - * - * <p>Example. [ 1, 2, 3, 4, 5 ] becomes '\x11\x23\x45' - * - * @param nibbles - getting byte of data [ 04 ] and turning it to a '\x04' representation - * @return pretty string of nibbles - */ - public static String nibblesToPrettyString(byte[] nibbles) { - StringBuilder builder = new StringBuilder(); - for (byte nibble : nibbles) { - final String nibbleString = oneByteToHexString(nibble); - builder.append("\\x").append(nibbleString); - } - return builder.toString(); - } - - public static String oneByteToHexString(byte value) { - String retVal = Integer.toString(value & 0xFF, 16); - if (retVal.length() == 1) { - retVal = "0" + retVal; - } - return retVal; - } - - /** - * Calculate the number of bytes need to encode the number - * - * @param val - number - * @return number of min bytes used to encode the number - */ - public static int numBytes(String val) { - - BigInteger bInt = new BigInteger(val); - int bytes = 0; - - while (!bInt.equals(BigInteger.ZERO)) { - bInt = bInt.shiftRight(8); - ++bytes; - } - if (bytes == 0) { - ++bytes; - } - return bytes; - } - - /** - * @param arg - not more that 32 bits - * @return - bytes of the value pad with complete to 32 zeroes - */ - public static byte[] encodeValFor32Bits(Object arg) { - - byte[] data; - - // check if the string is numeric - if (arg.toString().trim().matches("-?\\d+(\\.\\d+)?")) { - data = new BigInteger(arg.toString().trim()).toByteArray(); - } // check if it's hex number - else if (arg.toString().trim().matches("0[xX][0-9a-fA-F]+")) { - data = new BigInteger(arg.toString().trim().substring(2), 16).toByteArray(); - } else { - data = arg.toString().trim().getBytes(); - } - - if (data.length > 32) { - throw new RuntimeException("values can't be more than 32 byte"); - } - - byte[] val = new byte[32]; - - int j = 0; - for (int i = data.length; i > 0; --i) { - val[31 - j] = data[i - 1]; - ++j; - } - return val; - } - - /** - * encode the values and concatenate together - * - * @param args Object - * @return byte[] - */ - public static byte[] encodeDataList(Object... args) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - for (Object arg : args) { - byte[] val = encodeValFor32Bits(arg); - try { - baos.write(val); - } catch (IOException e) { - throw new Error("Happen something that should never happen ", e); - } - } - return baos.toByteArray(); - } - - public static int firstNonZeroByte(byte[] data) { - for (int i = 0; i < data.length; ++i) { - if (data[i] != 0) { - return i; - } - } - return -1; - } - - public static byte[] stripLeadingZeroes(byte[] data) { - - if (data == null) { - return null; - } - - final int firstNonZero = firstNonZeroByte(data); - switch (firstNonZero) { - case -1: - return ZERO_BYTE_ARRAY; - - case 0: - return data; - - default: - byte[] result = new byte[data.length - firstNonZero]; - System.arraycopy(data, firstNonZero, result, 0, data.length - firstNonZero); - - return result; - } - } - - /** - * increment byte array as a number until max is reached - * - * @param bytes byte[] - * @return boolean - */ - public static boolean increment(byte[] bytes) { - final int startIndex = 0; - int i; - for (i = bytes.length - 1; i >= startIndex; i--) { - bytes[i]++; - if (bytes[i] != 0) { - break; - } - } - // we return false when all bytes are 0 again - return (i >= startIndex || bytes[startIndex] != 0); - } - - /** - * Utility function to copy a byte array into a new byte array with given size. If the src - * length is smaller than the given size, the result will be left-padded with zeros. - * - * @param value - a BigInteger with a maximum value of 2^256-1 - * @return Byte array of given size with a copy of the <code>src</code> - */ - public static byte[] copyToArray(BigInteger value) { - byte[] src = ByteUtil.bigIntegerToBytes(value); - byte[] dest = ByteBuffer.allocate(32).array(); - System.arraycopy(src, 0, dest, dest.length - src.length, src.length); - return dest; - } - - public static byte[] setBit(byte[] data, int pos, int val) { - - if ((data.length * 8) - 1 < pos) { - throw new Error("outside byte array limit, pos: " + pos); - } - - int posByte = data.length - 1 - (pos) / 8; - int posBit = (pos) % 8; - byte setter = (byte) (1 << (posBit)); - byte toBeSet = data[posByte]; - byte result; - if (val == 1) { - result = (byte) (toBeSet | setter); - } else { - result = (byte) (toBeSet & ~setter); - } - - data[posByte] = result; - return data; - } - - public static int getBit(byte[] data, int pos) { - - if ((data.length * 8) - 1 < pos) { - throw new Error("outside byte array limit, pos: " + pos); - } - - int posByte = data.length - 1 - pos / 8; - int posBit = pos % 8; - byte dataByte = data[posByte]; - return Math.min(1, (dataByte & (1 << (posBit)))); - } - - public static byte[] and(byte[] b1, byte[] b2) { - if (b1.length != b2.length) { - throw new RuntimeException("Array sizes differ"); - } - byte[] ret = new byte[b1.length]; - for (int i = 0; i < ret.length; i++) { - ret[i] = (byte) (b1[i] & b2[i]); - } - return ret; - } - - public static byte[] or(byte[] b1, byte[] b2) { - if (b1.length != b2.length) { - throw new RuntimeException("Array sizes differ"); - } - byte[] ret = new byte[b1.length]; - for (int i = 0; i < ret.length; i++) { - ret[i] = (byte) (b1[i] | b2[i]); - } - return ret; - } - - public static byte[] xor(byte[] b1, byte[] b2) { - if (b1.length != b2.length) { - throw new RuntimeException("Array sizes differ"); - } - byte[] ret = new byte[b1.length]; - for (int i = 0; i < ret.length; i++) { - ret[i] = (byte) (b1[i] ^ b2[i]); - } - return ret; - } - - /** - * XORs byte arrays of different lengths by aligning length of the shortest via adding zeros at - * beginning - */ - public static byte[] xorAlignRight(byte[] b1, byte[] b2) { - if (b1.length > b2.length) { - byte[] b2_ = new byte[b1.length]; - System.arraycopy(b2, 0, b2_, b1.length - b2.length, b2.length); - b2 = b2_; - } else if (b2.length > b1.length) { - byte[] b1_ = new byte[b2.length]; - System.arraycopy(b1, 0, b1_, b2.length - b1.length, b1.length); - b1 = b1_; - } - - return xor(b1, b2); - } - - /** - * @param arrays - arrays to merge - * @return - merged array - */ - public static byte[] merge(byte[]... arrays) { - int count = 0; - for (byte[] array : arrays) { - count += array.length; - } - - // Create new array and copy all array contents - byte[] mergedArray = new byte[count]; - int start = 0; - for (byte[] array : arrays) { - System.arraycopy(array, 0, mergedArray, start, array.length); - start += array.length; - } - return mergedArray; - } - - public static boolean isNullOrZeroArray(byte[] array) { - return (array == null) || (array.length == 0); - } - - public static boolean isSingleZero(byte[] array) { - return (array.length == 1 && array[0] == 0); - } - - public static Set<byte[]> difference(Set<byte[]> setA, Set<byte[]> setB) { - - Set<byte[]> result = new HashSet<>(); - - for (byte[] elementA : setA) { - boolean found = false; - for (byte[] elementB : setB) { - - if (Arrays.equals(elementA, elementB)) { - found = true; - break; - } - } - if (!found) { - result.add(elementA); - } - } - - return result; - } - - public static int length(byte[]... bytes) { - int result = 0; - for (byte[] array : bytes) { - result += (array == null) ? 0 : array.length; - } - return result; - } - - public static byte[] intsToBytes(int[] arr, boolean bigEndian) { - byte[] ret = new byte[arr.length * 4]; - intsToBytes(arr, ret, bigEndian); - return ret; - } - - public static int[] bytesToInts(byte[] arr, boolean bigEndian) { - int[] ret = new int[arr.length / 4]; - bytesToInts(arr, ret, bigEndian); - return ret; - } - - public static void bytesToInts(byte[] b, int[] arr, boolean bigEndian) { - if (!bigEndian) { - int off = 0; - for (int i = 0; i < arr.length; i++) { - int ii = b[off++] & 0x000000FF; - ii |= (b[off++] << 8) & 0x0000FF00; - ii |= (b[off++] << 16) & 0x00FF0000; - ii |= (b[off++] << 24); - arr[i] = ii; - } - } else { - int off = 0; - for (int i = 0; i < arr.length; i++) { - int ii = b[off++] << 24; - ii |= (b[off++] << 16) & 0x00FF0000; - ii |= (b[off++] << 8) & 0x0000FF00; - ii |= b[off++] & 0x000000FF; - arr[i] = ii; - } - } - } - - public static void intsToBytes(int[] arr, byte[] b, boolean bigEndian) { - if (!bigEndian) { - int off = 0; - for (int i = 0; i < arr.length; i++) { - int ii = arr[i]; - b[off++] = (byte) (ii & 0xFF); - b[off++] = (byte) ((ii >> 8) & 0xFF); - b[off++] = (byte) ((ii >> 16) & 0xFF); - b[off++] = (byte) ((ii >> 24) & 0xFF); - } - } else { - int off = 0; - for (int i = 0; i < arr.length; i++) { - int ii = arr[i]; - b[off++] = (byte) ((ii >> 24) & 0xFF); - b[off++] = (byte) ((ii >> 16) & 0xFF); - b[off++] = (byte) ((ii >> 8) & 0xFF); - b[off++] = (byte) (ii & 0xFF); - } - } - } - - public static short bigEndianToShort(byte[] bs) { - return bigEndianToShort(bs, 0); - } - - public static short bigEndianToShort(byte[] bs, int off) { - int n = bs[off] << 8; - ++off; - n |= bs[off] & 0xFF; - return (short) n; - } - - public static byte[] shortToBytes(short n) { - return ByteBuffer.allocate(2).putShort(n).array(); - } - - /** - * Converts string hex representation to data bytes Accepts following hex: - with or without 0x - * prefix - with no leading 0, like 0xabc -> 0x0abc - * - * @param data String like '0xa5e..' or just 'a5e..' - * @return decoded bytes array - */ - public static byte[] hexStringToBytes(String data) { - if (data == null) { - return EMPTY_BYTE_ARRAY; - } - if (data.startsWith("0x")) { - data = data.substring(2); - } - if (data.length() % 2 == 1) { - data = "0" + data; - } - return Hex.decode(data); - } - - /** - * Chops a 32-byte value into a 16-byte value. Keep in mind the subtlety that a "chopped" - * bytearray is a different reference from a "unchopped" bytearray, so make no assummptions as - * to whether this function news the element. - * - * @return 16-byte value representing the LOWER portion of the original - */ - public static byte[] chop(byte[] in) { - if (in.length <= 16) { - return in; - } - return Arrays.copyOfRange(in, in.length - 16, in.length); - } -} diff --git a/modAionBase/src/org/aion/base/util/Bytesable.java b/modAionBase/src/org/aion/base/util/Bytesable.java deleted file mode 100644 index ae6bc4189a..0000000000 --- a/modAionBase/src/org/aion/base/util/Bytesable.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.aion.base.util; - -/** @author jin */ -public interface Bytesable<T> { - - byte[] NULL_BYTE = new byte[] {(byte) 0x0}; - - byte[] toBytes(); - - T fromBytes(byte[] bs); -} diff --git a/modAionBase/src/org/aion/base/util/Copyable.java b/modAionBase/src/org/aion/base/util/Copyable.java deleted file mode 100644 index 5a3dc0daec..0000000000 --- a/modAionBase/src/org/aion/base/util/Copyable.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.aion.base.util; - -/** - * Alternative to Cloneable from java read: http://www.artima.com/intv/bloch13.html - * - * @author yao - * @param <T> - */ -public interface Copyable<T> { - T copy(); -} diff --git a/modAionBase/src/org/aion/base/util/FastByteComparisons.java b/modAionBase/src/org/aion/base/util/FastByteComparisons.java deleted file mode 100644 index 1cbfb79740..0000000000 --- a/modAionBase/src/org/aion/base/util/FastByteComparisons.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.aion.base.util; - -import java.util.Arrays; - -public final class FastByteComparisons { - /** - * Check if two byte arrays are equal. - * - * @param array1 - * @param array2 - * @return - */ - public static boolean equal(byte[] array1, byte[] array2) { - return Arrays.equals(array1, array2); - } - - /** - * Compares two byte arrays. - * - * @param array1 - * @param array2 - * @return - */ - public static int compareTo(byte[] array1, byte[] array2) { - return Arrays.compare(array1, array2); - } - - /** - * Compares two regions of byte array. - * - * @param array1 - * @param offset1 - * @param size1 - * @param array2 - * @param offset2 - * @param size2 - * @return - */ - public static int compareTo( - byte[] array1, int offset1, int size1, byte[] array2, int offset2, int size2) { - byte[] b1 = Arrays.copyOfRange(array1, offset1, offset1 + size1); - byte[] b2 = Arrays.copyOfRange(array2, offset2, offset2 + size2); - - return Arrays.compare(b1, b2); - } -} diff --git a/modAionBase/src/org/aion/base/util/Functional.java b/modAionBase/src/org/aion/base/util/Functional.java deleted file mode 100644 index 1bfd9fb3eb..0000000000 --- a/modAionBase/src/org/aion/base/util/Functional.java +++ /dev/null @@ -1,82 +0,0 @@ -package org.aion.base.util; - -public interface Functional { - - /** - * Represents an operation that accepts a single input argument and returns no result. Unlike - * most other functional interfaces, {@code Consumer} is expected to operate via side-effects. - * - * @param <T> the type of the input to the operation - */ - interface Consumer<T> { - - /** - * Performs this operation on the given argument. - * - * @param t the input argument - */ - void accept(T t); - } - - /** - * Represents an operation that accepts two input arguments and returns no result. This is the - * two-arity specialization of {@link java.util.function.Consumer}. Unlike most other functional - * interfaces, {@code BiConsumer} is expected to operate via side-effects. - * - * @param <T> the type of the first argument to the operation - * @param <U> the type of the second argument to the operation - * @see org.ethereum.util.Functional.Consumer - */ - interface BiConsumer<T, U> { - - /** - * Performs this operation on the given arguments. - * - * @param t the first input argument - * @param u the second input argument - */ - void accept(T t, U u); - } - - /** - * Represents a function that accepts one argument and produces a result. - * - * @param <T> the type of the input to the function - * @param <R> the type of the result of the function - */ - interface Function<T, R> { - - /** - * Applies this function to the given argument. - * - * @param t the function argument - * @return the function result - */ - R apply(T t); - } - - interface Supplier<T> { - - /** - * Gets a result. - * - * @return a result - */ - T get(); - } - - interface InvokeWrapper { - - void invoke(); - } - - interface InvokeWrapperWithResult<R> { - - R invoke(); - } - - interface Predicate<T> { - - boolean test(T t); - } -} diff --git a/modAionBase/src/org/aion/base/util/Hex.java b/modAionBase/src/org/aion/base/util/Hex.java deleted file mode 100644 index 0fb02b3ae4..0000000000 --- a/modAionBase/src/org/aion/base/util/Hex.java +++ /dev/null @@ -1,109 +0,0 @@ -package org.aion.base.util; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -/** - * Utility class for converting hex data to bytes and back again. - * - * @apiNote This functionality is migrated to modUtil. Use that class instead. - */ -@Deprecated -public class Hex { - - private static final HexEncoder encoder = new HexEncoder(); - - public static String toHexString(byte[] data) { - return toHexString(data, 0, data.length); - } - - public static String toHexString(byte[] data, int off, int length) { - byte[] encoded = encode(data, off, length); - return new String(encoded); - } - - /** - * encode the input data producing a Hex encoded byte array. - * - * @return a byte array containing the Hex encoded data. - */ - public static byte[] encode(byte[] data) { - return encode(data, 0, data.length); - } - - /** - * encode the input data producing a Hex encoded byte array. - * - * @return a byte array containing the Hex encoded data. - */ - public static byte[] encode(byte[] data, int off, int length) { - ByteArrayOutputStream bOut = new ByteArrayOutputStream(); - - encoder.encode(data, off, length, bOut); - - return bOut.toByteArray(); - } - - /** - * Hex encode the byte data writing it to the given output stream. - * - * @return the number of bytes produced. - */ - public static int encode(byte[] data, OutputStream out) throws IOException { - return encoder.encode(data, 0, data.length, out); - } - - /** - * Hex encode the byte data writing it to the given output stream. - * - * @return the number of bytes produced. - */ - public static int encode(byte[] data, int off, int length, OutputStream out) - throws IOException { - return encoder.encode(data, off, length, out); - } - - /** - * decode the Hex encoded input data. It is assumed the input data is valid. - * - * @return a byte array representing the decoded data. - */ - public static byte[] decode(byte[] data) { - ByteArrayOutputStream bOut = new ByteArrayOutputStream(); - try { - encoder.decode(data, 0, data.length, bOut); - } catch (IOException e) { - System.err.println("Hex decode failed! " + data); - return null; - } - - return bOut.toByteArray(); - } - - /** - * decode the Hex encoded String data - whitespace will be ignored. - * - * @return a byte array representing the decoded data. - */ - public static byte[] decode(String data) { - ByteArrayOutputStream bOut = new ByteArrayOutputStream(); - try { - encoder.decode(data, bOut); - } catch (IOException e) { - System.err.println("Hex decode failed! " + data); - return null; - } - return bOut.toByteArray(); - } - - /** - * decode the Hex encoded String data writing it to the given output stream, whitespace - * characters will be ignored. - * - * @return the number of bytes produced. - */ - public static int decode(String data, OutputStream out) throws IOException { - return encoder.decode(data, out); - } -} diff --git a/modAionBase/src/org/aion/base/util/HexEncoder.java b/modAionBase/src/org/aion/base/util/HexEncoder.java deleted file mode 100644 index f8d688f406..0000000000 --- a/modAionBase/src/org/aion/base/util/HexEncoder.java +++ /dev/null @@ -1,164 +0,0 @@ -package org.aion.base.util; - -import java.io.IOException; -import java.io.OutputStream; - -/** - * A streaming Hex encoder. - * - * @apiNote This functionality is migrated to modUtil. Use that class instead. - */ -@Deprecated -public class HexEncoder { - - protected final byte[] encodingTable = { - (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', - (byte) '7', - (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', - (byte) 'f' - }; - - /* - * set up the decoding table. - */ - protected final byte[] decodingTable = new byte[128]; - - protected void initialiseDecodingTable() { - for (int i = 0; i < decodingTable.length; i++) { - decodingTable[i] = (byte) 0xff; - } - - for (int i = 0; i < encodingTable.length; i++) { - decodingTable[encodingTable[i]] = (byte) i; - } - - decodingTable['A'] = decodingTable['a']; - decodingTable['B'] = decodingTable['b']; - decodingTable['C'] = decodingTable['c']; - decodingTable['D'] = decodingTable['d']; - decodingTable['E'] = decodingTable['e']; - decodingTable['F'] = decodingTable['f']; - } - - public HexEncoder() { - initialiseDecodingTable(); - } - - /** - * encode the input data producing a Hex output stream. - * - * @return the number of bytes produced. - */ - public int encode(byte[] data, int off, int length, OutputStream out) { - for (int i = off; i < (off + length); i++) { - int v = data[i] & 0xff; - try { - out.write(encodingTable[(v >>> 4)]); - out.write(encodingTable[v & 0xf]); - } catch (Exception e) { - } - } - - return length * 2; - } - - private static boolean ignore(char c) { - return c == '\n' || c == '\r' || c == '\t' || c == ' '; - } - - /** - * decode the Hex encoded byte data writing it to the given output stream, whitespace characters - * will be ignored. - * - * @return the number of bytes produced. - */ - public int decode(byte[] data, int off, int length, OutputStream out) throws IOException { - byte b1, b2; - int outLen = 0; - - int end = off + length; - - while (end > off) { - if (!ignore((char) data[end - 1])) { - break; - } - - end--; - } - - int i = off; - while (i < end) { - while (i < end && ignore((char) data[i])) { - i++; - } - - b1 = decodingTable[data[i++]]; - - while (i < end && ignore((char) data[i])) { - i++; - } - - b2 = decodingTable[data[i++]]; - - if ((b1 | b2) < 0) { - throw new IOException("invalid characters encountered in Hex data"); - } - - try { - out.write((b1 << 4) | b2); - } catch (Exception e) { - - } - - outLen++; - } - - return outLen; - } - - /** - * decode the Hex encoded String data writing it to the given output stream, whitespace - * characters will be ignored. - * - * @return the number of bytes produced. - */ - public int decode(String data, OutputStream out) throws IOException { - byte b1, b2; - int length = 0; - - int end = data.length(); - - while (end > 0) { - if (!ignore(data.charAt(end - 1))) { - break; - } - - end--; - } - - int i = 0; - while (i < end) { - while (i < end && ignore(data.charAt(i))) { - i++; - } - - b1 = decodingTable[data.charAt(i++)]; - - while (i < end && ignore(data.charAt(i))) { - i++; - } - - b2 = decodingTable[data.charAt(i++)]; - - if ((b1 | b2) < 0) { - throw new IOException("invalid characters encountered in Hex string"); - } - - out.write((b1 << 4) | b2); - - length++; - } - - return length; - } -} diff --git a/modAionBase/src/org/aion/base/util/ImmutableByteArrayWrapper.java b/modAionBase/src/org/aion/base/util/ImmutableByteArrayWrapper.java deleted file mode 100644 index 5262341723..0000000000 --- a/modAionBase/src/org/aion/base/util/ImmutableByteArrayWrapper.java +++ /dev/null @@ -1,92 +0,0 @@ -package org.aion.base.util; - -import java.util.Arrays; - -/** - * Immutable byte array wrapper used when storing keys inside HashMap, this way we guarantee that - * keys inside HashMap never change - * - * @author yao - */ -public class ImmutableByteArrayWrapper implements Comparable<ImmutableByteArrayWrapper> { - protected byte[] data; - protected int hashCode; - - /** For us to be able to create MutableByteArrayObserver */ - protected ImmutableByteArrayWrapper() { - data = null; - hashCode = 0; - } - - public ImmutableByteArrayWrapper(byte[] data) { - if (data == null) throw new NullPointerException("data cannot be null"); - this.data = new byte[data.length]; - System.arraycopy(data, 0, this.data, 0, data.length); - this.hashCode = Arrays.hashCode(this.data); - } - - public ByteArrayWrapper toByteArrayWrapper() { - byte[] d = new byte[data.length]; - System.arraycopy(this.data, 0, d, 0, this.data.length); - return new ByteArrayWrapper(d); - } - - public byte[] getData() { - byte[] d = new byte[data.length]; - System.arraycopy(this.data, 0, d, 0, this.data.length); - return d; - } - - /** - * Utility constructor, for us to easily convert ByteArrayWrapper over to immutable form - * - * @param other - */ - public ImmutableByteArrayWrapper(ByteArrayWrapper other) { - this(other.getData()); - } - - /** - * Copy constructor - * - * @param other - */ - public ImmutableByteArrayWrapper(ImmutableByteArrayWrapper other) { - this(other.data); - } - - /** Allow comparisons between both ByteArrayWrapper and ImmutableByteArrayWrapper */ - @Override - public boolean equals(Object other) { - if (!(other instanceof ByteArrayWrapper) && !(other instanceof ImmutableByteArrayWrapper)) { - return false; - } - - byte[] otherData = null; - if (other instanceof ByteArrayWrapper) otherData = ((ByteArrayWrapper) other).getData(); - - if (other instanceof ImmutableByteArrayWrapper) - otherData = ((ImmutableByteArrayWrapper) other).data; - - // probably impossible, but be safe - if (otherData == null) return false; - - return Arrays.compare(data, otherData) == 0; - } - - @Override - public int hashCode() { - return hashCode; - } - - @Override - public String toString() { - return ByteUtil.toHexString(this.data); - } - - /** TODO: what happens when one is null and the other is not? */ - @Override - public int compareTo(ImmutableByteArrayWrapper o) { - return Arrays.compare(data, o.data); - } -} diff --git a/modAionBase/src/org/aion/base/util/MAFast.java b/modAionBase/src/org/aion/base/util/MAFast.java deleted file mode 100644 index 3c11566f4f..0000000000 --- a/modAionBase/src/org/aion/base/util/MAFast.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.aion.base.util; - -/** - * http://www.daycounter.com/LabBook/Moving-Average.phtml - * - * <p>The best way to remove noise and smooth out sensor data is to compute a moving average. The - * moving average is a running average computer over a window the last N points of data. The average - * is expressed as the sum of the last N points divided by N: - * - * <p>MA[i]= sum(x[i]+x[i-(N-1)])/N - * - * <p>The brute force way to compute this is to repeat the computation for every new data point. - * This requires that N data points are stored, and N-1 additions are computed with a single divide. - * - * <p>Another way to update the moving average is to merely subtract x[i-N-1]/N and to add x[i]/N to - * the current MA. This requires 2 additions and 2 divisions, but still this requires that N data - * points be saved. - * - * <p>The following method doesn't require any storage of previous data values, and minimizes - * divisions which are computationally intensive. It works by subtracting out the mean each time, - * and adding in a new point. - * - * <p>MA*[i]= MA*[i-1] +X[i] - MA*[i-1]/N - * - * <p>where MA* is the moving average*N. - * - * <p>MA[i]= MA*[i]/N - * - * <p>The advantage of this technique is that previous data values need not be stored. If N is a - * power of 2 the division can be accomplished with a computationally efficient shift. So a single - * division and subtraction and 2 shifts are all that are needed, rendering suitable for a simple - * microcontroller. - */ -// public class MAFast { -// private double MAStar; // MA*[i-1] -// private volatile double MA; // MA[i] -// private int N; -// -// public MAFast(int N) { -// this.N = N; -// this.MAStar = 0D; -// this.MA = 0D; -// } -// -// public synchronized double compute(int X) { // X[i] -// double MAStar_i = MAStar + X + (MAStar / N); -// MA = MAStar_i/N; -// MAStar = MAStar_i; -// return MA; -// } -// -// public double get() { -// return MA; -// } -// } diff --git a/modAionBase/src/org/aion/base/util/NativeLoader.java b/modAionBase/src/org/aion/base/util/NativeLoader.java deleted file mode 100644 index 3e00ed14b4..0000000000 --- a/modAionBase/src/org/aion/base/util/NativeLoader.java +++ /dev/null @@ -1,144 +0,0 @@ -package org.aion.base.util; - -import java.io.File; -import java.io.IOException; -import java.util.Scanner; - -// import java.io.FileNotFoundException; -// import java.io.FileOutputStream; -// import java.io.InputStream; -// import java.io.OutputStream; - -/** - * Native library loader. - * - * @author jin - * @apiNote This functionality is migrated to modUtil. Use that class instead. - */ -@Deprecated -public class NativeLoader { - - /** - * Returns the current OS name. - * - * @return - */ - public static String getOS() { - String osName = System.getProperty("os.name").toLowerCase(); - if (osName.contains("win")) { - return "win"; - } else if (osName.contains("linux")) { - return "linux"; - } else if (osName.contains("mac")) { - return "mac"; - } else { - throw new RuntimeException("Unrecognized OS: " + osName); - } - } - - /** - * Builds a file path given a list of folder names. - * - * @param args - * @return - */ - public static File buildPath(String... args) { - StringBuilder sb = new StringBuilder(); - for (String arg : args) { - sb.append(File.separator); - sb.append(arg); - } - - return sb.length() > 0 ? new File(sb.substring(1)) : new File("."); - } - - /** - * Loads library based on the file list in the given module folder. - * - * @param module - */ - public static void loadLibrary(String module) { - File dir = buildPath("native", getOS(), module); - - try (Scanner s = new Scanner(new File(dir, "file.list"))) { - while (s.hasNextLine()) { - String line = s.nextLine(); - - if (line.startsWith("/") || line.startsWith(".")) { // for debug - // purpose - // mainly - System.load(line); - } else { - System.load(new File(dir, line).getCanonicalPath()); - } - } - } catch (IOException e) { - throw new RuntimeException("Failed to load libraries for " + module, e); - } - } - - // public static void loadLibraryFromJar(@SuppressWarnings("rawtypes") Class clz, String - // path) - // throws IOException { - // - // if (!path.startsWith("/")) { - // throw new IllegalArgumentException("The path has to be absolute (start with - // '/')."); - // } - // - // // Obtain filename from path - // String[] parts = path.split("/"); - // String filename = (parts.length > 1) ? parts[parts.length - 1] : null; - // - // // Split filename to prexif and suffix (extension) - // String prefix = ""; - // String suffix = null; - // if (filename != null) { - // parts = filename.split("\\.", 2); - // prefix = parts[0]; - // suffix = (parts.length > 1) ? "." + parts[parts.length - 1] : null; - // } - // - // // Check if the filename is okay - // if (filename == null || prefix.length() < 3) { - // throw new IllegalArgumentException( - // "The filename has to be at least 3 characters long."); - // } - // - // // Prepare temporary file - // File temp = File.createTempFile(prefix, suffix); - // temp.deleteOnExit(); - // - // if (!temp.exists()) { - // throw new FileNotFoundException("File " + temp.getAbsolutePath() + " does not - // exist."); - // } - // - // // Prepare buffer for data copying - // byte[] buffer = new byte[1024]; - // int readBytes; - // - // // Open and check input stream - // InputStream is = clz.getResourceAsStream(path); - // if (is == null) { - // throw new FileNotFoundException("File " + path + " was not found inside JAR."); - // } - // - // // Open output stream and copy data between source file in JAR and the - // // temporary file - // OutputStream os = new FileOutputStream(temp); - // try { - // while ((readBytes = is.read(buffer)) != -1) { - // os.write(buffer, 0, readBytes); - // } - // } finally { - // // If read/write fails, close streams safely before throwing an - // // exception - // os.close(); - // is.close(); - // } - // - // // Finally, load the library - // System.load(temp.getAbsolutePath()); - // } -} diff --git a/modAionBase/src/org/aion/base/util/TypeConverter.java b/modAionBase/src/org/aion/base/util/TypeConverter.java deleted file mode 100644 index 16e0483023..0000000000 --- a/modAionBase/src/org/aion/base/util/TypeConverter.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.aion.base.util; - -import java.math.BigInteger; - -public class TypeConverter { - - // public static byte[] StringNumberAsBytes(String input) { - // return ByteUtil.bigIntegerToBytes(StringDecimalToBigInteger(input)); - // } - - public static BigInteger StringNumberAsBigInt(String input) { - if (input.startsWith("0x")) { - return TypeConverter.StringHexToBigInteger(input); - } else { - return TypeConverter.StringDecimalToBigInteger(input); - } - } - - public static BigInteger StringHexToBigInteger(String input) { - String hexa = input.startsWith("0x") ? input.substring(2) : input; - return new BigInteger(hexa, 16); - } - - private static BigInteger StringDecimalToBigInteger(String input) { - return new BigInteger(input); - } - - public static byte[] StringHexToByteArray(String x) { - if (x.startsWith("0x")) { - x = x.substring(2); - } - if (x.length() % 2 != 0) { - x = "0" + x; - } - return Hex.decode(x); - } - - public static String toJsonHex(byte[] x) { - return "0x" + Hex.toHexString(x); - } - - public static String toJsonHex(String x) { - return x.startsWith("0x") ? x : "0x" + x; - } - - public static String toJsonHex(long n) { - return "0x" + Long.toHexString(n); - } - - public static String toJsonHex(BigInteger n) { - return "0x" + n.toString(16); - } -} diff --git a/modAionBase/src/org/aion/base/util/Utils.java b/modAionBase/src/org/aion/base/util/Utils.java deleted file mode 100644 index 81cce12e01..0000000000 --- a/modAionBase/src/org/aion/base/util/Utils.java +++ /dev/null @@ -1,328 +0,0 @@ -package org.aion.base.util; - -// import java.lang.reflect.Array; - -import java.math.BigInteger; -import java.security.SecureRandom; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.Date; -import java.util.Optional; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -// import java.util.List; - -public class Utils { - - private static SecureRandom random = new SecureRandom(); - - public static final Object dummy = new Object(); - - /** - * @param number should be in form '0x34fabd34....' - * @return String - */ - // public static BigInteger unifiedNumericToBigInteger(String number) { - // - // boolean match = Pattern.matches("0[xX][0-9a-fA-F]+", number); - // if (!match) { - // return (new BigInteger(number)); - // } else { - // number = number.substring(2); - // number = number.length() % 2 != 0 ? "0".concat(number) : number; - // byte[] numberBytes = Hex.decode(number); - // return (new BigInteger(1, numberBytes)); - // } - // } - - /** - * Return formatted Date String: yyyy.MM.dd HH:mm:ss Based on Unix's time() input in seconds - * - * @param timestamp seconds since start of Unix-time - * @return String formatted as - yyyy.MM.dd HH:mm:ss - */ - public static String longToDateTime(long timestamp) { - Date date = new Date(timestamp * 1000); - DateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); - return formatter.format(date); - } - - // public static ImageIcon getImageIcon(String resource) { - // URL imageURL = ClassLoader.getSystemResource(resource); - // ImageIcon image = new ImageIcon(imageURL); - // return image; - // } - static BigInteger _1000_ = new BigInteger("1000"); - - public static String getValueShortString(BigInteger number) { - BigInteger result = number; - int pow = 0; - while (result.compareTo(_1000_) == 1 || result.compareTo(_1000_) == 0) { - result = result.divide(_1000_); - pow += 3; - } - return result.toString() + "\u00b7(" + "10^" + pow + ")"; - } - - /** - * Decodes a hex string to address bytes and checks validity - * - * @param hex - a hex string of the address, e.g., 6c386a4b26f73c802f34673f7248bb118f97424a - * @return - decode and validated address byte[] - */ - // public static byte[] addressStringToBytes(String hex) { - // final byte[] addr; - // try { - // addr = Hex.decode(hex); - // } catch (Exception addressIsNotValid) { - // return null; - // } - // - // if (isValidAddress(addr)) { - // return addr; - // } - // return null; - // } - - public static boolean isValidAddress(byte[] addr) { - return addr != null && addr.length == 20; - } - - /** Validate a passed hex string is a valid address */ - public static boolean isValidAddress(String address) { - if (address == null || address.isEmpty() || address.length() < 64) { - return false; - } - - if (address.startsWith("0x")) { - address = address.substring(2); - } - - // Will need to change this for a1, a2.... - if (address.startsWith("a0")) { - return address.length() == 64 && address.substring(2).matches("^[0-9A-Fa-f]+$"); - } else { - return false; - } - } - - /** - * @param addr length should be 20 - * @return short string represent 1f21c... - */ - // public static String getAddressShortString(byte[] addr) { - // - // if (!isValidAddress(addr)) { - // throw new Error("not an address"); - // } - // - // String addrShort = Hex.toHexString(addr, 0, 3); - // - // StringBuffer sb = new StringBuffer(); - // sb.append(addrShort); - // sb.append("..."); - // - // return sb.toString(); - // } - - public static SecureRandom getRandom() { - return random; - } - - public static double JAVA_VERSION = getJavaVersion(); - - static double getJavaVersion() { - String version = System.getProperty("java.version"); - - // on android this property equals to 0 - if (version.equals("0")) { - return 0; - } - - int dpos = 0; - for (; dpos < version.length(); dpos++) { - if (version.charAt(dpos) == '-') { - version = version.substring(0, dpos); - break; - } - } - - if (version.length() == 1) { - return Double.parseDouble(version); - } - - int pos = 0, count = 0; - for (; pos < version.length() && count < 2; pos++) { - if (version.charAt(pos) == '.') { - count++; - } - } - return Double.parseDouble(version.substring(0, pos - 1)); - } - - // public static String getHashListShort(List<byte[]> blockHashes) { - // if (blockHashes.isEmpty()) { - // return "[]"; - // } - // - // StringBuilder sb = new StringBuilder(); - // String firstHash = Hex.toHexString(blockHashes.get(0)); - // String lastHash = Hex.toHexString(blockHashes.get(blockHashes.size() - 1)); - // return sb.append(" ").append(firstHash).append("...").append(lastHash).toString(); - // } - - public static String getNodeIdShort(String nodeId) { - return nodeId == null ? "<null>" : nodeId.substring(0, 8); - } - - // public static long toUnixTime(long javaTime) { - // return javaTime / 1000; - // } - - // public static long fromUnixTime(long unixTime) { - // return unixTime * 1000; - // } - - @SuppressWarnings("unchecked") - // public static <T> T[] mergeArrays(T[]... arr) { - // int size = 0; - // for (T[] ts : arr) { - // size += ts.length; - // } - // T[] ret = (T[]) Array.newInstance(arr[0].getClass().getComponentType(), size); - // int off = 0; - // for (T[] ts : arr) { - // System.arraycopy(ts, 0, ret, off, ts.length); - // off += ts.length; - // } - // return ret; - // } - - public static String align(String s, char fillChar, int targetLen, boolean alignRight) { - if (targetLen <= s.length()) { - return s; - } - String alignString = repeat("" + fillChar, targetLen - s.length()); - return alignRight ? alignString + s : s + alignString; - } - - public static String repeat(String s, int n) { - if (s.length() == 1) { - byte[] bb = new byte[n]; - Arrays.fill(bb, s.getBytes()[0]); - return new String(bb); - } else { - StringBuilder ret = new StringBuilder(); - for (int i = 0; i < n; i++) { - ret.append(s); - } - return ret.toString(); - } - } - - private static final Pattern matchPattern = Pattern.compile("^([0-9]+)([a-zA-Z]+)$"); - public static final long KILO_BYTE = 1024; - public static final long MEGA_BYTE = 1048576; - public static final long GIGA_BYTE = 1073741824; - /** - * Matches file sizes based on fileSize string, in the format: [numericalValue][sizeDescriptor] - * - * <p>Examples of acceptable formats: - * <li> - * - * <ul> - * 10b - * </ul> - * - * <ul> - * 10B - * </ul> - * - * <ul> - * 10K - * </ul> - * - * <ul> - * 10KB - * </ul> - * - * <ul> - * 10kB - * </ul> - * - * <ul> - * 10M - * </ul> - * - * <ul> - * 10mB - * </ul> - * - * <ul> - * 10MB - * </ul> - * - * <ul> - * 10G - * </ul> - * - * <ul> - * 10gB - * </ul> - * - * <ul> - * 10GB - * </ul> - * - * <p>Commas are <b>not</b> accepted by the parser, and are considered invalid. - * - * <p>Note: Anything beyond {@code gigaByte (GB, G, gB)} is not considered valid, and will be - * treated as a parse exception. - * - * <p>Note: this function assumes the binary representation of magnitudes, therefore 1kB - * (kiloByte) is not {@code 1000 bytes} but rather {@code 1024 bytes}. - * - * @param fileSize file size string - * @return {@code Optional.of(fileSizeInt)} if we were able to successfully decode the filesize - * string, otherwise outputs {@code Optional.empty()} indicating that we were unable to - * decode the file size string, this usually refers to some sort of syntactic error made by - * the user. - */ - public static Optional<Long> parseSize(String fileSize) { - Matcher m = matchPattern.matcher(fileSize); - // if anything does not match - if (!m.find()) { - return Optional.empty(); - } - - String numerical = m.group(1); - String sizeSuffix = m.group(2); - - long size = Integer.parseInt(numerical); - switch (sizeSuffix) { - case "B": - break; - case "K": - case "kB": - case "KB": - // process kiloByte (1024 * byte) here - size = size * KILO_BYTE; - break; - case "M": - case "mB": - case "MB": - size = size * MEGA_BYTE; - break; - case "G": - case "gB": - case "GB": - size = size * GIGA_BYTE; - break; - default: - return Optional.empty(); - } - return Optional.of(size); - } -} diff --git a/modAionBase/src/org/aion/base/vm/IDataWord.java b/modAionBase/src/org/aion/base/vm/IDataWord.java deleted file mode 100644 index 974d461965..0000000000 --- a/modAionBase/src/org/aion/base/vm/IDataWord.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.aion.base.vm; - -import java.math.BigInteger; -import org.aion.base.util.ByteArrayWrapper; - -public interface IDataWord { - - byte[] getData(); - - byte[] getNoLeadZeroesData(); - - BigInteger value(); - - IDataWord copy(); - - boolean isZero(); - - ByteArrayWrapper toWrapper(); -} diff --git a/modAionBase/src/org/aion/base/vm/VirtualMachineSpecs.java b/modAionBase/src/org/aion/base/vm/VirtualMachineSpecs.java deleted file mode 100644 index f53f8d2b4c..0000000000 --- a/modAionBase/src/org/aion/base/vm/VirtualMachineSpecs.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.aion.base.vm; - -public final class VirtualMachineSpecs { - - public static final byte AVM_CREATE_CODE = 0x0f; - public static final byte FVM_ALLOWED_TX_TYPE = 0x00; - public static final byte FVM_DEFAULT_TX_TYPE = 0x01; -} diff --git a/modAionBase/test/org/aion/base/type/AddressTest.java b/modAionBase/test/org/aion/base/type/AddressTest.java deleted file mode 100644 index 7e28a8f900..0000000000 --- a/modAionBase/test/org/aion/base/type/AddressTest.java +++ /dev/null @@ -1,205 +0,0 @@ -package org.aion.base.type; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; -import org.junit.Test; - -public class AddressTest { - - private final String[] addrHex = { - null, // 0 - Null - "", // 1 - Empty - "eE55fF66eE55fF66eE55fF66eE55fF66", // 2 - Short - "aA11bB22cC33dd44aA11bB22cC33dd44aA11bB22cC33dd44aA11bB22cC33dd44", // 3 - Upper/Lower - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", // 4 - Negative (-1) - "0000000000000000000000000000000000000000000000000000000000000000", // 5 - Zeroes - "0000000000000000000000000000000000000000000000000000000000000001", // 6 - Positive (+1) - }; - - private final byte[][] addrByte = { // Changes every time - null, - AionAddress.EMPTY_ADDRESS().toBytes(), - ByteUtil.hexStringToBytes(addrHex[2]), - ByteUtil.hexStringToBytes(addrHex[3]), - ByteUtil.hexStringToBytes(addrHex[4]), - AionAddress.ZERO_ADDRESS().toBytes(), - ByteUtil.hexStringToBytes(addrHex[6]) - }; - - private final ByteArrayWrapper[] addrArray = { // Same as addrHex - null, - new ByteArrayWrapper(new byte[0]), - new ByteArrayWrapper(addrByte[2]), - new ByteArrayWrapper(addrByte[3]), - new ByteArrayWrapper(addrByte[4]), - new ByteArrayWrapper(new byte[32]), - new ByteArrayWrapper(addrByte[6]) - }; - - /** - * Test address wrap function for each input type; String(Hex), Byte, ByteArrayWrapper For each - * input type: 1. Wrap the input data 2. Clone, Convert and Wrap as other input type 3. Assert - * they are all equal - */ - @Test - public void testWrap() { - - AionAddress tempHex; - AionAddress tempByte; - AionAddress tempArray; - - System.out.println("\nHex address test:"); - for (int a = 0; a < addrHex.length; a++) { - try { - tempHex = AionAddress.wrap(addrHex[a]); - tempByte = AionAddress.wrap(tempHex.clone().toBytes()); - tempArray = AionAddress.wrap(tempHex.clone().toByteArrayWrapper()); - - assertTrue(tempHex.equals(tempByte)); - assertTrue(tempByte.equals(tempArray)); - assertTrue(tempArray.equals(tempHex)); - assertEquals(tempHex.toString(), addrHex[a].toLowerCase()); - - System.out.println("Test " + a + ": Valid " + tempHex.toString()); - } catch (IllegalArgumentException e) { - System.out.println("Test " + a + ": Invalid"); - } - } - - System.out.println("\nByte address test:"); - for (int a = 0; a < addrByte.length; a++) { - try { - tempByte = AionAddress.wrap(addrByte[a]); - tempArray = AionAddress.wrap(tempByte.clone().toByteArrayWrapper()); - tempHex = AionAddress.wrap(tempByte.clone().toString()); - - assertTrue(tempByte.equals(tempArray)); - assertTrue(tempArray.equals(tempHex)); - assertTrue(tempHex.equals(tempByte)); - // assertEquals(tempByte.toBytes(), addrByte[a]); - - System.out.println("Test " + a + ": Valid " + tempByte); - } catch (IllegalArgumentException e) { - System.out.println("Test " + a + ": Invalid"); - } - } - - System.out.println("\nArray address test:"); - for (int a = 0; a < addrArray.length; a++) { - try { - tempArray = AionAddress.wrap(addrArray[a]); - tempHex = AionAddress.wrap(tempArray.clone().toString()); - tempByte = AionAddress.wrap(tempArray.clone().toBytes()); - - assertTrue(tempArray.equals(tempHex)); - assertTrue(tempHex.equals(tempByte)); - assertTrue(tempByte.equals(tempArray)); - assertEquals(tempArray.toByteArrayWrapper(), addrArray[a]); - - System.out.println("Test " + a + ": Valid " + tempArray.toByteArrayWrapper()); - } catch (IllegalArgumentException e) { - System.out.println("Test " + a + ": Invalid"); - } - } - } - - /** - * Test address comparison; A compareTo B For each input type: 1. Wrap the two inputs 2. Assert - * (-ve: A < B && +ve: A > B) 3. Increment Up/Down - */ - @Test - public void testCompare() { - - System.out.println("\nHex address test:"); - for (int b = 3; b < 6; b++) { - try { - int temp = AionAddress.wrap(addrHex[b]).compareTo(AionAddress.wrap(addrHex[b + 1])); - boolean same = - AionAddress.wrap(addrHex[b]).equals(AionAddress.wrap(addrHex[b + 1])); - boolean negative = temp < 0; - System.out.println("Test " + b + " & " + (b + 1) + " >> " + temp); - assertFalse(same); - assertTrue(negative); - } catch (IllegalArgumentException e) { - System.out.println("Test " + b + ": Input Invalid"); - } - } - for (int b = 6; b > 3; b--) { - try { - int temp = AionAddress.wrap(addrHex[b]).compareTo(AionAddress.wrap(addrHex[b - 1])); - boolean same = - AionAddress.wrap(addrHex[b]).equals(AionAddress.wrap(addrHex[b - 1])); - boolean positive = temp > 0; - System.out.println("Test " + b + " & " + (b - 1) + " >> " + temp); - assertFalse(same); - assertTrue(positive); - } catch (IllegalArgumentException e) { - System.out.println("Test " + b + ": Input Invalid"); - } - } - - System.out.println("\nByte address test:"); - for (int b = 3; b < 6; b++) { - try { - int temp = AionAddress.wrap(addrByte[b]).compareTo(addrByte[b + 1]); - boolean same = - AionAddress.wrap(addrByte[b]).equals(AionAddress.wrap(addrByte[b + 1])); - boolean negative = temp < 0; - System.out.println("Test " + b + " & " + (b + 1) + " >> " + temp); - assertFalse(same); - assertTrue(negative); - } catch (IllegalArgumentException e) { - System.out.println("Test " + b + ": Input Invalid"); - } - } - for (int b = 6; b > 3; b--) { - try { - int temp = AionAddress.wrap(addrByte[b]).compareTo(addrByte[b - 1]); - boolean same = - AionAddress.wrap(addrByte[b]).equals(AionAddress.wrap(addrByte[b - 1])); - boolean positive = temp > 0; - System.out.println("Test " + b + " & " + (b - 1) + " >> " + temp); - assertFalse(same); - assertTrue(positive); - } catch (IllegalArgumentException e) { - System.out.println("Test " + b + ": Input Invalid"); - } - } - - System.out.println("\nArray address test:"); - for (int b = 3; b < 6; b++) { - try { - int temp = - AionAddress.wrap(addrArray[b]) - .compareTo(AionAddress.wrap(addrArray[b + 1])); - boolean same = - AionAddress.wrap(addrArray[b]).equals(AionAddress.wrap(addrArray[b + 1])); - boolean negative = temp < 0; - System.out.println("Test " + b + " & " + (b + 1) + " >> " + temp); - assertFalse(same); - assertTrue(negative); - } catch (IllegalArgumentException e) { - System.out.println("Test " + b + ": Input Invalid"); - } - } - for (int b = 6; b > 3; b--) { - try { - int temp = - AionAddress.wrap(addrArray[b]) - .compareTo(AionAddress.wrap(addrArray[b - 1])); - boolean same = - AionAddress.wrap(addrArray[b]).equals(AionAddress.wrap(addrArray[b - 1])); - boolean positive = temp > 0; - System.out.println("Test " + b + " & " + (b - 1) + " >> " + temp); - assertFalse(same); - assertTrue(positive); - } catch (IllegalArgumentException e) { - System.out.println("Test " + b + ": Input Invalid"); - } - } - } -} diff --git a/modAionBase/test/org/aion/base/type/Hash256Test.java b/modAionBase/test/org/aion/base/type/Hash256Test.java deleted file mode 100644 index d4acf2d494..0000000000 --- a/modAionBase/test/org/aion/base/type/Hash256Test.java +++ /dev/null @@ -1,195 +0,0 @@ -package org.aion.base.type; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; -import org.junit.Test; - -public class Hash256Test { - - private final String[] hashHex = { - null, // 0 - Null - "", // 1 - Empty - "eE55fF66eE55fF66eE55fF66eE55fF66", // 2 - Short - "aA11bB22cC33dd44aA11bB22cC33dd44aA11bB22cC33dd44aA11bB22cC33dd44", // 3 - Upper/Lower - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", // 4 - Negative (-1) - "0000000000000000000000000000000000000000000000000000000000000000", // 5 - Zeroes - "0000000000000000000000000000000000000000000000000000000000000001", // 6 - Positive (+1) - }; - - private final byte[][] hashByte = { // Changes every time - null, - ByteUtil.hexStringToBytes(hashHex[1]), - ByteUtil.hexStringToBytes(hashHex[2]), - ByteUtil.hexStringToBytes(hashHex[3]), - ByteUtil.hexStringToBytes(hashHex[4]), - Hash256.ZERO_HASH().toBytes(), - ByteUtil.hexStringToBytes(hashHex[6]) - }; - - private final ByteArrayWrapper[] hashArray = { // Same as hashHex - null, - new ByteArrayWrapper(new byte[0]), - new ByteArrayWrapper(hashByte[2]), - new ByteArrayWrapper(hashByte[3]), - new ByteArrayWrapper(hashByte[4]), - new ByteArrayWrapper(new byte[32]), - new ByteArrayWrapper(hashByte[6]) - }; - - /** - * Test hash wrap function for each input type; String(Hex), Byte, ByteArrayWrapper For each - * input type: 1. Wrap the input data 2. Convert and Wrap as other input type 3. Assert they are - * all equal - */ - @Test - public void testWrap() { - - Hash256 tempHex; - Hash256 tempByte; - Hash256 tempArray; - - System.out.println("\nHex hash test:"); - for (int a = 0; a < hashHex.length; a++) { - try { - tempHex = Hash256.wrap(hashHex[a]); - tempByte = Hash256.wrap(tempHex.toBytes()); - tempArray = Hash256.wrap(tempHex.toByteArrayWrapper()); - - assertTrue(tempHex.equals(tempByte)); - assertTrue(tempByte.equals(tempArray)); - assertTrue(tempArray.equals(tempHex)); - assertEquals(tempHex.toString(), hashHex[a].toLowerCase()); - - System.out.println("Test " + a + ": Valid " + tempHex.toString()); - } catch (IllegalArgumentException e) { - System.out.println("Test " + a + ": Invalid"); - } - } - - System.out.println("\nByte hash test:"); - for (int a = 0; a < hashByte.length; a++) { - try { - tempByte = Hash256.wrap(hashByte[a]); - tempArray = Hash256.wrap(tempByte.toByteArrayWrapper()); - tempHex = Hash256.wrap(tempByte.toString()); - - assertTrue(tempByte.equals(tempArray)); - assertTrue(tempArray.equals(tempHex)); - assertTrue(tempHex.equals(tempByte)); - // assertEquals(tempByte.toBytes(), hashByte[a]); - - System.out.println("Test " + a + ": Valid " + tempByte.toBytes()); - } catch (IllegalArgumentException e) { - System.out.println("Test " + a + ": Invalid"); - } - } - - System.out.println("\nArray hash test:"); - for (int a = 0; a < hashArray.length; a++) { - try { - tempArray = Hash256.wrap(hashArray[a]); - tempHex = Hash256.wrap(tempArray.toString()); - tempByte = Hash256.wrap(tempArray.toBytes()); - - assertTrue(tempArray.equals(tempHex)); - assertTrue(tempHex.equals(tempByte)); - assertTrue(tempByte.equals(tempArray)); - assertEquals(tempArray.toByteArrayWrapper(), hashArray[a]); - - System.out.println("Test " + a + ": Valid " + tempArray.toByteArrayWrapper()); - } catch (IllegalArgumentException e) { - System.out.println("Test " + a + ": Invalid"); - } - } - } - - /** - * Test hash comparison; A compareTo B For each input type: 1. Wrap the two inputs 2. Assert - * (-ve: A < B && +ve: A > B) 3. Increment Up/Down - */ - @Test - public void testCompare() { - - System.out.println("\nHex hash test:"); - for (int b = 3; b < 6; b++) { - try { - int temp = Hash256.wrap(hashHex[b]).compareTo(Hash256.wrap(hashHex[b + 1])); - boolean same = Hash256.wrap(hashHex[b]).equals(Hash256.wrap(hashHex[b + 1])); - boolean negative = temp < 0; - System.out.println("Test " + b + " & " + (b + 1) + " >> " + temp); - assertFalse(same); - assertTrue(negative); - } catch (IllegalArgumentException e) { - System.out.println("Test " + b + ": Input Invalid"); - } - } - for (int b = 6; b > 3; b--) { - try { - int temp = Hash256.wrap(hashHex[b]).compareTo(Hash256.wrap(hashHex[b - 1])); - boolean same = Hash256.wrap(hashHex[b]).equals(Hash256.wrap(hashHex[b - 1])); - boolean positive = temp > 0; - System.out.println("Test " + b + " & " + (b - 1) + " >> " + temp); - assertFalse(same); - assertTrue(positive); - } catch (IllegalArgumentException e) { - System.out.println("Test " + b + ": Input Invalid"); - } - } - - System.out.println("\nByte hash test:"); - for (int b = 3; b < 6; b++) { - try { - int temp = Hash256.wrap(hashByte[b]).compareTo(Hash256.wrap(hashByte[b + 1])); - boolean same = Hash256.wrap(hashByte[b]).equals(Hash256.wrap(hashByte[b + 1])); - boolean negative = temp < 0; - System.out.println("Test " + b + " & " + (b + 1) + " >> " + temp); - assertFalse(same); - assertTrue(negative); - } catch (IllegalArgumentException e) { - System.out.println("Test " + b + ": Input Invalid"); - } - } - for (int b = 6; b > 3; b--) { - try { - int temp = Hash256.wrap(hashByte[b]).compareTo(Hash256.wrap(hashByte[b - 1])); - boolean same = Hash256.wrap(hashByte[b]).equals(Hash256.wrap(hashByte[b - 1])); - boolean positive = temp > 0; - System.out.println("Test " + b + " & " + (b - 1) + " >> " + temp); - assertFalse(same); - assertTrue(positive); - } catch (IllegalArgumentException e) { - System.out.println("Test " + b + ": Input Invalid"); - } - } - - System.out.println("\nArray hash test:"); - for (int b = 3; b < 6; b++) { - try { - int temp = Hash256.wrap(hashArray[b]).compareTo(Hash256.wrap(hashArray[b + 1])); - boolean same = Hash256.wrap(hashArray[b]).equals(Hash256.wrap(hashArray[b + 1])); - boolean negative = temp < 0; - System.out.println("Test " + b + " & " + (b + 1) + " >> " + temp); - assertFalse(same); - assertTrue(negative); - } catch (IllegalArgumentException e) { - System.out.println("Test " + b + ": Input Invalid"); - } - } - for (int b = 6; b > 3; b--) { - try { - int temp = Hash256.wrap(hashArray[b]).compareTo(Hash256.wrap(hashArray[b - 1])); - boolean same = Hash256.wrap(hashArray[b]).equals(Hash256.wrap(hashArray[b - 1])); - boolean positive = temp > 0; - System.out.println("Test " + b + " & " + (b - 1) + " >> " + temp); - assertFalse(same); - assertTrue(positive); - } catch (IllegalArgumentException e) { - System.out.println("Test " + b + ": Input Invalid"); - } - } - } -} diff --git a/modAionBase/test/org/aion/base/util/BIUtilTest.java b/modAionBase/test/org/aion/base/util/BIUtilTest.java deleted file mode 100644 index b2628dd668..0000000000 --- a/modAionBase/test/org/aion/base/util/BIUtilTest.java +++ /dev/null @@ -1,166 +0,0 @@ -package org.aion.base.util; - -import static org.aion.base.util.BIUtil.isCovers; -import static org.aion.base.util.BIUtil.isEqual; -import static org.aion.base.util.BIUtil.isLessThan; -import static org.aion.base.util.BIUtil.isMoreThan; -import static org.aion.base.util.BIUtil.isNotCovers; -import static org.aion.base.util.BIUtil.isNotEqual; -import static org.aion.base.util.BIUtil.isPositive; -import static org.aion.base.util.BIUtil.isZero; -import static org.aion.base.util.BIUtil.max; -import static org.aion.base.util.BIUtil.min; -import static org.aion.base.util.BIUtil.sum; -import static org.aion.base.util.BIUtil.toBI; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.math.BigInteger; -import org.junit.Test; - -public class BIUtilTest { - - private final BigInteger[][] bigInt = { - {new BigInteger("-00000000000000000000"), new BigInteger("00000000000000000000")}, - {new BigInteger("-00000000000000000001"), new BigInteger("00000000000000000001")}, - {new BigInteger("-10000000000000000000"), new BigInteger("10000000000000000000")}, - {new BigInteger("-20000000000000000000"), new BigInteger("20000000000000000000")}, - {new BigInteger("-30000000000000000000"), new BigInteger("30000000000000000000")}, - {new BigInteger("-99999999999999999999"), new BigInteger("99999999999999999999")}, - }; - - @Test - public void testIntegrity() { - - // isZero && isPositive - assertTrue(isZero(bigInt[0][0])); - assertTrue(isZero(bigInt[0][1])); - - assertFalse(isPositive(bigInt[0][0])); - assertFalse(isPositive(bigInt[0][1])); - - // isEqual && isNotEqual - assertTrue(isEqual(bigInt[0][0], bigInt[0][1])); - assertFalse(isNotEqual(bigInt[0][0], bigInt[0][1])); - - // isLessThan && isMoreThan - assertFalse(isLessThan(bigInt[0][0], bigInt[0][1])); - assertFalse(isMoreThan(bigInt[0][0], bigInt[0][1])); - - for (int a = 1; a < bigInt.length; a++) { - - assertFalse(isPositive(bigInt[a][0])); - assertTrue(isPositive(bigInt[a][1])); - - assertFalse(isEqual(bigInt[a][0], bigInt[a][1])); - assertTrue(isNotEqual(bigInt[a][0], bigInt[a][1])); - - assertTrue(isLessThan(bigInt[a][0], bigInt[a][1])); - assertFalse(isMoreThan(bigInt[a][0], bigInt[a][1])); - } - - // isCovers && isNotCovers - for (int a = 1; a < bigInt.length; a++) { - assertTrue(isNotCovers(bigInt[a - 1][1], bigInt[a][1])); - } - for (int a = 1; a < bigInt.length; a++) { - assertTrue(isNotCovers(bigInt[a][0], bigInt[a - 1][0])); - } - for (int a = bigInt.length - 1; a > 0; a--) { - assertTrue(isCovers(bigInt[a][1], bigInt[a - 1][1])); - } - - for (int a = bigInt.length - 1; a > 0; a--) { - assertTrue(isCovers(bigInt[a - 1][0], bigInt[a][0])); - } - - // isIn20PercentRange - } - - @Test - public void testType() { - - // toBI(byte), toBI(long) - final long[] testLong = { - 0L, 1L, 1000000000000000000L, 9223372036854775807L, - }; - - final byte[][] testByte = { - ByteUtil.longToBytes(testLong[0]), - ByteUtil.longToBytes(testLong[1]), - ByteUtil.longToBytes(testLong[2]), - ByteUtil.longToBytes(testLong[3]), - }; - - for (int i = 0; i < 4; i++) { - assertEquals(toBI(testLong[i]), toBI(testByte[i])); - } - - // exitLong - } - - @Test - public void testSum() { - - // sum - for (int a = 0; a < bigInt.length; a++) { - assertEquals(new BigInteger("0"), sum(bigInt[a][0], bigInt[a][1])); - } - - for (int b = 0; b < 2; b++) { - for (int a = 0; a < bigInt.length; a++) { - assertEquals(bigInt[a][b], sum(bigInt[0][b], bigInt[a][b])); - } - } - - assertEquals( - new BigInteger("-160000000000000000000"), - sum( - bigInt[0][0], - sum( - bigInt[1][0], - sum( - bigInt[2][0], - sum(bigInt[3][0], sum(bigInt[4][0], bigInt[5][0])))))); - - assertEquals( - new BigInteger("160000000000000000000"), - sum( - bigInt[0][1], - sum( - bigInt[1][1], - sum( - bigInt[2][1], - sum(bigInt[3][1], sum(bigInt[4][1], bigInt[5][1])))))); - } - - @Test - public void testMinMax() { - // min && max - for (int c = 0; c < bigInt.length; c++) { - assertEquals(bigInt[c][0], min(bigInt[c][0], bigInt[c][1])); - assertEquals(bigInt[c][1], max(bigInt[c][0], bigInt[c][1])); - } - - assertEquals( - bigInt[bigInt.length - 1][0], - min( - bigInt[0][0], - min( - bigInt[1][0], - min( - bigInt[2][0], - min(bigInt[3][0], min(bigInt[4][0], bigInt[5][0])))))); - - assertEquals( - bigInt[bigInt.length - 1][1], - max( - bigInt[0][1], - max( - bigInt[1][1], - max( - bigInt[2][1], - max(bigInt[3][1], max(bigInt[4][1], bigInt[5][1])))))); - } -} diff --git a/modAionBase/test/org/aion/base/util/ByteArrayWrapperTest.java b/modAionBase/test/org/aion/base/util/ByteArrayWrapperTest.java deleted file mode 100644 index 6c0c108247..0000000000 --- a/modAionBase/test/org/aion/base/util/ByteArrayWrapperTest.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.aion.base.util; - -import static org.aion.base.util.ByteUtil.hexStringToBytes; -import static org.junit.Assert.assertEquals; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; -import junitparams.JUnitParamsRunner; -import junitparams.Parameters; -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(JUnitParamsRunner.class) -public class ByteArrayWrapperTest { - - /** @return input values for {@link #testWrap(String)} */ - @SuppressWarnings("unused") - private Object hexValues() { - - List<Object> parameters = new ArrayList<>(); - - parameters.add(""); - parameters.add("eE55fF66eE55fF66eE55fF66eE55fF66"); - parameters.add("aA11bB22cC33dd44aA11bB22cC33dd44aA11bB22cC33dd44aA11bB22cC33dd44"); - parameters.add("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); - parameters.add("0000000000000000000000000000000000000000000000000000000000000000"); - parameters.add("0000000000000000000000000000000000000000000000000000000000000001"); - - return parameters.toArray(); - } - - /** 1. Wrap the input data 2. Assert to see if equal */ - @Test - @Parameters(method = "hexValues") - public void testWrap(String inputString) { - - ByteArrayWrapper tempArray; - byte[] inputByte = hexStringToBytes(inputString); - - try { - tempArray = ByteArrayWrapper.wrap(inputByte); - assertEquals(tempArray.toString(), inputString.toLowerCase()); - assertEquals(tempArray.toBytes(), tempArray.getData()); - System.out.println("Valid " + tempArray); - } catch (NullPointerException e) { - System.out.println("Invalid"); - } - } - - @Test - public void testCollision() { - java.util.HashMap<ByteArrayWrapper, Object> map = new java.util.HashMap<>(); - - for (int i = 0; i < 2000; i++) { - ByteBuffer buffer = ByteBuffer.allocate(20); - buffer.putInt(i); - - map.put(new ByteArrayWrapper(buffer.array()), new Object()); - } - - int cnt1 = 0; - for (ByteArrayWrapper k : map.keySet()) { - if (map.get(k) == null) { - System.out.println("1111 " + k); - cnt1++; - } - } - - int cnt2 = 0; - for (java.util.Map.Entry<ByteArrayWrapper, Object> e : map.entrySet()) { - if (e.getValue() == null) { - System.out.println("2222 " + e); - cnt2++; - } - } - - assertEquals(0, cnt1); - assertEquals(0, cnt2); - } -} diff --git a/modAionBase/test/org/aion/base/util/ByteUtilTest.java b/modAionBase/test/org/aion/base/util/ByteUtilTest.java deleted file mode 100644 index 849a63d422..0000000000 --- a/modAionBase/test/org/aion/base/util/ByteUtilTest.java +++ /dev/null @@ -1,445 +0,0 @@ -package org.aion.base.util; - -import static org.aion.base.util.ByteUtil.and; -import static org.aion.base.util.ByteUtil.appendByte; -import static org.aion.base.util.ByteUtil.bigEndianToShort; -import static org.aion.base.util.ByteUtil.bigIntegerToBytes; -import static org.aion.base.util.ByteUtil.bigIntegerToBytesSigned; -import static org.aion.base.util.ByteUtil.byteArrayToInt; -import static org.aion.base.util.ByteUtil.byteArrayToLong; -import static org.aion.base.util.ByteUtil.bytesToBigInteger; -import static org.aion.base.util.ByteUtil.copyToArray; -import static org.aion.base.util.ByteUtil.encodeValFor32Bits; -import static org.aion.base.util.ByteUtil.firstNonZeroByte; -import static org.aion.base.util.ByteUtil.getBit; -import static org.aion.base.util.ByteUtil.hexStringToBytes; -import static org.aion.base.util.ByteUtil.increment; -import static org.aion.base.util.ByteUtil.intToBytes; -import static org.aion.base.util.ByteUtil.intToBytesBE; -import static org.aion.base.util.ByteUtil.intToBytesLE; -import static org.aion.base.util.ByteUtil.intToBytesNoLeadZeroes; -import static org.aion.base.util.ByteUtil.isNullOrZeroArray; -import static org.aion.base.util.ByteUtil.isSingleZero; -import static org.aion.base.util.ByteUtil.length; -import static org.aion.base.util.ByteUtil.longToBytes; -import static org.aion.base.util.ByteUtil.longToBytesLE; -import static org.aion.base.util.ByteUtil.longToBytesNoLeadZeroes; -import static org.aion.base.util.ByteUtil.numBytes; -import static org.aion.base.util.ByteUtil.oneByteToHexString; -import static org.aion.base.util.ByteUtil.or; -import static org.aion.base.util.ByteUtil.setBit; -import static org.aion.base.util.ByteUtil.shortToBytes; -import static org.aion.base.util.ByteUtil.stripLeadingZeroes; -import static org.aion.base.util.ByteUtil.toByte; -import static org.aion.base.util.ByteUtil.toHexString; -import static org.aion.base.util.ByteUtil.toHexStringWithPrefix; -import static org.aion.base.util.ByteUtil.toLEByteArray; -import static org.aion.base.util.ByteUtil.xor; -import static org.aion.base.util.ByteUtil.xorAlignRight; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; -import junitparams.JUnitParamsRunner; -import junitparams.Parameters; -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(JUnitParamsRunner.class) -public class ByteUtilTest { - - private static final Random random = new Random(); - - private final String[] testHex = { - null, // 0 - Null - "", // 1 - Empty - "eF", // 2 - One Byte - "aA11bB22cC33dd44aA11bB22cC33dd44aA11bB22cC33dd44aA11bB22cC33dd44", // 3 - Upper/Lower - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", // 4 - Negative (-1) - "0000000000000000000000000000000000000000000000000000000000000000", // 5 - Zeroes - "0000000000000000000000000000000000000000000000000000000000000001", // 6 - Positive (+1) - }; // 1byte - - private final byte[][] testByte = { - null, - hexStringToBytes(testHex[1]), - hexStringToBytes(testHex[2]), - hexStringToBytes(testHex[3]), - hexStringToBytes(testHex[4]), - hexStringToBytes(testHex[5]), - hexStringToBytes(testHex[6]), - }; - - private final String[][] testNum = { - {null, null}, - {"-00000000000000000000", "00000000000000000000"}, - {"-00000000000000000001", "00000000000000000001"}, - {"-10000000000000000000", "10000000000000000000"}, - {"-20000000000000000000", "20000000000000000000"}, - {"-30000000000000000000", "30000000000000000000"}, - {"-99999999999999999999", "99999999999999999999"}, - }; - - private final BigInteger[][] testBigInt = { - {null, null}, - {new BigInteger(testNum[1][0]), new BigInteger(testNum[1][1])}, - {new BigInteger(testNum[2][0]), new BigInteger(testNum[2][1])}, - {new BigInteger(testNum[3][0]), new BigInteger(testNum[3][1])}, - {new BigInteger(testNum[4][0]), new BigInteger(testNum[4][1])}, - {new BigInteger(testNum[5][0]), new BigInteger(testNum[5][1])}, - {new BigInteger(testNum[6][0]), new BigInteger(testNum[6][1])}, - }; - - /** @return input values for {@link #longTest(long)} */ - @SuppressWarnings("unused") - private Object longValues() { - - List<Object> parameters = new ArrayList<>(); - - // longs similar to integer values - parameters.add(0L); - parameters.add(1L); - parameters.add(10L); - parameters.add((long) random.nextInt(Integer.MAX_VALUE)); - parameters.add((long) Integer.MAX_VALUE); - - // additional long values - parameters.add((long) Integer.MAX_VALUE + random.nextInt(Integer.MAX_VALUE)); - parameters.add(10L * (long) Integer.MAX_VALUE); - parameters.add(Long.MAX_VALUE - 1L); - - return parameters.toArray(); - } - - /** @return input values for {@link #intTest(int)} */ - @SuppressWarnings("unused") - private Object intValues() { - - List<Object> parameters = new ArrayList<>(); - - // integer values - parameters.add(0); - parameters.add(1); - parameters.add(10); - parameters.add(15); - parameters.add(20); - parameters.add(random.nextInt(Integer.MAX_VALUE)); - parameters.add(Integer.MAX_VALUE); - - return parameters.toArray(); - } - - /** @return input values for {@link #shortTest(short)} */ - @SuppressWarnings("unused") - private Object shortValues() { - - Short[] temp = { - 0, 1, 10, 15, 20, (short) random.nextInt(Integer.MAX_VALUE), (short) Integer.MAX_VALUE - }; - - return temp; - } - - /** - * TEST: BI <--> Bytes (+ve) bigIntegerToBytes(BI) bigIntegerToBytes(BI, num) - * bigIntegerToBytesSigned(BI, num) bytesToBigInteger(byte) {@link #intValues()}. - */ - @Test - public void bigIntegerTest() { - for (int b = 1; b < 2; b++) { - for (int a = 0; a < testBigInt.length; a++) { - try { - byte[] temp1 = bigIntegerToBytes(testBigInt[a][b]); - byte[] temp2 = bigIntegerToBytes(testBigInt[a][b], temp1.length); - byte[] temp3 = bigIntegerToBytesSigned(testBigInt[a][b], temp1.length); - byte[] temp4 = encodeValFor32Bits(testNum[a][b]); - - assertEquals(testBigInt[a][b], bytesToBigInteger(temp1)); - assertEquals(testBigInt[a][b], bytesToBigInteger(temp2)); - assertEquals(testBigInt[a][b], bytesToBigInteger(temp3)); - assertEquals(bytesToBigInteger(temp1), bytesToBigInteger(temp4)); - } catch (NullPointerException e) { - System.out.println(b + " " + a); - System.out.println("\nNull Big Integer Test!"); - } - } - } - } - - // TODO: Object --> Bytes - // encodeValFor32Bits(Object) - // encodeDataList(Object) - @Test - public void objectTest() { - for (int a = 0; a < testNum.length; a++) { - try { - byte[] temp1 = encodeValFor32Bits(testNum[a][1]); - } catch (NullPointerException e) { - System.out.println("\nNull Object Test!"); - } - } - } - - /** TEST: hex <--> Bytes hexStringToBytes(hex) toHexString(byte) toHexStringWithPrefix(byte) */ - @Test - public void hexTest() { - byte[] temp0 = hexStringToBytes(testHex[0]); - assertEquals("", toHexString(temp0)); - assertEquals("0x", toHexStringWithPrefix(temp0)); - - for (int a = 1; a < testHex.length; a++) { - byte[] temp1 = hexStringToBytes(testHex[a]); - assertEquals(testHex[a].toLowerCase(), toHexString(temp1)); - assertEquals("0x" + testHex[a].toLowerCase(), toHexStringWithPrefix(temp1)); - } - } - - /** - * TEST: long <--> Bytes longToBytes(long) longToBytesNoLeadZeroes(long) byteArrayToLong(byte) - * longToBytesLE(long) - */ - @Test - @Parameters(method = "longValues") - public void longTest(long testLong) { - - byte[] temp1 = longToBytes(testLong); - byte[] temp2 = longToBytesNoLeadZeroes(testLong); - byte[] temp3 = longToBytesLE(testLong); - - toLEByteArray(temp3); - - assertEquals(testLong, byteArrayToLong(temp1)); - assertEquals(testLong, byteArrayToLong(temp2)); - assertEquals(testLong, byteArrayToLong(temp3)); - } - - /** - * TEST: short <--> Bytes shortToBytes(short) bigEndianToShort(byte) ~ bigEndianToShort(byte, - * offset) - */ - @Test - @Parameters(method = "shortValues") - public void shortTest(short testShort) { - byte[] temp1 = shortToBytes(testShort); - assertEquals(testShort, bigEndianToShort(temp1)); - } - - /** - * TEST: int <--> Bytes intToBytes(int) intToBytesLE(int) intToBytesBE(int) - * intToBytesNoLeadZeroes(int) byteArrayToInt(byte) ~ intsToBytes(array, BE) ~ - * intsToBytes(array, byte, BE) ~ bytesToInts(byte, BE) ~ bytesToInts(byte, array, BE) - */ - @Test - @Parameters(method = "intValues") - public void intTest(int testInt) { - - byte[] temp1 = intToBytes(testInt); - byte[] temp2 = intToBytesLE(testInt); - byte[] temp3 = intToBytesBE(testInt); - byte[] temp4 = intToBytesNoLeadZeroes(testInt); - - toLEByteArray(temp2); - - assertEquals(testInt, byteArrayToInt(temp1)); - assertEquals(testInt, byteArrayToInt(temp2)); - assertEquals(testInt, byteArrayToInt(temp3)); - assertEquals(testInt, byteArrayToInt(temp4)); - } - - /** - * TEST: Byte validation toByte(byte) toLEByteArray(byte) firstNonZeroByte(byte) numBytes(hex) - * isNullOrZeroArray(byte) isSingleZero(byte) length(byte) stripLeadingZeroes(byte) - * appendByte(byte1, byte2) oneByteToHexString(byte) ~ matchingNibbleLength(byte1, byte2) ~ - * nibblesToPrettyString(byte) ~ difference(byteSet1, byteSet2) - */ - @Test - public void byteTest() { - - // Single 'zero' byte - String singleZero = "0"; - byte[] temp = bigIntegerToBytes(new BigInteger(singleZero)); - assertEquals(-1, firstNonZeroByte(temp)); - assertEquals(1, numBytes(singleZero)); - assertEquals(1, length(temp)); - assertFalse(isNullOrZeroArray(temp)); - assertTrue(isSingleZero(temp)); - - // Null Variable - byte[] temp0 = bigIntegerToBytes(new BigInteger(singleZero)); - assertEquals(-1, firstNonZeroByte(temp0)); - assertTrue(isNullOrZeroArray(testByte[0])); - assertNull(stripLeadingZeroes(testByte[0])); - - // Empty Array - byte[] temp1 = stripLeadingZeroes(testByte[1]); - assertEquals(-1, firstNonZeroByte(temp1)); - assertEquals(-1, firstNonZeroByte(testByte[1])); - assertTrue(isNullOrZeroArray(testByte[1])); - - // Leading Non-zero - byte[] temp2 = stripLeadingZeroes(testByte[2]); - assertEquals(0, firstNonZeroByte(temp2)); - assertEquals(0, firstNonZeroByte(testByte[2])); - assertEquals(0, firstNonZeroByte(testByte[3])); - assertEquals(0, firstNonZeroByte(testByte[4])); - - // Only Zeroes - assertEquals(-1, firstNonZeroByte(testByte[5])); - assertFalse(isNullOrZeroArray(testByte[5])); - assertFalse(isSingleZero(testByte[5])); - - // Leading Zeroes - byte[] temp3 = stripLeadingZeroes(testByte[6]); - assertEquals(0, firstNonZeroByte(temp3)); - assertEquals(31, firstNonZeroByte(testByte[6])); - - // n Byte = { 2n Hex || (256^n) - 1 } - assertEquals(1, numBytes("255")); - assertEquals(2, numBytes("65535")); - assertEquals(3, numBytes("16777215")); - assertEquals(4, numBytes("4294967295")); - assertEquals(5, numBytes("1099511627775")); - - // TFAE (+ve); - // 1) numBytes(number) - // 2) bigInt.length - // 3) hex.length()/2 - for (int a = 1; a < testBigInt.length; a++) { - byte[] temp4 = bigIntegerToBytes(testBigInt[a][1]); - String temp5 = toHexString(temp4); - assertEquals(temp5.length() / 2, temp4.length); - assertEquals(temp5.length() / 2, numBytes(testNum[a][1])); - } - - // Append - for (int a = 0; a < testHex.length; a++) { - byte[] temp6 = hexStringToBytes(testHex[a]); - byte temp7 = toByte(testByte[2]); - byte[] temp8 = appendByte(temp6, temp7); - String temp9 = oneByteToHexString(temp7); - assertEquals(testHex[2].toLowerCase(), temp9); - assertEquals(toHexString(temp6) + temp9, toHexString(temp8)); - } - } - - /** - * TEST: Bit manipulation increment(byte) copyToArray(byte) setBit(byte, pos, val) getBit(byte, - * pos) and(byte1, byte2) or(byte1, byte2) xor(byte1, byte2) xorAlignRight(byte1, byte2) - */ - @Test - public void bitTest() { - - // Increment for all byte size (+ve) - int increment = 123; - boolean max = false; - for (int a = 1; a < testBigInt.length; a++) { - - BigInteger temp1 = testBigInt[a][1]; - byte[] temp2 = bigIntegerToBytes(temp1); - BigInteger capacity = BigInteger.valueOf((long) Math.pow(255, temp2.length)); - - for (int i = 0; i < increment; i++) { - while (!max) { - max = increment(temp2); - } - temp1 = bytesToBigInteger(temp2); - max = false; - } - - BigInteger temp3 = BigInteger.valueOf(increment).mod(capacity); - BigInteger temp4 = testBigInt[a][1].add(temp3); - assertEquals(temp4, temp1); - } - - // Copy array, convert and assert (+ve) - for (int a = 1; a < testBigInt.length; a++) { - byte[] temp5 = copyToArray(testBigInt[a][1]); - BigInteger temp6 = bytesToBigInteger(temp5); - assertEquals(testBigInt[a][1], temp6); - } - - // Set bit, get bit for every bit - int shifts = 8; - for (int c = 0; c < shifts; c++) { - byte[] temp6 = bigIntegerToBytesSigned(testBigInt[1][1], 1); - setBit(temp6, c, 1); - assertEquals(1, getBit(temp6, c)); - assertEquals(BigInteger.valueOf((long) Math.pow(2, c)), bytesToBigInteger(temp6)); - } - - // AND + OR with zero (+ve) - for (int a = 2; a < testBigInt.length; a++) { - byte[] temp8 = bigIntegerToBytes(testBigInt[a][1]); - byte[] temp9 = bigIntegerToBytes(testBigInt[1][1], temp8.length); - byte[] temp10 = or(temp8, temp9); - byte[] temp11 = and(temp8, temp9); - assertEquals(testBigInt[a][1], bytesToBigInteger(temp10)); - assertEquals(testBigInt[1][1], bytesToBigInteger(temp11)); - System.out.println( - testBigInt[a][1] - + " || " - + testBigInt[1][1] - + " --> " - + bytesToBigInteger(temp10)); - System.out.println( - testBigInt[a][1] - + " && " - + testBigInt[1][1] - + " --> " - + bytesToBigInteger(temp11)); - } - - // 2's compliment -ve BI --> +ve BI - for (int a = 1; a < testBigInt.length; a++) { - boolean found = false; - int rightMost = 0; - byte[] temp7 = bigIntegerToBytes(testBigInt[a][0]); - while (!found && rightMost < temp7.length * 8) { - if (getBit(temp7, rightMost) == 1) { - found = true; - } else { - rightMost++; - } - } - if (found) { - for (int b = rightMost + 1; b < temp7.length * 8; b++) { - if (getBit(temp7, b) == 0) { - setBit(temp7, b, 1); - } else if (getBit(temp7, b) == 1) { - setBit(temp7, b, 0); - } - } - } - assertEquals(testBigInt[a][1], bytesToBigInteger(temp7)); - System.out.println(testBigInt[a][0] + " --> " + bytesToBigInteger(temp7)); - } - - // 1's compliment | XOR with "FF" * numBytes - for (int b = 0; b < 2; b++) { - for (int a = 1; a < testBigInt.length; a++) { - byte[] temp12 = bigIntegerToBytes(testBigInt[a][b]); - StringBuilder allForOne = new StringBuilder(); - for (int i = 0; i < temp12.length; i++) { - allForOne.append("FF"); - } - byte[] temp13 = hexStringToBytes(allForOne.toString()); - byte[] temp14 = xor(temp13, temp12); - byte[] temp15 = xorAlignRight(temp13, temp12); - for (int c = 0; c < temp12.length * 8; c++) { - if (getBit(temp12, c) == 0) { - setBit(temp12, c, 1); - } else if (getBit(temp12, c) == 1) { - setBit(temp12, c, 0); - } - } - assertEquals(bytesToBigInteger(temp14), bytesToBigInteger(temp12)); - assertEquals(bytesToBigInteger(temp15), bytesToBigInteger(temp12)); - } - } - } -} diff --git a/modAionImpl/build.gradle b/modAionImpl/build.gradle index 9abcd6b352..f44c926a03 100644 --- a/modAionImpl/build.gradle +++ b/modAionImpl/build.gradle @@ -11,7 +11,9 @@ sourceSets { } dependencies { - compile project(':modAionBase') + compile 'network.aion:vm-api4j:0.4.0' + compile 'network.aion:util4j:0.4.0' + compile project(':modAion') compile project(':modRlp') compile project(':modCrypto') @@ -24,8 +26,6 @@ dependencies { compile project(':modVM') compile project(':modTxPool') compile project(':aion_fastvm') - compile files('../lib/org-aion-avm-rt.jar') - //compile files('../lib/libJson.jar') compile 'org.json:json:20180813' compile 'info.picocli:picocli:3.6.1' @@ -38,6 +38,10 @@ dependencies { compile group: 'org.ow2.asm', name: 'asm-tree', version: '6.2.1' compile group: 'org.ow2.asm', name: 'asm-util', version: '6.2.1' + testCompile files('../lib/org-aion-avm-rt.jar') + testCompile files('../lib/org-aion-avm-api.jar') + + testCompile 'junit:junit:4.12' testCompile 'pl.pragmatists:JUnitParams:1.1.1' testCompile 'org.hamcrest:hamcrest-all:1.3' diff --git a/modAionImpl/src/module-info.java b/modAionImpl/src/module-info.java index a82b515c41..ad386bb2a0 100644 --- a/modAionImpl/src/module-info.java +++ b/modAionImpl/src/module-info.java @@ -2,7 +2,7 @@ uses org.aion.evtmgr.EventMgrModule; uses org.aion.txpool.TxPoolModule; - requires aion.base; + requires aion.util; requires aion.mcf; requires aion.log; requires aion.p2p; diff --git a/modAionImpl/src/org/aion/equihash/EquiUtils.java b/modAionImpl/src/org/aion/equihash/EquiUtils.java index 170c5de2b5..3462d70a30 100644 --- a/modAionImpl/src/org/aion/equihash/EquiUtils.java +++ b/modAionImpl/src/org/aion/equihash/EquiUtils.java @@ -1,7 +1,8 @@ package org.aion.equihash; -import static org.aion.base.util.ByteUtil.bytesToInts; -import static org.aion.base.util.ByteUtil.intsToBytes; + +import static org.aion.util.bytes.ByteUtil.bytesToInts; +import static org.aion.util.bytes.ByteUtil.intsToBytes; /** * This package contains utility functions commonly called across multiple equihash classes. diff --git a/modAionImpl/src/org/aion/equihash/EquiValidator.java b/modAionImpl/src/org/aion/equihash/EquiValidator.java index 25df6e8bf7..ca7edf4c8b 100644 --- a/modAionImpl/src/org/aion/equihash/EquiValidator.java +++ b/modAionImpl/src/org/aion/equihash/EquiValidator.java @@ -1,8 +1,8 @@ package org.aion.equihash; -import static org.aion.base.util.ByteUtil.bytesToInts; -import static org.aion.base.util.ByteUtil.intToBytesLE; -import static org.aion.base.util.ByteUtil.merge; +import static org.aion.util.bytes.ByteUtil.bytesToInts; +import static org.aion.util.bytes.ByteUtil.intToBytesLE; +import static org.aion.util.bytes.ByteUtil.merge; import java.util.Arrays; import org.aion.crypto.hash.Blake2b; @@ -148,14 +148,14 @@ public boolean isValidSolution(byte[] solution, byte[] blockHeader, byte[] nonce for (int i = 0; i < loopLen / 2; i++) { if (!hasCollision(X[i * 2], X[i * 2 + 1], collisionByteLength)) { - LOG.error("Invalid Solution: Collision not present"); + LOG.error("Invalid SolutionImpl: Collision not present"); System.out.println("No collision"); return false; } if (EquiUtils.indicesBefore(X[i * 2 + 1], X[i * 2], hashLen, lenIndices)) { System.out.println("Incorrect order"); - LOG.error("Invalid Solution: Index tree incorrecly ordered"); + LOG.error("Invalid SolutionImpl: Index tree incorrecly ordered"); return false; } if (!distinctIndices(X[i * 2 + 1], X[i * 2], hashLen, lenIndices)) { diff --git a/modAionImpl/src/org/aion/equihash/Equihash.java b/modAionImpl/src/org/aion/equihash/Equihash.java index f6014a7dcc..e73a3cb799 100644 --- a/modAionImpl/src/org/aion/equihash/Equihash.java +++ b/modAionImpl/src/org/aion/equihash/Equihash.java @@ -1,15 +1,16 @@ package org.aion.equihash; -import static org.aion.base.util.ByteUtil.merge; -import static org.aion.base.util.ByteUtil.toLEByteArray; -import static org.aion.base.util.Hex.toHexString; + +import static org.aion.util.bytes.ByteUtil.merge; +import static org.aion.util.bytes.ByteUtil.toLEByteArray; +import static org.aion.util.conversions.Hex.toHexString; import java.math.BigInteger; import java.util.concurrent.atomic.AtomicLong; -import org.aion.base.util.NativeLoader; import org.aion.crypto.HashUtil; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; +import org.aion.util.file.NativeLoader; import org.aion.zero.types.A0BlockHeader; import org.aion.zero.types.IAionBlock; import org.slf4j.Logger; @@ -75,7 +76,7 @@ public int[][] getSolutionsForNonce(byte[] header, byte[] nonce) { /* * Mine for a single nonce */ - public Solution mine(IAionBlock block, byte[] nonce) { + public SolutionImpl mine(IAionBlock block, byte[] nonce) { A0BlockHeader updateHeader = new A0BlockHeader(block.getHeader()); @@ -109,7 +110,7 @@ public Solution mine(IAionBlock block, byte[] nonce) { // Found a valid solution if (isValidBlock(validationBytes, target)) { - return new Solution(block, nonce, minimal); + return new SolutionImpl(block, nonce, minimal); } } diff --git a/modAionImpl/src/org/aion/equihash/EquihashMiner.java b/modAionImpl/src/org/aion/equihash/EquihashMiner.java index dd17f65507..d5d3288c7c 100644 --- a/modAionImpl/src/org/aion/equihash/EquihashMiner.java +++ b/modAionImpl/src/org/aion/equihash/EquihashMiner.java @@ -1,6 +1,7 @@ package org.aion.equihash; -import static org.aion.base.util.Hex.toHexString; + +import static org.aion.util.conversions.Hex.toHexString; import java.util.ArrayList; import java.util.Collections; @@ -12,7 +13,6 @@ import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; -import org.aion.base.util.MAF; import org.aion.evtmgr.IEvent; import org.aion.evtmgr.IEventMgr; import org.aion.evtmgr.IHandler; @@ -21,6 +21,7 @@ import org.aion.evtmgr.impl.evt.EventConsensus; import org.aion.evtmgr.impl.evt.EventMiner; import org.aion.mcf.mine.AbstractMineRunner; +import org.aion.util.others.MAF; import org.aion.zero.impl.blockchain.AionImpl; import org.aion.zero.impl.blockchain.IAionChain; import org.aion.zero.impl.config.CfgAion; @@ -191,7 +192,7 @@ private void mine() { nonce = new byte[32]; ThreadLocalRandom.current().nextBytes(nonce); - Solution s = miner.mine(block, nonce); + SolutionImpl s = miner.mine(block, nonce); if (s != null) { IEvent ev = new EventConsensus(EventConsensus.CALLBACK.ON_SOLUTION); ev.setFuncArgs(Collections.singletonList(s)); diff --git a/modAionImpl/src/org/aion/equihash/FullStepRow.java b/modAionImpl/src/org/aion/equihash/FullStepRow.java index 2048151603..e0c253001e 100644 --- a/modAionImpl/src/org/aion/equihash/FullStepRow.java +++ b/modAionImpl/src/org/aion/equihash/FullStepRow.java @@ -1,6 +1,6 @@ package org.aion.equihash; -import static org.aion.base.util.ByteUtil.intToBytes; +import static org.aion.util.bytes.ByteUtil.intToBytes; class FullStepRow extends StepRow { diff --git a/modAionImpl/src/org/aion/equihash/OptimizedEquiValidator.java b/modAionImpl/src/org/aion/equihash/OptimizedEquiValidator.java index bcfe85f47f..b9bb2cc3db 100644 --- a/modAionImpl/src/org/aion/equihash/OptimizedEquiValidator.java +++ b/modAionImpl/src/org/aion/equihash/OptimizedEquiValidator.java @@ -1,7 +1,7 @@ package org.aion.equihash; -import static org.aion.base.util.ByteUtil.intToBytesLE; -import static org.aion.base.util.ByteUtil.merge; +import static org.aion.util.bytes.ByteUtil.intToBytesLE; +import static org.aion.util.bytes.ByteUtil.merge; import java.util.HashSet; import java.util.Set; @@ -183,7 +183,7 @@ private boolean verify( // Check out of order indices if (indices[index] >= indices[index1]) { - LOG.debug("Solution validation failed - indices out of order"); + LOG.debug("SolutionImpl validation failed - indices out of order"); return false; } @@ -192,13 +192,13 @@ private boolean verify( boolean verify0 = verify(blockHeader, nonce, blake, indices, index, hash0, round - 1); if (!verify0) { - LOG.debug("Solution validation failed - unable to verify left subtree"); + LOG.debug("SolutionImpl validation failed - unable to verify left subtree"); return false; } boolean verify1 = verify(blockHeader, nonce, blake, indices, index1, hash1, round - 1); if (!verify1) { - LOG.debug("Solution validation failed - unable to verify right subtree"); + LOG.debug("SolutionImpl validation failed - unable to verify right subtree"); return false; } @@ -210,14 +210,14 @@ private boolean verify( for (int i = 0; i < bitsDiv8; i++) { if (hash[i] != 0) { - LOG.debug("Solution validation failed - Non-zero XOR"); + LOG.debug("SolutionImpl validation failed - Non-zero XOR"); return false; } } // Check remainder bits if ((bitsMod8) > 0 && (hash[bitsDiv8] >> (8 - (bitsMod8))) != 0) { - LOG.debug("Solution validation failed - Non-zero XOR"); + LOG.debug("SolutionImpl validation failed - Non-zero XOR"); return false; } @@ -237,7 +237,7 @@ private boolean verifyNative( // Check out of order indices if (indices[index] >= indices[index1]) { - LOG.debug("Solution validation failed - indices out of order"); + LOG.debug("SolutionImpl validation failed - indices out of order"); return false; } @@ -246,13 +246,13 @@ private boolean verifyNative( boolean verify0 = verifyNative(indices, index, hash0, round - 1, hashes); if (!verify0) { - LOG.debug("Solution validation failed - unable to verify left subtree"); + LOG.debug("SolutionImpl validation failed - unable to verify left subtree"); return false; } boolean verify1 = verifyNative(indices, index1, hash1, round - 1, hashes); if (!verify1) { - LOG.debug("Solution validation failed - unable to verify right subtree"); + LOG.debug("SolutionImpl validation failed - unable to verify right subtree"); return false; } @@ -264,14 +264,14 @@ private boolean verifyNative( for (int i = 0; i < bitsDiv8; i++) { if (hash[i] != 0) { - LOG.debug("Solution validation failed - Non-zero XOR"); + LOG.debug("SolutionImpl validation failed - Non-zero XOR"); return false; } } // Check remainder bits if ((bitsMod8) > 0 && (hash[bitsDiv8] >> (8 - (bitsMod8))) != 0) { - LOG.debug("Solution validation failed - Non-zero XOR"); + LOG.debug("SolutionImpl validation failed - Non-zero XOR"); return false; } diff --git a/modAionImpl/src/org/aion/equihash/Solution.java b/modAionImpl/src/org/aion/equihash/SolutionImpl.java similarity index 79% rename from modAionImpl/src/org/aion/equihash/Solution.java rename to modAionImpl/src/org/aion/equihash/SolutionImpl.java index 60e0574a3a..0d6730d442 100644 --- a/modAionImpl/src/org/aion/equihash/Solution.java +++ b/modAionImpl/src/org/aion/equihash/SolutionImpl.java @@ -1,6 +1,6 @@ package org.aion.equihash; -import org.aion.base.type.ISolution; +import org.aion.interfaces.block.Solution; import org.aion.zero.types.IAionBlock; /** @@ -9,13 +9,13 @@ * * @author Ross Kitsis (ross@nuco.io) */ -public class Solution implements ISolution { +public class SolutionImpl implements Solution { private final IAionBlock block; private final byte[] nonce; private final byte[] solution; - public Solution(IAionBlock block, byte[] nonce, byte[] solution) { + public SolutionImpl(IAionBlock block, byte[] nonce, byte[] solution) { this.block = block; this.nonce = nonce; diff --git a/modAionImpl/src/org/aion/net/Peer.java b/modAionImpl/src/org/aion/net/Peer.java index 56dde2823a..61d539e146 100644 --- a/modAionImpl/src/org/aion/net/Peer.java +++ b/modAionImpl/src/org/aion/net/Peer.java @@ -4,7 +4,7 @@ import java.util.Set; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; /** * Peer is a class intended to represent application specific information about a network node, diff --git a/modAionImpl/src/org/aion/utils/NativeLibrary.java b/modAionImpl/src/org/aion/utils/NativeLibrary.java index 35899d56f6..da6d13297a 100644 --- a/modAionImpl/src/org/aion/utils/NativeLibrary.java +++ b/modAionImpl/src/org/aion/utils/NativeLibrary.java @@ -2,7 +2,7 @@ import java.util.ArrayList; import java.util.List; -import org.aion.base.util.NativeLoader; +import org.aion.util.file.NativeLoader; public enum NativeLibrary { COMMON("common"), diff --git a/modAionImpl/src/org/aion/zero/impl/A0BCConfig.java b/modAionImpl/src/org/aion/zero/impl/A0BCConfig.java index 190b15ac9d..b23bda6799 100644 --- a/modAionImpl/src/org/aion/zero/impl/A0BCConfig.java +++ b/modAionImpl/src/org/aion/zero/impl/A0BCConfig.java @@ -1,6 +1,6 @@ package org.aion.zero.impl; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Address; import org.aion.zero.impl.core.energy.AbstractEnergyStrategyLimit; public interface A0BCConfig { diff --git a/modAionImpl/src/org/aion/zero/impl/AionBlockLoader.java b/modAionImpl/src/org/aion/zero/impl/AionBlockLoader.java index 650445d52b..ef4d66ee69 100644 --- a/modAionImpl/src/org/aion/zero/impl/AionBlockLoader.java +++ b/modAionImpl/src/org/aion/zero/impl/AionBlockLoader.java @@ -3,7 +3,6 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; -import org.aion.base.util.ExecutorPipeline; import org.aion.log.LogEnum; import org.aion.mcf.core.ImportResult; import org.aion.zero.impl.types.AionBlock; diff --git a/modAionImpl/src/org/aion/zero/impl/AionBlockchainImpl.java b/modAionImpl/src/org/aion/zero/impl/AionBlockchainImpl.java index d7fbbf6019..0b90151807 100644 --- a/modAionImpl/src/org/aion/zero/impl/AionBlockchainImpl.java +++ b/modAionImpl/src/org/aion/zero/impl/AionBlockchainImpl.java @@ -1,16 +1,16 @@ package org.aion.zero.impl; -import static java.lang.Math.max; +import static java.lang.Long.max; import static java.lang.Runtime.getRuntime; import static java.math.BigInteger.ZERO; import static java.util.Collections.emptyList; -import static org.aion.base.util.BIUtil.isMoreThan; -import static org.aion.base.util.Hex.toHexString; import static org.aion.mcf.core.ImportResult.EXIST; import static org.aion.mcf.core.ImportResult.IMPORTED_BEST; import static org.aion.mcf.core.ImportResult.IMPORTED_NOT_BEST; import static org.aion.mcf.core.ImportResult.INVALID_BLOCK; import static org.aion.mcf.core.ImportResult.NO_PARENT; +import static org.aion.util.biginteger.BIUtil.isMoreThan; +import static org.aion.util.conversions.Hex.toHexString; import java.math.BigInteger; import java.util.ArrayDeque; @@ -26,13 +26,8 @@ import java.util.Stack; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; -import org.aion.base.db.IRepository; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; -import org.aion.base.type.Hash256; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.Hex; +import org.aion.interfaces.db.Repository; +import org.aion.interfaces.db.RepositoryCache; import org.aion.crypto.HashUtil; import org.aion.equihash.EquihashMiner; import org.aion.evtmgr.IEvent; @@ -47,16 +42,20 @@ import org.aion.mcf.trie.Trie; import org.aion.mcf.trie.TrieImpl; import org.aion.mcf.trie.TrieNodeResult; -import org.aion.mcf.types.BlockIdentifier; +import org.aion.mcf.types.BlockIdentifierImpl; import org.aion.mcf.valid.BlockHeaderValidator; import org.aion.mcf.valid.GrandParentBlockHeaderValidator; import org.aion.mcf.valid.ParentBlockHeaderValidator; import org.aion.mcf.vm.types.Bloom; import org.aion.rlp.RLP; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; +import org.aion.types.Hash256; +import org.aion.util.bytes.ByteUtil; +import org.aion.util.conversions.Hex; import org.aion.vm.BulkExecutor; import org.aion.vm.ExecutionBatch; import org.aion.vm.PostExecutionWork; -import org.aion.vm.api.interfaces.Address; import org.aion.zero.exceptions.HeaderStructureException; import org.aion.zero.impl.blockchain.ChainConfiguration; import org.aion.zero.impl.config.CfgAion; @@ -108,7 +107,7 @@ public class AionBlockchainImpl implements IAionBlockchain { private long exitOn = Long.MAX_VALUE; private AionRepositoryImpl repository; - private IRepositoryCache track; + private RepositoryCache track; private TransactionStore<AionTransaction, AionTxReceipt, org.aion.zero.impl.types.AionTxInfo> transactionStore; private AionBlock bestBlock; @@ -132,8 +131,8 @@ public class AionBlockchainImpl implements IAionBlockchain { private final GrandParentBlockHeaderValidator<A0BlockHeader> grandParentBlockHeaderValidator; private final ParentBlockHeaderValidator<A0BlockHeader> parentHeaderValidator; private final BlockHeaderValidator<A0BlockHeader> blockHeaderValidator; - private AtomicReference<BlockIdentifier> bestKnownBlock = - new AtomicReference<BlockIdentifier>(); + private AtomicReference<BlockIdentifierImpl> bestKnownBlock = + new AtomicReference<BlockIdentifierImpl>(); private boolean fork = false; @@ -180,7 +179,7 @@ public boolean getExitOnBlockConflict() { @Override public Address getMinerCoinbase() { - return AionAddress.wrap(cfgAion.getConsensus().getMinerAddress()); + return Address.wrap(cfgAion.getConsensus().getMinerAddress()); } // TODO: hook up to configuration file @@ -231,9 +230,9 @@ protected AionBlockchainImpl( this.transactionStore = this.repository.getTransactionStore(); - this.minerCoinbase = this.config.getMinerCoinbase(); - if (minerCoinbase.isEmptyAddress()) { + this.minerCoinbase = this.config.getMinerCoinbase(); + if (minerCoinbase == null) { LOG.warn("No miner Coinbase!"); } @@ -1034,7 +1033,7 @@ private boolean isValid(AionBlock block) { } if (txs != null && !txs.isEmpty()) { - IRepository parentRepo = repository; + Repository parentRepo = repository; if (!Arrays.equals(bestBlock.getHash(), block.getParentHash())) { parentRepo = repository.getSnapshotTo( @@ -1525,7 +1524,7 @@ private byte[] getStartHash(long blockNumber, int qty) { // * @return {@link A0BlockHeader}'s list or empty list if none found // */ // @Override - // public List<A0BlockHeader> getListOfHeadersStartFrom(BlockIdentifier identifier, int skip, + // public List<A0BlockHeader> getListOfHeadersStartFrom(BlockIdentifierImpl identifier, int skip, // int limit, // boolean reverse) { // @@ -1696,7 +1695,7 @@ private void updateBestKnownBlock(AionBlock block) { private void updateBestKnownBlock(A0BlockHeader header) { if (bestKnownBlock.get() == null || header.getNumber() > bestKnownBlock.get().getNumber()) { - bestKnownBlock.set(new BlockIdentifier(header.getHash(), header.getNumber())); + bestKnownBlock.set(new BlockIdentifierImpl(header.getHash(), header.getNumber())); } } @@ -1705,7 +1704,7 @@ public IEventMgr getEventMgr() { } @Override - public synchronized boolean recoverWorldState(IRepository repository, AionBlock block) { + public synchronized boolean recoverWorldState(Repository repository, AionBlock block) { if (block == null) { LOG.error("World state recovery attempted with null block."); return false; @@ -1788,7 +1787,7 @@ public synchronized boolean recoverWorldState(IRepository repository, AionBlock } @Override - public synchronized boolean recoverIndexEntry(IRepository repository, AionBlock block) { + public synchronized boolean recoverIndexEntry(Repository repository, AionBlock block) { if (block == null) { LOG.error("Index recovery attempted with null block."); return false; diff --git a/modAionImpl/src/org/aion/zero/impl/AionGenesis.java b/modAionImpl/src/org/aion/zero/impl/AionGenesis.java index 2748220be6..444bc63bb9 100644 --- a/modAionImpl/src/org/aion/zero/impl/AionGenesis.java +++ b/modAionImpl/src/org/aion/zero/impl/AionGenesis.java @@ -4,17 +4,16 @@ import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.crypto.HashUtil; import org.aion.mcf.core.AccountState; import org.aion.mcf.trie.SecureTrie; import org.aion.mcf.trie.Trie; import org.aion.mcf.types.AbstractBlockHeader; -import org.aion.mcf.vm.types.DataWord; import org.aion.precompiled.ContractFactory; -import org.aion.vm.api.interfaces.Address; +import org.aion.util.bytes.ByteUtil; import org.aion.zero.db.AionContractDetailsImpl; import org.aion.zero.exceptions.HeaderStructureException; import org.aion.zero.impl.types.AionBlock; @@ -49,7 +48,7 @@ public class AionGenesis extends AionBlock { * Corresponds to {@link AbstractBlockHeader#getCoinbase()} that mined the first block. For * fairness, the address is set to an address that is not ever to be used */ - protected static final Address GENESIS_COINBASE = AionAddress.ZERO_ADDRESS(); + protected static final Address GENESIS_COINBASE = Address.ZERO_ADDRESS(); /** * Corresponds to {@link AbstractBlockHeader#getLogsBloom()} indicates the logsBloom of the @@ -351,8 +350,8 @@ protected byte[] generateRootHash() { for (Map.Entry<Integer, BigInteger> entry : this.networkBalance.entrySet()) { // we assume there are no deletions in the genesis networkBalanceStorage.put( - new DataWord(entry.getKey()).toWrapper(), - wrapValueForPut(new DataWord(entry.getValue()))); + new DataWordImpl(entry.getKey()).toWrapper(), + wrapValueForPut(new DataWordImpl(entry.getValue()))); } byte[] networkBalanceStorageHash = networkBalanceStorage.getStorageHash(); @@ -370,9 +369,9 @@ protected byte[] generateRootHash() { return worldTrie.getRootHash(); } - private static ByteArrayWrapper wrapValueForPut(DataWord value) { + private static ByteArrayWrapper wrapValueForPut(DataWordImpl value) { return (value.isZero()) - ? DataWord.ZERO.toWrapper() + ? DataWordImpl.ZERO.toWrapper() : new ByteArrayWrapper(value.getNoLeadZeroesData()); } diff --git a/modAionImpl/src/org/aion/zero/impl/AionHub.java b/modAionImpl/src/org/aion/zero/impl/AionHub.java index ad667833a4..9cf3938be8 100644 --- a/modAionImpl/src/org/aion/zero/impl/AionHub.java +++ b/modAionImpl/src/org/aion/zero/impl/AionHub.java @@ -9,8 +9,7 @@ import java.util.Properties; import java.util.ServiceLoader; import java.util.concurrent.atomic.AtomicBoolean; -import org.aion.base.db.IRepository; -import org.aion.base.util.ByteUtil; +import org.aion.interfaces.db.Repository; import org.aion.evtmgr.EventMgrModule; import org.aion.evtmgr.IEvent; import org.aion.evtmgr.IEventMgr; @@ -24,6 +23,7 @@ import org.aion.p2p.Handler; import org.aion.p2p.IP2pMgr; import org.aion.p2p.impl1.P2pMgr; +import org.aion.util.bytes.ByteUtil; import org.aion.zero.impl.blockchain.AionPendingStateImpl; import org.aion.zero.impl.blockchain.ChainConfiguration; import org.aion.zero.impl.config.CfgAion; @@ -259,8 +259,8 @@ private void loadEventMgr(boolean forTest) { } } - public IRepository getRepository() { - return repository; + public Repository getRepository() { + return (Repository) repository; } public IAionBlockchain getBlockchain() { @@ -339,7 +339,7 @@ private void loadBlockchain() { } } - recovered = this.blockchain.recoverWorldState(this.repository, bestBlock); + recovered = this.blockchain.recoverWorldState((Repository) this.repository, bestBlock); if (!this.repository.isValidRoot(bestBlock.getStateRoot())) { // reverting back one block diff --git a/modAionImpl/src/org/aion/zero/impl/AionHubUtils.java b/modAionImpl/src/org/aion/zero/impl/AionHubUtils.java index 09bc4661dd..600f2390ef 100644 --- a/modAionImpl/src/org/aion/zero/impl/AionHubUtils.java +++ b/modAionImpl/src/org/aion/zero/impl/AionHubUtils.java @@ -2,11 +2,11 @@ import java.math.BigInteger; import java.util.Map; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.mcf.vm.types.DataWord; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.precompiled.ContractFactory; -import org.aion.vm.api.interfaces.Address; import org.aion.zero.impl.db.AionRepositoryImpl; /** {@link AionHub} functionality where a full instantiation of the class is not desirable. */ @@ -14,7 +14,7 @@ public class AionHubUtils { public static void buildGenesis(AionGenesis genesis, AionRepositoryImpl repository) { // initialization section for network balance contract - IRepositoryCache track = repository.startTracking(); + RepositoryCache track = repository.startTracking(); Address networkBalanceAddress = ContractFactory.getTotalCurrencyContractAddress(); track.createAccount(networkBalanceAddress); @@ -23,8 +23,8 @@ public static void buildGenesis(AionGenesis genesis, AionRepositoryImpl reposito // assumes only additions are performed in the genesis track.addStorageRow( networkBalanceAddress, - new DataWord(addr.getKey()).toWrapper(), - wrapValueForPut(new DataWord(addr.getValue()))); + new DataWordImpl(addr.getKey()).toWrapper(), + wrapValueForPut(new DataWordImpl(addr.getValue()))); } for (Address addr : genesis.getPremine().keySet()) { @@ -37,7 +37,7 @@ public static void buildGenesis(AionGenesis genesis, AionRepositoryImpl reposito repository.getBlockStore().saveBlock(genesis, genesis.getDifficultyBI(), true); } - private static ByteArrayWrapper wrapValueForPut(DataWord value) { + private static ByteArrayWrapper wrapValueForPut(DataWordImpl value) { return (value.isZero()) ? value.toWrapper() : new ByteArrayWrapper(value.getNoLeadZeroesData()); diff --git a/modAionBase/src/org/aion/base/util/ExecutorPipeline.java b/modAionImpl/src/org/aion/zero/impl/ExecutorPipeline.java similarity index 98% rename from modAionBase/src/org/aion/base/util/ExecutorPipeline.java rename to modAionImpl/src/org/aion/zero/impl/ExecutorPipeline.java index 399d18e3d8..043f0c9969 100644 --- a/modAionBase/src/org/aion/base/util/ExecutorPipeline.java +++ b/modAionImpl/src/org/aion/zero/impl/ExecutorPipeline.java @@ -1,4 +1,6 @@ -package org.aion.base.util; +package org.aion.zero.impl; + +import org.aion.interfaces.functional.Functional; import java.util.HashMap; import java.util.List; diff --git a/modAionImpl/src/org/aion/zero/impl/GenesisBlockLoader.java b/modAionImpl/src/org/aion/zero/impl/GenesisBlockLoader.java index f616912f1a..193021b00b 100644 --- a/modAionImpl/src/org/aion/zero/impl/GenesisBlockLoader.java +++ b/modAionImpl/src/org/aion/zero/impl/GenesisBlockLoader.java @@ -7,9 +7,9 @@ import java.io.InputStream; import java.math.BigInteger; import java.util.regex.Pattern; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteUtil; +import org.aion.types.Address; import org.aion.mcf.core.AccountState; +import org.aion.util.bytes.ByteUtil; import org.aion.zero.exceptions.HeaderStructureException; import org.json.JSONException; import org.json.JSONObject; @@ -50,7 +50,7 @@ public static AionGenesis loadJSON(String filePath) } if (mapper.has("coinbase")) { - genesisBuilder.withCoinbase(AionAddress.wrap(mapper.getString("coinbase"))); + genesisBuilder.withCoinbase(Address.wrap(mapper.getString("coinbase"))); } if (mapper.has("difficulty")) { @@ -130,7 +130,7 @@ public static AionGenesis loadJSON(String filePath) new BigInteger( accountAllocs.getJSONObject(key).getString("balance")); AccountState acctState = new AccountState(BigInteger.ZERO, balance); - genesisBuilder.addPreminedAccount(AionAddress.wrap(key), acctState); + genesisBuilder.addPreminedAccount(Address.wrap(key), acctState); } } diff --git a/modAionImpl/src/org/aion/zero/impl/StandaloneBlockchain.java b/modAionImpl/src/org/aion/zero/impl/StandaloneBlockchain.java index 57b93393e9..3106275501 100644 --- a/modAionImpl/src/org/aion/zero/impl/StandaloneBlockchain.java +++ b/modAionImpl/src/org/aion/zero/impl/StandaloneBlockchain.java @@ -8,13 +8,13 @@ import java.util.List; import java.util.Map; import java.util.Properties; -import org.aion.base.db.IContractDetails; -import org.aion.base.db.IPruneConfig; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.db.IRepositoryConfig; -import org.aion.base.type.AionAddress; -import org.aion.base.type.Hash256; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.interfaces.db.ContractDetails; +import org.aion.interfaces.db.PruneConfig; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.interfaces.db.RepositoryConfig; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; import org.aion.crypto.HashUtil; @@ -24,9 +24,8 @@ import org.aion.mcf.core.AccountState; import org.aion.mcf.core.ImportResult; import org.aion.mcf.valid.BlockHeaderValidator; -import org.aion.mcf.vm.types.DataWord; import org.aion.precompiled.ContractFactory; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Hash256; import org.aion.zero.exceptions.HeaderStructureException; import org.aion.zero.impl.blockchain.ChainConfiguration; import org.aion.zero.impl.core.energy.AbstractEnergyStrategyLimit; @@ -51,20 +50,20 @@ public class StandaloneBlockchain extends AionBlockchainImpl { public AionGenesis genesis; - private static IRepositoryConfig repoConfig = - new IRepositoryConfig() { + private static RepositoryConfig repoConfig = + new RepositoryConfig() { @Override public String getDbPath() { return ""; } @Override - public IPruneConfig getPruneConfig() { + public PruneConfig getPruneConfig() { return new CfgPrune(false); } @Override - public IContractDetails contractDetailsImpl() { + public ContractDetails contractDetailsImpl() { return ContractDetailsAion.createForTesting(0, 1000000).getDetails(); } @@ -84,7 +83,7 @@ protected StandaloneBlockchain(final A0BCConfig config, final ChainConfiguration protected StandaloneBlockchain( final A0BCConfig config, final ChainConfiguration chainConfig, - IRepositoryConfig repoConfig) { + RepositoryConfig repoConfig) { super(config, AionRepositoryImpl.createForTesting(repoConfig), chainConfig); } @@ -123,7 +122,7 @@ public static class Builder { private List<ECKey> defaultKeys = new ArrayList<>(); private Map<ByteArrayWrapper, AccountState> initialState = new HashMap<>(); - private IRepositoryConfig repoConfig; + private RepositoryConfig repoConfig; public static final int INITIAL_ACC_LEN = 10; public static final BigInteger DEFAULT_BALANCE = @@ -164,7 +163,7 @@ public Builder withDefaultAccounts(List<ECKey> defaultAccounts) { return this; } - public Builder withRepoConfig(IRepositoryConfig config) { + public Builder withRepoConfig(RepositoryConfig config) { this.repoConfig = config; return this; } @@ -193,20 +192,20 @@ public Builder withAccount(ByteArrayWrapper publicKey, AccountState accState) { return this; } - private IRepositoryConfig generateRepositoryConfig() { - return new IRepositoryConfig() { + private RepositoryConfig generateRepositoryConfig() { + return new RepositoryConfig() { @Override public String getDbPath() { return ""; } @Override - public IPruneConfig getPruneConfig() { + public PruneConfig getPruneConfig() { return new CfgPrune(false); } @Override - public IContractDetails contractDetailsImpl() { + public ContractDetails contractDetailsImpl() { return ContractDetailsAion.createForTesting(0, 1000000).getDetails(); } @@ -226,7 +225,7 @@ public Bundle build() { ? new A0BCConfig() { @Override public Address getCoinbase() { - return AionAddress.ZERO_ADDRESS(); + return Address.ZERO_ADDRESS(); } @Override @@ -241,7 +240,7 @@ public boolean getExitOnBlockConflict() { @Override public Address getMinerCoinbase() { - return AionAddress.ZERO_ADDRESS(); + return Address.ZERO_ADDRESS(); } @Override @@ -309,7 +308,7 @@ public boolean isAvmEnabled() { AionGenesis.Builder genesisBuilder = new AionGenesis.Builder(); for (Map.Entry<ByteArrayWrapper, AccountState> acc : this.initialState.entrySet()) { - genesisBuilder.addPreminedAccount(AionAddress.wrap(acc.getKey()), acc.getValue()); + genesisBuilder.addPreminedAccount(Address.wrap(acc.getKey()), acc.getValue()); } AionGenesis genesis; @@ -320,15 +319,15 @@ public boolean isAvmEnabled() { } bc.genesis = genesis; - IRepositoryCache track = bc.getRepository().startTracking(); + RepositoryCache track = bc.getRepository().startTracking(); track.createAccount(ContractFactory.getTotalCurrencyContractAddress()); for (Map.Entry<Integer, BigInteger> key : genesis.getNetworkBalances().entrySet()) { // assumes only additions can be made in the genesis track.addStorageRow( ContractFactory.getTotalCurrencyContractAddress(), - new DataWord(key.getKey()).toWrapper(), - new ByteArrayWrapper(new DataWord(key.getValue()).getNoLeadZeroesData())); + new DataWordImpl(key.getKey()).toWrapper(), + new ByteArrayWrapper(new DataWordImpl(key.getValue()).getNoLeadZeroesData())); } for (Address key : genesis.getPremine().keySet()) { diff --git a/modAionImpl/src/org/aion/zero/impl/blockchain/AionImpl.java b/modAionImpl/src/org/aion/zero/impl/blockchain/AionImpl.java index 48fb3230ec..045a70770b 100644 --- a/modAionImpl/src/org/aion/zero/impl/blockchain/AionImpl.java +++ b/modAionImpl/src/org/aion/zero/impl/blockchain/AionImpl.java @@ -4,13 +4,10 @@ import java.util.Collections; import java.util.List; import java.util.Optional; -import org.aion.base.db.IRepository; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; import org.aion.crypto.ECKeyFac; import org.aion.equihash.EquihashMiner; +import org.aion.interfaces.db.Repository; +import org.aion.interfaces.db.RepositoryCache; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.aion.mcf.blockchain.IPendingStateInternal; @@ -18,10 +15,12 @@ import org.aion.mcf.core.AccountState; import org.aion.mcf.core.ImportResult; import org.aion.mcf.mine.IMineRunner; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; +import org.aion.util.bytes.ByteUtil; import org.aion.vm.BulkExecutor; import org.aion.vm.ExecutionBatch; import org.aion.vm.PostExecutionWork; -import org.aion.vm.api.interfaces.Address; import org.aion.zero.impl.AionHub; import org.aion.zero.impl.config.CfgAion; import org.aion.zero.impl.tx.TxCollector; @@ -44,14 +43,6 @@ public class AionImpl implements IAionChain { private TxCollector collector; - private static class Holder { - static final AionImpl INSTANCE = new AionImpl(); - } - - public static AionImpl inst() { - return Holder.INSTANCE; - } - private AionImpl() { this.cfg = CfgAion.inst(); aionHub = new AionHub(); @@ -67,6 +58,18 @@ private AionImpl() { collector = new TxCollector(this.aionHub.getP2pMgr(), LOG_TX); } + public static AionImpl inst() { + return Holder.INSTANCE; + } + + /** + * There is no post-execution work to do for any calls in this class to do. In accordance with + * the specs, we return zero since we have no meaningful value to return here. + */ + private static PostExecutionWork getPostExecutionWork() { + return (r, c, s, t, b) -> 0; + } + @Override public IPowChain<AionBlock, A0BlockHeader> getBlockchain() { return aionHub.getBlockchain(); @@ -84,14 +87,14 @@ public synchronized ImportResult addNewMinedBlock(AionBlock block) { @Override public IMineRunner getBlockMiner() { - AionAddress minerCoinbase = AionAddress.wrap(this.cfg.getConsensus().getMinerAddress()); - - if (minerCoinbase.isEmptyAddress()) { + try { + Address.wrap(this.cfg.getConsensus().getMinerAddress()); + return EquihashMiner.inst(); + } catch (Exception e) { LOG_GEN.info("Miner address is not set"); return null; } - return EquihashMiner.inst(); } @Override @@ -131,7 +134,7 @@ public long estimateTxNrg(AionTransaction tx, IAionBlock block) { tx.sign(ECKeyFac.inst().fromPrivate(new byte[64])); } - IRepositoryCache repository = + RepositoryCache repository = aionHub.getRepository().getSnapshotTo(block.getStateRoot()).startTracking(); try { @@ -158,7 +161,7 @@ public AionTxReceipt callConstant(AionTransaction tx, IAionBlock block) { tx.sign(ECKeyFac.inst().fromPrivate(new byte[64])); } - IRepositoryCache repository = + RepositoryCache repository = aionHub.getRepository().getSnapshotTo(block.getStateRoot()).startTracking(); try { @@ -178,30 +181,20 @@ public AionTxReceipt callConstant(AionTransaction tx, IAionBlock block) { } } - /** - * There is no post-execution work to do for any calls in this class to do. In accordance with - * the specs, we return zero since we have no meaningful value to return here. - */ - private static PostExecutionWork getPostExecutionWork() { - return (r, c, s, t, b) -> { - return 0; - }; - } - @Override - public IRepository getRepository() { + public Repository getRepository() { return aionHub.getRepository(); } @Override - public IRepository<?, ?> getPendingState() { + public Repository<?, ?> getPendingState() { return aionHub.getPendingState().getRepository(); } @Override - public IRepository<?, ?> getSnapshotTo(byte[] root) { - IRepository<?, ?> repository = aionHub.getRepository(); - IRepository<?, ?> snapshot = repository.getSnapshotTo(root); + public Repository<?, ?> getSnapshotTo(byte[] root) { + Repository<?, ?> repository = aionHub.getRepository(); + Repository<?, ?> snapshot = repository.getSnapshotTo(root); return snapshot; } @@ -357,4 +350,8 @@ public Optional<ByteArrayWrapper> getCode(Address address) { if (code == null) return Optional.empty(); return Optional.of(new ByteArrayWrapper(code)); } + + private static class Holder { + static final AionImpl INSTANCE = new AionImpl(); + } } diff --git a/modAionImpl/src/org/aion/zero/impl/blockchain/AionPendingStateImpl.java b/modAionImpl/src/org/aion/zero/impl/blockchain/AionPendingStateImpl.java index 7ff8707134..d9deb506ba 100644 --- a/modAionImpl/src/org/aion/zero/impl/blockchain/AionPendingStateImpl.java +++ b/modAionImpl/src/org/aion/zero/impl/blockchain/AionPendingStateImpl.java @@ -21,11 +21,9 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.aion.base.Constant; -import org.aion.base.db.IRepository; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.Hex; +import org.aion.interfaces.block.Constant; +import org.aion.interfaces.db.Repository; +import org.aion.interfaces.db.RepositoryCache; import org.aion.evtmgr.IEvent; import org.aion.evtmgr.IEventMgr; import org.aion.evtmgr.IHandler; @@ -43,10 +41,12 @@ import org.aion.p2p.IP2pMgr; import org.aion.txpool.ITxPool; import org.aion.txpool.TxPoolModule; +import org.aion.types.Address; +import org.aion.util.bytes.ByteUtil; +import org.aion.util.conversions.Hex; import org.aion.vm.BulkExecutor; import org.aion.vm.ExecutionBatch; import org.aion.vm.PostExecutionWork; -import org.aion.vm.api.interfaces.Address; import org.aion.zero.impl.AionBlockchainImpl; import org.aion.zero.impl.config.CfgAion; import org.aion.zero.impl.core.IAionBlockchain; @@ -95,13 +95,13 @@ public TransactionSortedSet() { private TransactionStore<AionTransaction, AionTxReceipt, AionTxInfo> transactionStore; - private IRepository repository; + private Repository repository; private ITxPool<AionTransaction> txPool; private IEventMgr evtMgr = null; - private IRepositoryCache pendingState; + private RepositoryCache pendingState; private AtomicReference<AionBlock> best; @@ -283,7 +283,7 @@ public static AionPendingStateImpl createForTesting( private AionPendingStateImpl(CfgAion _cfgAion, AionRepositoryImpl _repository) { - this.repository = _repository; + this.repository = (Repository) _repository; this.isSeed = _cfgAion.getConsensus().isSeed(); @@ -401,7 +401,7 @@ private void regTxEvents() { } @Override - public synchronized IRepositoryCache<?, ?> getRepository() { + public synchronized RepositoryCache<?, ?> getRepository() { // Todo : no class use this method. return pendingState; } diff --git a/modAionImpl/src/org/aion/zero/impl/blockchain/ChainConfiguration.java b/modAionImpl/src/org/aion/zero/impl/blockchain/ChainConfiguration.java index bad21ca04a..c0a2de4678 100644 --- a/modAionImpl/src/org/aion/zero/impl/blockchain/ChainConfiguration.java +++ b/modAionImpl/src/org/aion/zero/impl/blockchain/ChainConfiguration.java @@ -2,6 +2,7 @@ import java.math.BigInteger; import java.util.Arrays; +import java.util.Collections; import org.aion.equihash.OptimizedEquiValidator; import org.aion.mcf.blockchain.IBlockConstants; import org.aion.mcf.blockchain.IChainCfg; @@ -13,11 +14,12 @@ import org.aion.mcf.valid.GrandParentBlockHeaderValidator; import org.aion.mcf.valid.ParentBlockHeaderValidator; import org.aion.mcf.valid.TimeStampRule; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Address; import org.aion.zero.api.BlockConstants; import org.aion.zero.impl.config.CfgAion; import org.aion.zero.impl.core.DiffCalc; import org.aion.zero.impl.core.RewardsCalculator; +import org.aion.zero.impl.types.AionBlock; import org.aion.zero.impl.valid.AionDifficultyRule; import org.aion.zero.impl.valid.AionExtraDataRule; import org.aion.zero.impl.valid.AionHeaderVersionRule; @@ -27,7 +29,6 @@ import org.aion.zero.impl.valid.EquihashSolutionRule; import org.aion.zero.types.A0BlockHeader; import org.aion.zero.types.AionTransaction; -import org.aion.zero.types.IAionBlock; /** * Chain configuration handles the default parameters on a particular chain. Also handles the @@ -36,7 +37,7 @@ * * @author yao */ -public class ChainConfiguration implements IChainCfg<IAionBlock, AionTransaction> { +public class ChainConfiguration implements IChainCfg<AionBlock, AionTransaction> { protected BlockConstants constants; protected IMiner<?, ?> miner; @@ -123,6 +124,7 @@ public ParentBlockHeaderValidator<A0BlockHeader> createParentHeaderValidator() { } public GrandParentBlockHeaderValidator<A0BlockHeader> createGrandParentHeaderValidator() { - return new GrandParentBlockHeaderValidator<>(Arrays.asList(new AionDifficultyRule(this))); + return new GrandParentBlockHeaderValidator<>( + Collections.singletonList(new AionDifficultyRule(this))); } } diff --git a/modAionImpl/src/org/aion/zero/impl/blockchain/IAionChain.java b/modAionImpl/src/org/aion/zero/impl/blockchain/IAionChain.java index 6602c356e3..354084c2d2 100644 --- a/modAionImpl/src/org/aion/zero/impl/blockchain/IAionChain.java +++ b/modAionImpl/src/org/aion/zero/impl/blockchain/IAionChain.java @@ -2,10 +2,10 @@ import java.math.BigInteger; import java.util.List; -import org.aion.base.db.IRepository; +import org.aion.interfaces.db.Repository; import org.aion.mcf.blockchain.IChainInstancePOW; import org.aion.mcf.blockchain.IPowChain; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Address; import org.aion.zero.impl.AionHub; import org.aion.zero.impl.query.QueryInterface; import org.aion.zero.impl.types.AionBlock; @@ -27,11 +27,11 @@ public interface IAionChain extends IChainInstancePOW, QueryInterface { AionTxReceipt callConstant(AionTransaction tx, IAionBlock block); - IRepository<?, ?> getRepository(); + Repository<?, ?> getRepository(); - IRepository<?, ?> getPendingState(); + Repository<?, ?> getPendingState(); - IRepository<?, ?> getSnapshotTo(byte[] root); + Repository<?, ?> getSnapshotTo(byte[] root); List<AionTransaction> getWireTransactions(); diff --git a/modAionImpl/src/org/aion/zero/impl/blockchain/PendingTxCache.java b/modAionImpl/src/org/aion/zero/impl/blockchain/PendingTxCache.java index 7681dc5cd3..33ec9a8fff 100644 --- a/modAionImpl/src/org/aion/zero/impl/blockchain/PendingTxCache.java +++ b/modAionImpl/src/org/aion/zero/impl/blockchain/PendingTxCache.java @@ -10,10 +10,9 @@ import java.util.Set; import java.util.TreeMap; import java.util.concurrent.atomic.AtomicInteger; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; -import org.aion.vm.api.interfaces.Address; import org.aion.zero.types.AionTransaction; import org.apache.commons.collections4.map.LRUMap; import org.slf4j.Logger; diff --git a/modAionImpl/src/org/aion/zero/impl/cli/Cli.java b/modAionImpl/src/org/aion/zero/impl/cli/Cli.java index ee1818809a..e35905321e 100644 --- a/modAionImpl/src/org/aion/zero/impl/cli/Cli.java +++ b/modAionImpl/src/org/aion/zero/impl/cli/Cli.java @@ -18,13 +18,13 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import org.aion.base.util.Hex; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; import org.aion.mcf.account.Keystore; import org.aion.mcf.config.Cfg; import org.aion.mcf.config.CfgSsl; import org.aion.mcf.config.CfgSync; +import org.aion.util.conversions.Hex; import org.aion.zero.impl.Version; import org.aion.zero.impl.config.Network; import org.aion.zero.impl.db.RecoveryUtils; diff --git a/modAionImpl/src/org/aion/zero/impl/config/CfgConsensusPow.java b/modAionImpl/src/org/aion/zero/impl/config/CfgConsensusPow.java index 0fb38882b0..a1e135445c 100644 --- a/modAionImpl/src/org/aion/zero/impl/config/CfgConsensusPow.java +++ b/modAionImpl/src/org/aion/zero/impl/config/CfgConsensusPow.java @@ -7,7 +7,7 @@ import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamWriter; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.mcf.config.Cfg; import org.aion.mcf.config.CfgConsensus; @@ -17,7 +17,7 @@ public final class CfgConsensusPow extends CfgConsensus { CfgConsensusPow() { this.mining = false; - this.minerAddress = AionAddress.ZERO_ADDRESS().toString(); + this.minerAddress = Address.ZERO_ADDRESS().toString(); this.cpuMineThreads = (byte) (Runtime.getRuntime().availableProcessors() diff --git a/modAionImpl/src/org/aion/zero/impl/core/BloomFilter.java b/modAionImpl/src/org/aion/zero/impl/core/BloomFilter.java index f57018551f..eb39393394 100644 --- a/modAionImpl/src/org/aion/zero/impl/core/BloomFilter.java +++ b/modAionImpl/src/org/aion/zero/impl/core/BloomFilter.java @@ -2,7 +2,7 @@ import org.aion.crypto.HashUtil; import org.aion.mcf.vm.types.Bloom; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Address; public class BloomFilter { public static boolean containsAddress(Bloom bloom, Address address) { diff --git a/modAionImpl/src/org/aion/zero/impl/core/DiffCalc.java b/modAionImpl/src/org/aion/zero/impl/core/DiffCalc.java index 8fcdafa2f4..cd648a955a 100644 --- a/modAionImpl/src/org/aion/zero/impl/core/DiffCalc.java +++ b/modAionImpl/src/org/aion/zero/impl/core/DiffCalc.java @@ -1,7 +1,7 @@ package org.aion.zero.impl.core; -import static org.aion.base.util.BIUtil.max; -import static org.aion.base.util.BIUtil.min; +import static org.aion.util.biginteger.BIUtil.max; +import static org.aion.util.biginteger.BIUtil.min; import java.math.BigInteger; import org.aion.mcf.blockchain.IBlockConstants; diff --git a/modAionImpl/src/org/aion/zero/impl/core/IAionBlockchain.java b/modAionImpl/src/org/aion/zero/impl/core/IAionBlockchain.java index be049ec165..eb50ec75ba 100644 --- a/modAionImpl/src/org/aion/zero/impl/core/IAionBlockchain.java +++ b/modAionImpl/src/org/aion/zero/impl/core/IAionBlockchain.java @@ -2,9 +2,9 @@ import java.util.List; import java.util.Map; -import org.aion.base.db.IRepository; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.interfaces.db.Repository; import org.aion.mcf.core.IBlockchain; +import org.aion.types.ByteArrayWrapper; import org.aion.zero.impl.BlockContext; import org.aion.zero.impl.sync.DatabaseType; import org.aion.zero.impl.types.AionBlock; @@ -32,14 +32,14 @@ BlockContext createNewBlockContext( * * @return {@code true} if the recovery was successful, {@code false} otherwise */ - boolean recoverWorldState(IRepository repository, AionBlock block); + boolean recoverWorldState(Repository repository, AionBlock block); /** * Recovery functionality for recreating the block info in the index database. * * @return {@code true} if the recovery was successful, {@code false} otherwise */ - boolean recoverIndexEntry(IRepository repository, AionBlock block); + boolean recoverIndexEntry(Repository repository, AionBlock block); /** * Heuristic for skipping the call to tryToConnect with very large or very small block number. diff --git a/modAionImpl/src/org/aion/zero/impl/db/AionBlockStore.java b/modAionImpl/src/org/aion/zero/impl/db/AionBlockStore.java index 152e9a44f7..55cc392901 100644 --- a/modAionImpl/src/org/aion/zero/impl/db/AionBlockStore.java +++ b/modAionImpl/src/org/aion/zero/impl/db/AionBlockStore.java @@ -21,9 +21,7 @@ import java.util.Map; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import org.aion.base.db.IByteArrayKeyValueDatabase; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.Hex; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.aion.mcf.db.AbstractPowBlockstore; @@ -33,6 +31,8 @@ import org.aion.rlp.RLP; import org.aion.rlp.RLPElement; import org.aion.rlp.RLPList; +import org.aion.util.bytes.ByteUtil; +import org.aion.util.conversions.Hex; import org.aion.zero.impl.types.AionBlock; import org.aion.zero.types.A0BlockHeader; import org.aion.zero.types.IAionBlock; @@ -54,19 +54,19 @@ public class AionBlockStore extends AbstractPowBlockstore<AionBlock, A0BlockHead preBranchingBlk = new ArrayDeque<>(); private long branchingLevel; - public AionBlockStore(IByteArrayKeyValueDatabase index, IByteArrayKeyValueDatabase blocks) { + public AionBlockStore(ByteArrayKeyValueDatabase index, ByteArrayKeyValueDatabase blocks) { init(index, blocks); } public AionBlockStore( - IByteArrayKeyValueDatabase index, - IByteArrayKeyValueDatabase blocks, + ByteArrayKeyValueDatabase index, + ByteArrayKeyValueDatabase blocks, boolean checkIntegrity) { this(index, blocks); this.checkIntegrity = checkIntegrity; } - private void init(IByteArrayKeyValueDatabase index, IByteArrayKeyValueDatabase blocks) { + private void init(ByteArrayKeyValueDatabase index, ByteArrayKeyValueDatabase blocks) { this.index = new DataSourceArray<>(new ObjectDataSource<>(index, BLOCK_INFO_SERIALIZER)); diff --git a/modAionImpl/src/org/aion/zero/impl/db/AionRepositoryDummy.java b/modAionImpl/src/org/aion/zero/impl/db/AionRepositoryDummy.java index df44bb878a..bab70ddd73 100644 --- a/modAionImpl/src/org/aion/zero/impl/db/AionRepositoryDummy.java +++ b/modAionImpl/src/org/aion/zero/impl/db/AionRepositoryDummy.java @@ -6,14 +6,13 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; -import org.aion.base.db.IContractDetails; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.db.IRepositoryConfig; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.Hex; +import org.aion.interfaces.db.ContractDetails; +import org.aion.interfaces.db.RepositoryCache; import org.aion.mcf.core.AccountState; import org.aion.mcf.db.ContractDetailsCacheImpl; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; +import org.aion.util.conversions.Hex; import org.aion.zero.db.AionRepositoryCache; import org.aion.zero.types.IAionBlock; import org.slf4j.Logger; @@ -24,9 +23,9 @@ public class AionRepositoryDummy extends AionRepositoryImpl { private static final Logger logger = LoggerFactory.getLogger("repository"); private Map<ByteArrayWrapper, AccountState> worldState = new HashMap<>(); - private Map<ByteArrayWrapper, IContractDetails> detailsDB = new HashMap<>(); + private Map<ByteArrayWrapper, ContractDetails> detailsDB = new HashMap<>(); - public AionRepositoryDummy(IRepositoryConfig cfg) { + public AionRepositoryDummy(RepositoryConfigImpl cfg) { super(cfg); } @@ -47,12 +46,12 @@ public boolean isClosed() { public void updateBatch( HashMap<ByteArrayWrapper, AccountState> stateCache, - HashMap<ByteArrayWrapper, IContractDetails> detailsCache) { + HashMap<ByteArrayWrapper, ContractDetails> detailsCache) { for (ByteArrayWrapper hash : stateCache.keySet()) { AccountState accountState = stateCache.get(hash); - IContractDetails contractDetails = detailsCache.get(hash); + ContractDetails contractDetails = detailsCache.get(hash); if (accountState.isDeleted()) { worldState.remove(hash); @@ -99,7 +98,7 @@ public void syncToRoot(byte[] root) { throw new UnsupportedOperationException(); } - public IRepositoryCache<?, ?> startTracking() { + public RepositoryCache<?, ?> startTracking() { return new AionRepositoryCache(this); } @@ -137,7 +136,7 @@ public BigInteger getBalance(Address addr) { } public ByteArrayWrapper getStorageValue(Address addr, ByteArrayWrapper key) { - IContractDetails details = getContractDetails(addr); + ContractDetails details = getContractDetails(addr); ByteArrayWrapper value = (details == null) ? null : details.get(key); if (value != null && value.isZero()) { @@ -156,7 +155,7 @@ public ByteArrayWrapper getStorageValue(Address addr, ByteArrayWrapper key) { // never used // public void addStorageRow(Address addr, ByteArrayWrapper key, ByteArrayWrapper value) { - // IContractDetails details = getContractDetails(addr); + // ContractDetails details = getContractDetails(addr); // // if (details == null) { // createAccount(addr); @@ -167,7 +166,7 @@ public ByteArrayWrapper getStorageValue(Address addr, ByteArrayWrapper key) { // } public byte[] getCode(Address addr) { - IContractDetails details = getContractDetails(addr); + ContractDetails details = getContractDetails(addr); if (details == null) { return null; @@ -177,7 +176,7 @@ public byte[] getCode(Address addr) { } public void saveCode(Address addr, byte[] code) { - IContractDetails details = getContractDetails(addr); + ContractDetails details = getContractDetails(addr); if (details == null) { createAccount(addr); @@ -230,7 +229,7 @@ public void delete(Address addr) { detailsDB.remove(ByteArrayWrapper.wrap(addr.toBytes())); } - public IContractDetails getContractDetails(Address addr) { + public ContractDetails getContractDetails(Address addr) { return detailsDB.get(ByteArrayWrapper.wrap(addr.toBytes())); } @@ -243,7 +242,7 @@ public AccountState createAccount(Address addr) { AccountState accountState = new AccountState(); worldState.put(ByteArrayWrapper.wrap(addr.toBytes()), accountState); - IContractDetails contractDetails = this.cfg.contractDetailsImpl(); + ContractDetails contractDetails = this.cfg.contractDetailsImpl(); detailsDB.put(ByteArrayWrapper.wrap(addr.toBytes()), contractDetails); return accountState; @@ -260,10 +259,10 @@ public byte[] getRoot() { public void loadAccount( Address addr, HashMap<ByteArrayWrapper, AccountState> cacheAccounts, - HashMap<ByteArrayWrapper, IContractDetails> cacheDetails) { + HashMap<ByteArrayWrapper, ContractDetails> cacheDetails) { AccountState account = getAccountState(addr); - IContractDetails details = getContractDetails(addr); + ContractDetails details = getContractDetails(addr); if (account == null) { account = new AccountState(); diff --git a/modAionImpl/src/org/aion/zero/impl/db/AionRepositoryImpl.java b/modAionImpl/src/org/aion/zero/impl/db/AionRepositoryImpl.java index dff34f2d74..d8118dd816 100644 --- a/modAionImpl/src/org/aion/zero/impl/db/AionRepositoryImpl.java +++ b/modAionImpl/src/org/aion/zero/impl/db/AionRepositoryImpl.java @@ -1,7 +1,7 @@ package org.aion.zero.impl.db; -import static org.aion.base.util.ByteUtil.EMPTY_BYTE_ARRAY; import static org.aion.crypto.HashUtil.EMPTY_TRIE_HASH; +import static org.aion.util.bytes.ByteUtil.EMPTY_BYTE_ARRAY; import static org.aion.zero.impl.AionHub.INIT_ERROR_EXIT_CODE; import java.math.BigInteger; @@ -14,13 +14,8 @@ import java.util.Map; import java.util.Optional; import java.util.Set; -import org.aion.base.db.IByteArrayKeyValueDatabase; -import org.aion.base.db.IContractDetails; -import org.aion.base.db.IRepository; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.db.IRepositoryConfig; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.Hex; + +import org.aion.interfaces.db.*; import org.aion.mcf.core.AccountState; import org.aion.mcf.db.AbstractRepository; import org.aion.mcf.db.ContractDetailsCacheImpl; @@ -30,7 +25,9 @@ import org.aion.mcf.trie.TrieImpl; import org.aion.mcf.trie.TrieNodeResult; import org.aion.p2p.V1Constants; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; +import org.aion.util.conversions.Hex; import org.aion.zero.db.AionRepositoryCache; import org.aion.zero.impl.config.CfgAion; import org.aion.zero.impl.sync.DatabaseType; @@ -57,29 +54,16 @@ public class AionRepositoryImpl */ protected AionRepositoryImpl() {} - protected AionRepositoryImpl(IRepositoryConfig repoConfig) { + protected AionRepositoryImpl(RepositoryConfig repoConfig) { this.cfg = repoConfig; init(); } - private static class AionRepositoryImplHolder { - // configuration - private static CfgAion config = CfgAion.inst(); - - // repository singleton instance - private static final AionRepositoryImpl inst = - new AionRepositoryImpl( - new RepositoryConfig( - config.getDatabasePath(), - ContractDetailsAion.getInstance(), - config.getDb())); - } - public static AionRepositoryImpl inst() { return AionRepositoryImplHolder.inst; } - public static AionRepositoryImpl createForTesting(IRepositoryConfig repoConfig) { + public static AionRepositoryImpl createForTesting(RepositoryConfig repoConfig) { return new AionRepositoryImpl(repoConfig); } @@ -122,14 +106,14 @@ private Trie createStateTrie() { @Override public void updateBatch( - Map<Address, AccountState> stateCache, Map<Address, IContractDetails> detailsCache) { + Map<Address, AccountState> stateCache, Map<Address, ContractDetails> detailsCache) { rwLock.writeLock().lock(); try { for (Map.Entry<Address, AccountState> entry : stateCache.entrySet()) { Address address = entry.getKey(); AccountState accountState = entry.getValue(); - IContractDetails contractDetails = detailsCache.get(address); + ContractDetails contractDetails = detailsCache.get(address); if (accountState.isDeleted()) { // TODO-A: batch operations here @@ -212,7 +196,7 @@ public void updateBatch( /** @implNote The method calling this method must handle the locking. */ private void updateContractDetails( - final Address address, final IContractDetails contractDetails) { + final Address address, final ContractDetails contractDetails) { // locked by calling method detailsDS.update(address, contractDetails); } @@ -242,7 +226,7 @@ public void flush() { } if (databaseGroup != null) { - for (IByteArrayKeyValueDatabase db : databaseGroup) { + for (ByteArrayKeyValueDatabase db : databaseGroup) { if (!db.isAutoCommitEnabled()) { db.commit(); } @@ -297,7 +281,7 @@ public void syncToRoot(final byte[] root) { } @Override - public IRepositoryCache startTracking() { + public RepositoryCache startTracking() { return new AionRepositoryCache(this); } @@ -318,7 +302,7 @@ public BigInteger getBalance(Address address) { @Override public ByteArrayWrapper getStorageValue(Address address, ByteArrayWrapper key) { - IContractDetails details = getContractDetails(address); + ContractDetails details = getContractDetails(address); return (details == null) ? null : details.get(key); } @@ -365,7 +349,7 @@ public List<byte[]> getCacheTx() { @Override public Map<ByteArrayWrapper, ByteArrayWrapper> getStorage( Address address, Collection<ByteArrayWrapper> keys) { - IContractDetails details = getContractDetails(address); + ContractDetails details = getContractDetails(address); return (details == null) ? Collections.emptyMap() : details.getStorage(keys); } @@ -379,7 +363,7 @@ public byte[] getCode(Address address) { byte[] codeHash = accountState.getCodeHash(); - IContractDetails details = getContractDetails(address); + ContractDetails details = getContractDetails(address); return (details == null) ? EMPTY_BYTE_ARRAY : details.getCode(codeHash); } @@ -398,16 +382,16 @@ private void updateAccountState(Address address, AccountState accountState) { /** * @inheritDoc * @implNote Any other method calling this can rely on the fact that the contract details - * returned is a newly created object by {@link IContractDetails#getSnapshotTo(byte[])}. + * returned is a newly created object by {@link ContractDetails#getSnapshotTo(byte[])}. * Since this querying method it locked, the methods calling it <b>may not need to be locked * or synchronized</b>, depending on the specific use case. */ @Override - public IContractDetails getContractDetails(Address address) { + public ContractDetails getContractDetails(Address address) { rwLock.readLock().lock(); try { - IContractDetails details; + ContractDetails details; // That part is important cause if we have // to sync details storage according the trie root @@ -482,10 +466,10 @@ public boolean hasAccountState(Address address) { public void loadAccountState( Address address, Map<Address, AccountState> cacheAccounts, - Map<Address, IContractDetails> cacheDetails) { + Map<Address, ContractDetails> cacheDetails) { AccountState account = getAccountState(address); - IContractDetails details = getContractDetails(address); + ContractDetails details = getContractDetails(address); account = (account == null) ? new AccountState() : new AccountState(account); details = new ContractDetailsCacheImpl(details); @@ -571,7 +555,7 @@ public Trie getWorldState() { } @Override - public IRepository getSnapshotTo(byte[] root) { + public Repository getSnapshotTo(byte[] root) { rwLock.readLock().lock(); try { @@ -739,11 +723,11 @@ public void close() { * * @return */ - public IByteArrayKeyValueDatabase getStateDatabase() { + public ByteArrayKeyValueDatabase getStateDatabase() { return this.stateDatabase; } - public IByteArrayKeyValueDatabase getStateArchiveDatabase() { + public ByteArrayKeyValueDatabase getStateArchiveDatabase() { return this.stateArchiveDatabase; } @@ -756,17 +740,17 @@ public IByteArrayKeyValueDatabase getStateArchiveDatabase() { * * @return */ - public IByteArrayKeyValueDatabase getDetailsDatabase() { + public ByteArrayKeyValueDatabase getDetailsDatabase() { return this.detailsDatabase; } /** For testing. */ - public IByteArrayKeyValueDatabase getBlockDatabase() { + public ByteArrayKeyValueDatabase getBlockDatabase() { return this.blockDatabase; } /** For testing. */ - public IByteArrayKeyValueDatabase getIndexDatabase() { + public ByteArrayKeyValueDatabase getIndexDatabase() { return this.indexDatabase; } @@ -782,13 +766,13 @@ public String toString() { } /** - * Calls {@link IByteArrayKeyValueDatabase#drop()} on all the current databases except for the + * Calls {@link ByteArrayKeyValueDatabase#drop()} on all the current databases except for the * ones given in the list by name. * * @param names the names of the databases that should not be dropped */ public void dropDatabasesExcept(List<String> names) { - for (IByteArrayKeyValueDatabase db : databaseGroup) { + for (ByteArrayKeyValueDatabase db : databaseGroup) { if (!names.contains(db.getName().get())) { LOG.warn("Dropping database " + db.toString() + " ..."); db.drop(); @@ -802,7 +786,7 @@ public void compact() { rwLock.writeLock().lock(); try { if (databaseGroup != null) { - for (IByteArrayKeyValueDatabase db : databaseGroup) { + for (ByteArrayKeyValueDatabase db : databaseGroup) { db.compact(); } } else { @@ -833,7 +817,7 @@ public void compactState() { * supported */ public byte[] getTrieNode(byte[] key, DatabaseType dbType) { - IByteArrayKeyValueDatabase db = selectDatabase(dbType); + ByteArrayKeyValueDatabase db = selectDatabase(dbType); Optional<byte[]> value = db.get(key); if (value.isPresent()) { @@ -861,7 +845,7 @@ public Map<ByteArrayWrapper, byte[]> getReferencedTrieNodes( if (limit <= 0) { return Collections.emptyMap(); } else { - IByteArrayKeyValueDatabase db = selectDatabase(dbType); + ByteArrayKeyValueDatabase db = selectDatabase(dbType); Trie trie = new TrieImpl(db); return trie.getReferencedTrieNodes(value, limit); @@ -889,7 +873,7 @@ public TrieNodeResult importTrieNode(byte[] key, byte[] value, DatabaseType dbTy return TrieNodeResult.INVALID_VALUE; } - IByteArrayKeyValueDatabase db = selectDatabase(dbType); + ByteArrayKeyValueDatabase db = selectDatabase(dbType); Optional<byte[]> stored = db.get(key); if (stored.isPresent()) { @@ -904,7 +888,7 @@ public TrieNodeResult importTrieNode(byte[] key, byte[] value, DatabaseType dbTy return TrieNodeResult.IMPORTED; } - private IByteArrayKeyValueDatabase selectDatabase(DatabaseType dbType) { + private ByteArrayKeyValueDatabase selectDatabase(DatabaseType dbType) { switch (dbType) { case DETAILS: return detailsDatabase; @@ -917,4 +901,17 @@ private IByteArrayKeyValueDatabase selectDatabase(DatabaseType dbType) { "The database type " + dbType.toString() + " is not supported."); } } + + private static class AionRepositoryImplHolder { + // configuration + private static CfgAion config = CfgAion.inst(); + + // repository singleton instance + private static final AionRepositoryImpl inst = + new AionRepositoryImpl( + new RepositoryConfigImpl( + config.getDatabasePath(), + (DetailsProvider) ContractDetailsAion.getInstance(), + config.getDb())); + } } diff --git a/modAionImpl/src/org/aion/zero/impl/db/AionTransactionStoreSerializer.java b/modAionImpl/src/org/aion/zero/impl/db/AionTransactionStoreSerializer.java index 36e6cff543..f1ea2b9e5f 100644 --- a/modAionImpl/src/org/aion/zero/impl/db/AionTransactionStoreSerializer.java +++ b/modAionImpl/src/org/aion/zero/impl/db/AionTransactionStoreSerializer.java @@ -10,7 +10,7 @@ public class AionTransactionStoreSerializer { public static final Serializer<List<AionTxInfo>, byte[]> serializer = - new Serializer<List<AionTxInfo>, byte[]>() { + new Serializer<>() { @Override public byte[] serialize(List<AionTxInfo> object) { byte[][] txsRlp = new byte[object.size()][]; diff --git a/modAionImpl/src/org/aion/zero/impl/db/ContractDetailsAion.java b/modAionImpl/src/org/aion/zero/impl/db/ContractDetailsAion.java index 54f1f53da7..ee76dc3224 100644 --- a/modAionImpl/src/org/aion/zero/impl/db/ContractDetailsAion.java +++ b/modAionImpl/src/org/aion/zero/impl/db/ContractDetailsAion.java @@ -1,7 +1,7 @@ package org.aion.zero.impl.db; -import org.aion.base.db.DetailsProvider; -import org.aion.base.db.IContractDetails; +import org.aion.interfaces.db.ContractDetails; +import org.aion.interfaces.db.DetailsProvider; import org.aion.zero.db.AionContractDetailsImpl; /** @@ -54,7 +54,7 @@ public static ContractDetailsAion getInstance() { } @Override - public IContractDetails getDetails() { + public ContractDetails getDetails() { return new AionContractDetailsImpl(this.prune, this.memStorageLimit); } } diff --git a/modAionImpl/src/org/aion/zero/impl/db/PendingBlockStore.java b/modAionImpl/src/org/aion/zero/impl/db/PendingBlockStore.java index 90a81671e1..33e937aeee 100644 --- a/modAionImpl/src/org/aion/zero/impl/db/PendingBlockStore.java +++ b/modAionImpl/src/org/aion/zero/impl/db/PendingBlockStore.java @@ -22,11 +22,9 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import org.aion.base.db.Flushable; -import org.aion.base.db.IByteArrayKeyValueDatabase; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.Hex; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; +import org.aion.interfaces.db.Flushable; +import org.aion.types.ByteArrayWrapper; import org.aion.db.impl.DBVendor; import org.aion.db.impl.DatabaseFactory.Props; import org.aion.log.AionLoggerFactory; @@ -37,6 +35,8 @@ import org.aion.rlp.RLP; import org.aion.rlp.RLPElement; import org.aion.rlp.RLPList; +import org.aion.util.bytes.ByteUtil; +import org.aion.util.conversions.Hex; import org.aion.zero.impl.types.AionBlock; import org.slf4j.Logger; @@ -79,13 +79,13 @@ public class PendingBlockStore implements Flushable, Closeable { */ private ObjectDataSource<List<byte[]>> levelSource; - private IByteArrayKeyValueDatabase levelDatabase; + private ByteArrayKeyValueDatabase levelDatabase; /** Used to map a queue identifier to a list of consecutive blocks. */ private ObjectDataSource<List<AionBlock>> queueSource; - private IByteArrayKeyValueDatabase queueDatabase; + private ByteArrayKeyValueDatabase queueDatabase; /** Used to maps a block hash to its current queue identifier. */ - private IByteArrayKeyValueDatabase indexSource; + private ByteArrayKeyValueDatabase indexSource; // tracking the status: with access managed by the `internalLock` private Map<ByteArrayWrapper, QueueInfo> status; @@ -531,7 +531,7 @@ int getQueueSize() { } } - private static int countDatabaseKeys(IByteArrayKeyValueDatabase db) { + private static int countDatabaseKeys(ByteArrayKeyValueDatabase db) { int size = 0; Iterator<byte[]> iterator = db.keys(); while (iterator.hasNext()) { diff --git a/modAionImpl/src/org/aion/zero/impl/db/RecoveryUtils.java b/modAionImpl/src/org/aion/zero/impl/db/RecoveryUtils.java index dd24833b8b..30338cc7b9 100644 --- a/modAionImpl/src/org/aion/zero/impl/db/RecoveryUtils.java +++ b/modAionImpl/src/org/aion/zero/impl/db/RecoveryUtils.java @@ -4,7 +4,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.aion.base.type.IBlock; +import org.aion.interfaces.block.Block; import org.aion.log.AionLoggerFactory; import org.aion.mcf.config.CfgDb; import org.aion.mcf.core.ImportResult; @@ -78,7 +78,7 @@ public static void pruneAndCorrect() { IBlockStoreBase store = blockchain.getBlockStore(); - IBlock bestBlock = store.getBestBlock(); + Block bestBlock = store.getBestBlock(); if (bestBlock == null) { System.out.println("Empty database. Nothing to do."); return; @@ -151,7 +151,7 @@ public static void dumpBlocks(long count) { public static Status revertTo(IAionBlockchain blockchain, long nbBlock) { IBlockStoreBase store = blockchain.getBlockStore(); - IBlock bestBlock = store.getBestBlock(); + Block bestBlock = store.getBestBlock(); if (bestBlock == null) { System.out.println("Empty database. Nothing to do."); return Status.ILLEGAL_ARGUMENT; diff --git a/modAionImpl/src/org/aion/zero/impl/db/RepositoryConfig.java b/modAionImpl/src/org/aion/zero/impl/db/RepositoryConfigImpl.java similarity index 65% rename from modAionImpl/src/org/aion/zero/impl/db/RepositoryConfig.java rename to modAionImpl/src/org/aion/zero/impl/db/RepositoryConfigImpl.java index a5190df066..c5bc52d8df 100644 --- a/modAionImpl/src/org/aion/zero/impl/db/RepositoryConfig.java +++ b/modAionImpl/src/org/aion/zero/impl/db/RepositoryConfigImpl.java @@ -2,16 +2,16 @@ import java.util.Map; import java.util.Properties; -import org.aion.base.db.DetailsProvider; -import org.aion.base.db.IContractDetails; -import org.aion.base.db.IPruneConfig; -import org.aion.base.db.IRepositoryConfig; +import org.aion.interfaces.db.ContractDetails; +import org.aion.interfaces.db.DetailsProvider; +import org.aion.interfaces.db.PruneConfig; +import org.aion.interfaces.db.RepositoryConfig; import org.aion.mcf.config.CfgDb; -public class RepositoryConfig implements IRepositoryConfig { +public class RepositoryConfigImpl implements RepositoryConfig { private final String dbPath; - private final IPruneConfig cfgPrune; + private final PruneConfig cfgPrune; private final DetailsProvider detailsProvider; private final Map<String, Properties> cfg; @@ -21,12 +21,12 @@ public String getDbPath() { } @Override - public IPruneConfig getPruneConfig() { + public PruneConfig getPruneConfig() { return cfgPrune; } @Override - public IContractDetails contractDetailsImpl() { + public ContractDetails contractDetailsImpl() { return this.detailsProvider.getDetails(); } @@ -39,11 +39,11 @@ public Properties getDatabaseConfig(String db_name) { return new Properties(prop); } - public RepositoryConfig( + public RepositoryConfigImpl( final String dbPath, final DetailsProvider detailsProvider, final CfgDb cfgDb) { this.dbPath = dbPath; this.detailsProvider = detailsProvider; this.cfg = cfgDb.asProperties(); - this.cfgPrune = cfgDb.getPrune(); + this.cfgPrune = (PruneConfig) cfgDb.getPrune(); } } diff --git a/modAionImpl/src/org/aion/zero/impl/pow/AionPoW.java b/modAionImpl/src/org/aion/zero/impl/pow/AionPoW.java index af90496b18..abb34894e0 100644 --- a/modAionImpl/src/org/aion/zero/impl/pow/AionPoW.java +++ b/modAionImpl/src/org/aion/zero/impl/pow/AionPoW.java @@ -10,8 +10,7 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; -import org.aion.base.util.Hex; -import org.aion.equihash.Solution; +import org.aion.equihash.SolutionImpl; import org.aion.evtmgr.IEvent; import org.aion.evtmgr.IEventMgr; import org.aion.evtmgr.IHandler; @@ -24,6 +23,7 @@ import org.aion.log.LogEnum; import org.aion.mcf.blockchain.IPendingState; import org.aion.mcf.core.ImportResult; +import org.aion.util.conversions.Hex; import org.aion.zero.impl.blockchain.AionImpl; import org.aion.zero.impl.config.CfgAion; import org.aion.zero.impl.core.IAionBlockchain; @@ -72,7 +72,7 @@ public void run() { createNewBlockTemplate(); } else if (e.getEventType() == IHandler.TYPE.CONSENSUS.getValue() && e.getCallbackType() == EventConsensus.CALLBACK.ON_SOLUTION.getValue()) { - processSolution((Solution) e.getFuncArgs().get(0)); + processSolution((SolutionImpl) e.getFuncArgs().get(0)); } else if (e.getEventType() == IHandler.TYPE.POISONPILL.getValue()) { go = false; } @@ -191,7 +191,7 @@ public void registerCallback() { * * @param solution The generated equihash solution */ - protected synchronized void processSolution(Solution solution) { + protected synchronized void processSolution(SolutionImpl solution) { if (!shutDown.get()) { if (LOG.isDebugEnabled()) { LOG.debug("Best block num [{}]", blockchain.getBestBlock().getNumber()); diff --git a/modAionImpl/src/org/aion/zero/impl/query/StateQueryInterface.java b/modAionImpl/src/org/aion/zero/impl/query/StateQueryInterface.java index a201f6bcb6..7e472a2ab4 100644 --- a/modAionImpl/src/org/aion/zero/impl/query/StateQueryInterface.java +++ b/modAionImpl/src/org/aion/zero/impl/query/StateQueryInterface.java @@ -1,9 +1,9 @@ package org.aion.zero.impl.query; import java.util.Optional; -import org.aion.base.util.ByteArrayWrapper; import org.aion.mcf.core.AccountState; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; public interface StateQueryInterface { Optional<AccountState> getAccountState(Address address, long blockNumber); diff --git a/modAionImpl/src/org/aion/zero/impl/sync/FastSyncManager.java b/modAionImpl/src/org/aion/zero/impl/sync/FastSyncManager.java index 7e0fa4b712..c82381d66f 100644 --- a/modAionImpl/src/org/aion/zero/impl/sync/FastSyncManager.java +++ b/modAionImpl/src/org/aion/zero/impl/sync/FastSyncManager.java @@ -5,7 +5,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; /** * Directs behavior for fast sync functionality. diff --git a/modAionImpl/src/org/aion/zero/impl/sync/SyncMgr.java b/modAionImpl/src/org/aion/zero/impl/sync/SyncMgr.java index 8e0f6e398e..0eaac62834 100644 --- a/modAionImpl/src/org/aion/zero/impl/sync/SyncMgr.java +++ b/modAionImpl/src/org/aion/zero/impl/sync/SyncMgr.java @@ -1,5 +1,7 @@ package org.aion.zero.impl.sync; +import static org.aion.util.string.StringUtils.getNodeIdShort; + import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; @@ -17,10 +19,6 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.Hex; -import org.aion.base.util.Utils; import org.aion.evtmgr.IEvent; import org.aion.evtmgr.IEventMgr; import org.aion.evtmgr.impl.evt.EventConsensus; @@ -29,6 +27,9 @@ import org.aion.mcf.config.StatsType; import org.aion.mcf.valid.BlockHeaderValidator; import org.aion.p2p.IP2pMgr; +import org.aion.types.ByteArrayWrapper; +import org.aion.util.bytes.ByteUtil; +import org.aion.util.conversions.Hex; import org.aion.zero.impl.AionBlockchainImpl; import org.aion.zero.impl.blockchain.ChainConfiguration; import org.aion.zero.impl.types.AionBlock; @@ -133,9 +134,9 @@ public void updateNetworkStatus( _remoteBestBlockNumber, this.networkStatus.getTargetBestBlockHash().isEmpty() ? "" - : Utils.getNodeIdShort( + : getNodeIdShort( this.networkStatus.getTargetBestBlockHash()), - Utils.getNodeIdShort(remoteBestBlockHash), + getNodeIdShort(remoteBestBlockHash), this.networkStatus.getTargetApiVersion(), (int) _apiVersion, this.networkStatus.getTargetPeerCount(), diff --git a/modAionImpl/src/org/aion/zero/impl/sync/TaskDropImportedBlocks.java b/modAionImpl/src/org/aion/zero/impl/sync/TaskDropImportedBlocks.java index daf9780b22..1b233e2975 100644 --- a/modAionImpl/src/org/aion/zero/impl/sync/TaskDropImportedBlocks.java +++ b/modAionImpl/src/org/aion/zero/impl/sync/TaskDropImportedBlocks.java @@ -3,7 +3,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; import org.aion.zero.impl.AionBlockchainImpl; import org.aion.zero.impl.types.AionBlock; import org.slf4j.Logger; diff --git a/modAionImpl/src/org/aion/zero/impl/sync/TaskImportBlocks.java b/modAionImpl/src/org/aion/zero/impl/sync/TaskImportBlocks.java index 72a7e4af22..31405bb5d7 100644 --- a/modAionImpl/src/org/aion/zero/impl/sync/TaskImportBlocks.java +++ b/modAionImpl/src/org/aion/zero/impl/sync/TaskImportBlocks.java @@ -23,7 +23,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; import org.aion.mcf.core.ImportResult; import org.aion.p2p.P2pConstant; import org.aion.zero.impl.AionBlockchainImpl; diff --git a/modAionImpl/src/org/aion/zero/impl/sync/TaskImportTrieData.java b/modAionImpl/src/org/aion/zero/impl/sync/TaskImportTrieData.java index e69530355b..f38e8685f9 100644 --- a/modAionImpl/src/org/aion/zero/impl/sync/TaskImportTrieData.java +++ b/modAionImpl/src/org/aion/zero/impl/sync/TaskImportTrieData.java @@ -4,9 +4,9 @@ import java.util.Map.Entry; import java.util.concurrent.BlockingQueue; import java.util.stream.Collectors; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.Hex; +import org.aion.types.ByteArrayWrapper; import org.aion.mcf.trie.TrieNodeResult; +import org.aion.util.conversions.Hex; import org.aion.zero.impl.AionBlockchainImpl; import org.slf4j.Logger; diff --git a/modAionImpl/src/org/aion/zero/impl/sync/TaskShowStatus.java b/modAionImpl/src/org/aion/zero/impl/sync/TaskShowStatus.java index e2a2cf186f..acbe8df3d3 100644 --- a/modAionImpl/src/org/aion/zero/impl/sync/TaskShowStatus.java +++ b/modAionImpl/src/org/aion/zero/impl/sync/TaskShowStatus.java @@ -8,10 +8,10 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; -import org.aion.base.util.Hex; import org.aion.mcf.config.StatsType; import org.aion.p2p.INode; import org.aion.p2p.IP2pMgr; +import org.aion.util.conversions.Hex; import org.aion.zero.impl.AionBlockchainImpl; import org.aion.zero.impl.types.AionBlock; import org.slf4j.Logger; diff --git a/modAionImpl/src/org/aion/zero/impl/sync/TrieNodeWrapper.java b/modAionImpl/src/org/aion/zero/impl/sync/TrieNodeWrapper.java index f329334332..a342e74865 100644 --- a/modAionImpl/src/org/aion/zero/impl/sync/TrieNodeWrapper.java +++ b/modAionImpl/src/org/aion/zero/impl/sync/TrieNodeWrapper.java @@ -2,7 +2,7 @@ import java.util.Map; import java.util.Objects; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; import org.aion.zero.impl.sync.msg.ResponseTrieData; /** diff --git a/modAionImpl/src/org/aion/zero/impl/sync/handler/BlockPropagationHandler.java b/modAionImpl/src/org/aion/zero/impl/sync/handler/BlockPropagationHandler.java index 60d68763d1..3f23e5ff18 100644 --- a/modAionImpl/src/org/aion/zero/impl/sync/handler/BlockPropagationHandler.java +++ b/modAionImpl/src/org/aion/zero/impl/sync/handler/BlockPropagationHandler.java @@ -3,7 +3,7 @@ import java.math.BigInteger; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.aion.mcf.blockchain.IPendingStateInternal; diff --git a/modAionImpl/src/org/aion/zero/impl/sync/handler/BroadcastNewBlockHandler.java b/modAionImpl/src/org/aion/zero/impl/sync/handler/BroadcastNewBlockHandler.java index 58a3fbb780..af7fbcc754 100644 --- a/modAionImpl/src/org/aion/zero/impl/sync/handler/BroadcastNewBlockHandler.java +++ b/modAionImpl/src/org/aion/zero/impl/sync/handler/BroadcastNewBlockHandler.java @@ -1,10 +1,10 @@ package org.aion.zero.impl.sync.handler; -import org.aion.base.util.ByteUtil; import org.aion.p2p.Ctrl; import org.aion.p2p.Handler; import org.aion.p2p.IP2pMgr; import org.aion.p2p.Ver; +import org.aion.util.bytes.ByteUtil; import org.aion.zero.impl.sync.Act; import org.aion.zero.impl.sync.msg.BroadcastNewBlock; import org.aion.zero.impl.types.AionBlock; diff --git a/modAionImpl/src/org/aion/zero/impl/sync/handler/BroadcastTxHandler.java b/modAionImpl/src/org/aion/zero/impl/sync/handler/BroadcastTxHandler.java index f2ca37158d..1baa948bd7 100644 --- a/modAionImpl/src/org/aion/zero/impl/sync/handler/BroadcastTxHandler.java +++ b/modAionImpl/src/org/aion/zero/impl/sync/handler/BroadcastTxHandler.java @@ -6,13 +6,13 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; +import org.aion.types.ByteArrayWrapper; import org.aion.mcf.blockchain.IPendingStateInternal; import org.aion.p2p.Ctrl; import org.aion.p2p.Handler; import org.aion.p2p.IP2pMgr; import org.aion.p2p.Ver; +import org.aion.util.bytes.ByteUtil; import org.aion.zero.impl.sync.Act; import org.aion.zero.impl.sync.msg.BroadcastTx; import org.aion.zero.impl.valid.TXValidator; diff --git a/modAionImpl/src/org/aion/zero/impl/sync/handler/ReqBlocksBodiesHandler.java b/modAionImpl/src/org/aion/zero/impl/sync/handler/ReqBlocksBodiesHandler.java index 14e0aeb775..4b5f4e60e4 100644 --- a/modAionImpl/src/org/aion/zero/impl/sync/handler/ReqBlocksBodiesHandler.java +++ b/modAionImpl/src/org/aion/zero/impl/sync/handler/ReqBlocksBodiesHandler.java @@ -4,13 +4,13 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; +import org.aion.types.ByteArrayWrapper; import org.aion.p2p.Ctrl; import org.aion.p2p.Handler; import org.aion.p2p.IP2pMgr; import org.aion.p2p.P2pConstant; import org.aion.p2p.Ver; +import org.aion.util.bytes.ByteUtil; import org.aion.zero.impl.core.IAionBlockchain; import org.aion.zero.impl.sync.Act; import org.aion.zero.impl.sync.SyncMgr; diff --git a/modAionImpl/src/org/aion/zero/impl/sync/handler/RequestTrieDataHandler.java b/modAionImpl/src/org/aion/zero/impl/sync/handler/RequestTrieDataHandler.java index ccbc2b3567..229af7c79e 100644 --- a/modAionImpl/src/org/aion/zero/impl/sync/handler/RequestTrieDataHandler.java +++ b/modAionImpl/src/org/aion/zero/impl/sync/handler/RequestTrieDataHandler.java @@ -5,7 +5,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.Map; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; import org.aion.p2p.Ctrl; import org.aion.p2p.Handler; import org.aion.p2p.IP2pMgr; diff --git a/modAionImpl/src/org/aion/zero/impl/sync/handler/ResBlocksBodiesHandler.java b/modAionImpl/src/org/aion/zero/impl/sync/handler/ResBlocksBodiesHandler.java index 028a3bf451..bd7a551de1 100644 --- a/modAionImpl/src/org/aion/zero/impl/sync/handler/ResBlocksBodiesHandler.java +++ b/modAionImpl/src/org/aion/zero/impl/sync/handler/ResBlocksBodiesHandler.java @@ -1,11 +1,11 @@ package org.aion.zero.impl.sync.handler; import java.util.List; -import org.aion.base.util.ByteUtil; import org.aion.p2p.Ctrl; import org.aion.p2p.Handler; import org.aion.p2p.IP2pMgr; import org.aion.p2p.Ver; +import org.aion.util.bytes.ByteUtil; import org.aion.zero.impl.sync.Act; import org.aion.zero.impl.sync.SyncMgr; import org.aion.zero.impl.sync.msg.ResBlocksBodies; diff --git a/modAionImpl/src/org/aion/zero/impl/sync/handler/ResBlocksHeadersHandler.java b/modAionImpl/src/org/aion/zero/impl/sync/handler/ResBlocksHeadersHandler.java index 8301153cb5..e840ceaacf 100644 --- a/modAionImpl/src/org/aion/zero/impl/sync/handler/ResBlocksHeadersHandler.java +++ b/modAionImpl/src/org/aion/zero/impl/sync/handler/ResBlocksHeadersHandler.java @@ -1,11 +1,11 @@ package org.aion.zero.impl.sync.handler; import java.util.List; -import org.aion.base.util.ByteUtil; import org.aion.p2p.Ctrl; import org.aion.p2p.Handler; import org.aion.p2p.IP2pMgr; import org.aion.p2p.Ver; +import org.aion.util.bytes.ByteUtil; import org.aion.zero.impl.sync.Act; import org.aion.zero.impl.sync.SyncMgr; import org.aion.zero.impl.sync.msg.ResBlocksHeaders; diff --git a/modAionImpl/src/org/aion/zero/impl/sync/handler/ResStatusHandler.java b/modAionImpl/src/org/aion/zero/impl/sync/handler/ResStatusHandler.java index 1f32edf4c1..17aa9f717a 100644 --- a/modAionImpl/src/org/aion/zero/impl/sync/handler/ResStatusHandler.java +++ b/modAionImpl/src/org/aion/zero/impl/sync/handler/ResStatusHandler.java @@ -1,12 +1,12 @@ package org.aion.zero.impl.sync.handler; import java.math.BigInteger; -import org.aion.base.util.ByteUtil; import org.aion.p2p.Ctrl; import org.aion.p2p.Handler; import org.aion.p2p.INode; import org.aion.p2p.IP2pMgr; import org.aion.p2p.Ver; +import org.aion.util.bytes.ByteUtil; import org.aion.zero.impl.sync.Act; import org.aion.zero.impl.sync.SyncMgr; import org.aion.zero.impl.sync.msg.ResStatus; diff --git a/modAionImpl/src/org/aion/zero/impl/sync/msg/BroadcastNewBlock.java b/modAionImpl/src/org/aion/zero/impl/sync/msg/BroadcastNewBlock.java index ca97001026..ceed7962f1 100644 --- a/modAionImpl/src/org/aion/zero/impl/sync/msg/BroadcastNewBlock.java +++ b/modAionImpl/src/org/aion/zero/impl/sync/msg/BroadcastNewBlock.java @@ -1,6 +1,6 @@ package org.aion.zero.impl.sync.msg; -import org.aion.base.type.IBlock; +import org.aion.interfaces.block.Block; import org.aion.p2p.Ctrl; import org.aion.p2p.Msg; import org.aion.p2p.Ver; @@ -11,9 +11,9 @@ public final class BroadcastNewBlock extends Msg { @SuppressWarnings("rawtypes") - private final IBlock block; + private final Block block; - public BroadcastNewBlock(@SuppressWarnings("rawtypes") final IBlock __newblock) { + public BroadcastNewBlock(@SuppressWarnings("rawtypes") final Block __newblock) { super(Ver.V0, Ctrl.SYNC, Act.BROADCAST_BLOCK); this.block = __newblock; } diff --git a/modAionImpl/src/org/aion/zero/impl/sync/msg/BroadcastTx.java b/modAionImpl/src/org/aion/zero/impl/sync/msg/BroadcastTx.java index 48a04f68e8..d6b281db68 100644 --- a/modAionImpl/src/org/aion/zero/impl/sync/msg/BroadcastTx.java +++ b/modAionImpl/src/org/aion/zero/impl/sync/msg/BroadcastTx.java @@ -2,7 +2,7 @@ import java.util.ArrayList; import java.util.List; -import org.aion.base.type.ITransaction; +import org.aion.interfaces.tx.Transaction; import org.aion.p2p.Ctrl; import org.aion.p2p.Msg; import org.aion.p2p.Ver; @@ -14,9 +14,9 @@ /** @author chris */ public final class BroadcastTx extends Msg { - private final List<? extends ITransaction> txl; + private final List<? extends Transaction> txl; - public BroadcastTx(final List<? extends ITransaction> _txl) { + public BroadcastTx(final List<? extends Transaction> _txl) { super(Ver.V0, Ctrl.SYNC, Act.BROADCAST_TX); this.txl = _txl; } @@ -27,7 +27,7 @@ public BroadcastTx(final List<? extends ITransaction> _txl) { @Override public byte[] encode() { List<byte[]> encodedTx = new ArrayList<>(); - for (ITransaction tx : txl) { + for (Transaction tx : txl) { encodedTx.add(tx.getEncoded()); } diff --git a/modAionImpl/src/org/aion/zero/impl/sync/msg/ResponseTrieData.java b/modAionImpl/src/org/aion/zero/impl/sync/msg/ResponseTrieData.java index 9009de4007..3b6872467a 100644 --- a/modAionImpl/src/org/aion/zero/impl/sync/msg/ResponseTrieData.java +++ b/modAionImpl/src/org/aion/zero/impl/sync/msg/ResponseTrieData.java @@ -9,7 +9,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Objects; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; import org.aion.p2p.Ctrl; import org.aion.p2p.Msg; import org.aion.p2p.Ver; diff --git a/modAionImpl/src/org/aion/zero/impl/types/AionBlock.java b/modAionImpl/src/org/aion/zero/impl/types/AionBlock.java index 4b001aac68..a403027a28 100644 --- a/modAionImpl/src/org/aion/zero/impl/types/AionBlock.java +++ b/modAionImpl/src/org/aion/zero/impl/types/AionBlock.java @@ -4,8 +4,6 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.Hex; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.aion.mcf.trie.Trie; @@ -14,7 +12,9 @@ import org.aion.rlp.RLP; import org.aion.rlp.RLPElement; import org.aion.rlp.RLPList; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Address; +import org.aion.util.bytes.ByteUtil; +import org.aion.util.conversions.Hex; import org.aion.zero.exceptions.HeaderStructureException; import org.aion.zero.types.A0BlockHeader; import org.aion.zero.types.AionTransaction; diff --git a/modAionImpl/src/org/aion/zero/impl/types/AionBlockSummary.java b/modAionImpl/src/org/aion/zero/impl/types/AionBlockSummary.java index 81af2b34bd..32f7355684 100644 --- a/modAionImpl/src/org/aion/zero/impl/types/AionBlockSummary.java +++ b/modAionImpl/src/org/aion/zero/impl/types/AionBlockSummary.java @@ -3,9 +3,9 @@ import java.math.BigInteger; import java.util.List; import java.util.Map; -import org.aion.base.type.IBlockSummary; +import org.aion.interfaces.block.BlockSummary; import org.aion.mcf.types.AbstractBlockSummary; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Address; import org.aion.zero.types.AionTransaction; import org.aion.zero.types.AionTxExecSummary; import org.aion.zero.types.AionTxReceipt; @@ -18,7 +18,7 @@ */ public class AionBlockSummary extends AbstractBlockSummary<IAionBlock, AionTransaction, AionTxReceipt, AionTxExecSummary> - implements IBlockSummary { + implements BlockSummary { public AionBlockSummary( IAionBlock block, diff --git a/modAionImpl/src/org/aion/zero/impl/types/RetValidPreBlock.java b/modAionImpl/src/org/aion/zero/impl/types/RetValidPreBlock.java index 0e238c33ef..14941dffe0 100644 --- a/modAionImpl/src/org/aion/zero/impl/types/RetValidPreBlock.java +++ b/modAionImpl/src/org/aion/zero/impl/types/RetValidPreBlock.java @@ -3,7 +3,8 @@ import java.math.BigInteger; import java.util.List; import java.util.Map; -import org.aion.vm.api.interfaces.Address; + +import org.aion.types.Address; import org.aion.zero.types.AionTransaction; import org.aion.zero.types.AionTxExecSummary; import org.aion.zero.types.AionTxReceipt; diff --git a/modAionImpl/src/org/aion/zero/impl/valid/AionDifficultyRule.java b/modAionImpl/src/org/aion/zero/impl/valid/AionDifficultyRule.java index ea279dbc3e..23c06907e4 100644 --- a/modAionImpl/src/org/aion/zero/impl/valid/AionDifficultyRule.java +++ b/modAionImpl/src/org/aion/zero/impl/valid/AionDifficultyRule.java @@ -1,22 +1,22 @@ package org.aion.zero.impl.valid; -import static org.aion.base.util.BIUtil.isEqual; +import static org.aion.util.biginteger.BIUtil.isEqual; import java.math.BigInteger; import java.util.List; import org.aion.mcf.blockchain.IChainCfg; import org.aion.mcf.core.IDifficultyCalculator; import org.aion.mcf.valid.GrandParentDependantBlockHeaderRule; +import org.aion.zero.impl.types.AionBlock; import org.aion.zero.types.A0BlockHeader; import org.aion.zero.types.AionTransaction; -import org.aion.zero.types.IAionBlock; /** Checks block's difficulty against calculated difficulty value */ public class AionDifficultyRule extends GrandParentDependantBlockHeaderRule<A0BlockHeader> { private IDifficultyCalculator diffCalc; - public AionDifficultyRule(IChainCfg<IAionBlock, AionTransaction> configuration) { + public AionDifficultyRule(IChainCfg<AionBlock, AionTransaction> configuration) { this.diffCalc = configuration.getDifficultyCalculator(); } diff --git a/modAionImpl/src/org/aion/zero/impl/valid/TXValidator.java b/modAionImpl/src/org/aion/zero/impl/valid/TXValidator.java index e67d438580..94e4314d6d 100644 --- a/modAionImpl/src/org/aion/zero/impl/valid/TXValidator.java +++ b/modAionImpl/src/org/aion/zero/impl/valid/TXValidator.java @@ -5,12 +5,12 @@ import java.util.Collections; import java.util.Map; -import org.aion.base.type.Hash256; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; import org.aion.crypto.ISignature; import org.aion.crypto.SignatureFac; import org.aion.log.LogEnum; -import org.aion.mcf.vm.types.DataWord; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Hash256; import org.aion.zero.types.AionTransaction; import org.apache.commons.collections4.map.LRUMap; import org.slf4j.Logger; @@ -40,7 +40,7 @@ public static boolean isInCache(ByteArrayWrapper hash) { public static boolean isValid0(AionTransaction tx) { byte[] check = tx.getNonce(); - if (check == null || check.length > DataWord.BYTES) { + if (check == null || check.length > DataWordImpl.BYTES) { LOG.error("invalid tx nonce!"); return false; } @@ -52,7 +52,7 @@ public static boolean isValid0(AionTransaction tx) { } check = tx.getValue(); - if (check == null || check.length > DataWord.BYTES) { + if (check == null || check.length > DataWordImpl.BYTES) { LOG.error("invalid tx value!"); return false; } diff --git a/modAionImpl/test/org/aion/db/AionContractDetailsTest.java b/modAionImpl/test/org/aion/db/AionContractDetailsTest.java index 2a7fb9957d..2843e13440 100644 --- a/modAionImpl/test/org/aion/db/AionContractDetailsTest.java +++ b/modAionImpl/test/org/aion/db/AionContractDetailsTest.java @@ -7,18 +7,18 @@ import java.util.HashMap; import java.util.Map; import java.util.Properties; -import org.aion.base.db.IByteArrayKeyValueDatabase; -import org.aion.base.db.IContractDetails; -import org.aion.base.db.IPruneConfig; -import org.aion.base.db.IRepositoryConfig; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; +import org.aion.interfaces.db.ContractDetails; +import org.aion.interfaces.db.PruneConfig; +import org.aion.interfaces.db.RepositoryConfig; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.db.impl.DBVendor; import org.aion.db.impl.DatabaseFactory; import org.aion.mcf.config.CfgPrune; -import org.aion.mcf.vm.types.DataWord; -import org.aion.vm.api.interfaces.Address; + +import org.aion.util.bytes.ByteUtil; import org.aion.zero.db.AionContractDetailsImpl; import org.aion.zero.impl.db.AionRepositoryImpl; import org.aion.zero.impl.db.ContractDetailsAion; @@ -30,20 +30,20 @@ public class AionContractDetailsTest { private static final int IN_MEMORY_STORAGE_LIMIT = 1000000; // CfgAion.inst().getDb().getDetailsInMemoryStorageLimit(); - protected IRepositoryConfig repoConfig = - new IRepositoryConfig() { + protected RepositoryConfig repoConfig = + new RepositoryConfig() { @Override public String getDbPath() { return ""; } @Override - public IPruneConfig getPruneConfig() { + public PruneConfig getPruneConfig() { return new CfgPrune(false); } @Override - public IContractDetails contractDetailsImpl() { + public ContractDetails contractDetailsImpl() { return ContractDetailsAion.createForTesting(0, 1000000).getDetails(); } @@ -56,8 +56,8 @@ public Properties getDatabaseConfig(String db_name) { } }; - private static IContractDetails deserialize( - byte[] rlp, IByteArrayKeyValueDatabase externalStorage) { + private static ContractDetails deserialize( + byte[] rlp, ByteArrayKeyValueDatabase externalStorage) { AionContractDetailsImpl result = new AionContractDetailsImpl(); result.setExternalStorageDataSource(externalStorage); result.decode(rlp); @@ -82,8 +82,8 @@ public void test_1() throws Exception { 1000000 // CfgAion.inst().getDb().getDetailsInMemoryStorageLimit() ); contractDetails.setCode(code); - contractDetails.put(new DataWord(key_1).toWrapper(), new DataWord(val_1).toWrapper()); - contractDetails.put(new DataWord(key_2).toWrapper(), new DataWord(val_2).toWrapper()); + contractDetails.put(new DataWordImpl(key_1).toWrapper(), new DataWordImpl(val_1).toWrapper()); + contractDetails.put(new DataWordImpl(key_2).toWrapper(), new DataWordImpl(val_2).toWrapper()); byte[] data = contractDetails.getEncoded(); @@ -95,14 +95,14 @@ public void test_1() throws Exception { ByteUtil.toHexString(val_1), ByteUtil.toHexString( contractDetails_ - .get(new DataWord(key_1).toWrapper()) + .get(new DataWordImpl(key_1).toWrapper()) .getNoLeadZeroesData())); assertEquals( ByteUtil.toHexString(val_2), ByteUtil.toHexString( contractDetails_ - .get(new DataWord(key_2).toWrapper()) + .get(new DataWordImpl(key_2).toWrapper()) .getNoLeadZeroesData())); } @@ -112,7 +112,7 @@ public void test_2() throws Exception { byte[] code = ByteUtil.hexStringToBytes( "7c0100000000000000000000000000000000000000000000000000000000600035046333d546748114610065578063430fe5f01461007c5780634d432c1d1461008d578063501385b2146100b857806357eb3b30146100e9578063dbc7df61146100fb57005b6100766004356024356044356102f0565b60006000f35b61008760043561039e565b60006000f35b610098600435610178565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b6100c96004356024356044356101a0565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b6100f1610171565b8060005260206000f35b610106600435610133565b8360005282602052816040528073ffffffffffffffffffffffffffffffffffffffff1660605260806000f35b5b60006020819052908152604090208054600182015460028301546003909301549192909173ffffffffffffffffffffffffffffffffffffffff1684565b5b60015481565b5b60026020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604081206002015481908302341080156101fe575073ffffffffffffffffffffffffffffffffffffffff8516600090815260208190526040812054145b8015610232575073ffffffffffffffffffffffffffffffffffffffff85166000908152602081905260409020600101548390105b61023b57610243565b3391506102e8565b6101966103ca60003973ffffffffffffffffffffffffffffffffffffffff3381166101965285166101b68190526000908152602081905260408120600201546101d6526101f68490526102169080f073ffffffffffffffffffffffffffffffffffffffff8616600090815260208190526040902060030180547fffffffffffffffffffffffff0000000000000000000000000000000000000000168217905591508190505b509392505050565b73ffffffffffffffffffffffffffffffffffffffff33166000908152602081905260408120548190821461032357610364565b60018054808201909155600090815260026020526040902080547fffffffffffffffffffffffff000000000000000000000000000000000000000016331790555b50503373ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090209081556001810192909255600290910155565b3373ffffffffffffffffffffffffffffffffffffffff166000908152602081905260409020600201555600608061019660043960048051602451604451606451600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000908116909517815560018054909516909317909355600355915561013390819061006390396000f3007c0100000000000000000000000000000000000000000000000000000000600035046347810fe381146100445780637e4a1aa81461005557806383d2421b1461006957005b61004f6004356100ab565b60006000f35b6100636004356024356100fc565b60006000f35b61007460043561007a565b60006000f35b6001543373ffffffffffffffffffffffffffffffffffffffff9081169116146100a2576100a8565b60078190555b50565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260026020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905550565b6001543373ffffffffffffffffffffffffffffffffffffffff9081169116146101245761012f565b600582905560068190555b505056"); - Address address = AionAddress.wrap(RandomUtils.nextBytes(Address.SIZE)); + Address address = Address.wrap(RandomUtils.nextBytes(Address.SIZE)); byte[] key_0 = ByteUtil.hexStringToBytes("18d63b70aa690ad37cb50908746c9a55"); byte[] val_0 = ByteUtil.hexStringToBytes("00000000000000000000000000000064"); @@ -157,20 +157,20 @@ public void test_2() throws Exception { AionContractDetailsImpl contractDetails = new AionContractDetailsImpl(); contractDetails.setCode(code); contractDetails.setAddress(address); - contractDetails.put(new DataWord(key_0).toWrapper(), new DataWord(val_0).toWrapper()); - contractDetails.put(new DataWord(key_1).toWrapper(), new DataWord(val_1).toWrapper()); - contractDetails.delete(new DataWord(key_2).toWrapper()); - contractDetails.put(new DataWord(key_3).toWrapper(), new DataWord(val_3).toWrapper()); - contractDetails.delete(new DataWord(key_4).toWrapper()); - contractDetails.put(new DataWord(key_5).toWrapper(), new DataWord(val_5).toWrapper()); - contractDetails.put(new DataWord(key_6).toWrapper(), new DataWord(val_6).toWrapper()); - contractDetails.put(new DataWord(key_7).toWrapper(), new DataWord(val_7).toWrapper()); - contractDetails.put(new DataWord(key_8).toWrapper(), new DataWord(val_8).toWrapper()); - contractDetails.put(new DataWord(key_9).toWrapper(), new DataWord(val_9).toWrapper()); - contractDetails.put(new DataWord(key_10).toWrapper(), new DataWord(val_10).toWrapper()); - contractDetails.put(new DataWord(key_11).toWrapper(), new DataWord(val_11).toWrapper()); - contractDetails.put(new DataWord(key_12).toWrapper(), new DataWord(val_12).toWrapper()); - contractDetails.put(new DataWord(key_13).toWrapper(), new DataWord(val_13).toWrapper()); + contractDetails.put(new DataWordImpl(key_0).toWrapper(), new DataWordImpl(val_0).toWrapper()); + contractDetails.put(new DataWordImpl(key_1).toWrapper(), new DataWordImpl(val_1).toWrapper()); + contractDetails.delete(new DataWordImpl(key_2).toWrapper()); + contractDetails.put(new DataWordImpl(key_3).toWrapper(), new DataWordImpl(val_3).toWrapper()); + contractDetails.delete(new DataWordImpl(key_4).toWrapper()); + contractDetails.put(new DataWordImpl(key_5).toWrapper(), new DataWordImpl(val_5).toWrapper()); + contractDetails.put(new DataWordImpl(key_6).toWrapper(), new DataWordImpl(val_6).toWrapper()); + contractDetails.put(new DataWordImpl(key_7).toWrapper(), new DataWordImpl(val_7).toWrapper()); + contractDetails.put(new DataWordImpl(key_8).toWrapper(), new DataWordImpl(val_8).toWrapper()); + contractDetails.put(new DataWordImpl(key_9).toWrapper(), new DataWordImpl(val_9).toWrapper()); + contractDetails.put(new DataWordImpl(key_10).toWrapper(), new DataWordImpl(val_10).toWrapper()); + contractDetails.put(new DataWordImpl(key_11).toWrapper(), new DataWordImpl(val_11).toWrapper()); + contractDetails.put(new DataWordImpl(key_12).toWrapper(), new DataWordImpl(val_12).toWrapper()); + contractDetails.put(new DataWordImpl(key_13).toWrapper(), new DataWordImpl(val_13).toWrapper()); byte[] data = contractDetails.getEncoded(); @@ -183,71 +183,71 @@ public void test_2() throws Exception { assertEquals( ByteUtil.toHexString(val_1), ByteUtil.toHexString( - contractDetails_.get(new DataWord(key_1).toWrapper()).getData())); + contractDetails_.get(new DataWordImpl(key_1).toWrapper()).getData())); - assertNull(contractDetails_.get(new DataWord(key_2).toWrapper())); + assertNull(contractDetails_.get(new DataWordImpl(key_2).toWrapper())); assertEquals( ByteUtil.toHexString(val_3), ByteUtil.toHexString( - contractDetails_.get(new DataWord(key_3).toWrapper()).getData())); + contractDetails_.get(new DataWordImpl(key_3).toWrapper()).getData())); - assertNull(contractDetails_.get(new DataWord(key_4).toWrapper())); + assertNull(contractDetails_.get(new DataWordImpl(key_4).toWrapper())); assertEquals( ByteUtil.toHexString(val_5), ByteUtil.toHexString( - contractDetails_.get(new DataWord(key_5).toWrapper()).getData())); + contractDetails_.get(new DataWordImpl(key_5).toWrapper()).getData())); assertEquals( ByteUtil.toHexString(val_6), ByteUtil.toHexString( - contractDetails_.get(new DataWord(key_6).toWrapper()).getData())); + contractDetails_.get(new DataWordImpl(key_6).toWrapper()).getData())); assertEquals( ByteUtil.toHexString(val_7), ByteUtil.toHexString( - contractDetails_.get(new DataWord(key_7).toWrapper()).getData())); + contractDetails_.get(new DataWordImpl(key_7).toWrapper()).getData())); assertEquals( ByteUtil.toHexString(val_8), ByteUtil.toHexString( - contractDetails_.get(new DataWord(key_8).toWrapper()).getData())); + contractDetails_.get(new DataWordImpl(key_8).toWrapper()).getData())); assertEquals( ByteUtil.toHexString(val_9), ByteUtil.toHexString( - contractDetails_.get(new DataWord(key_9).toWrapper()).getData())); + contractDetails_.get(new DataWordImpl(key_9).toWrapper()).getData())); assertEquals( ByteUtil.toHexString(val_10), ByteUtil.toHexString( - contractDetails_.get(new DataWord(key_10).toWrapper()).getData())); + contractDetails_.get(new DataWordImpl(key_10).toWrapper()).getData())); assertEquals( ByteUtil.toHexString(val_11), ByteUtil.toHexString( - contractDetails_.get(new DataWord(key_11).toWrapper()).getData())); + contractDetails_.get(new DataWordImpl(key_11).toWrapper()).getData())); assertEquals( ByteUtil.toHexString(val_12), ByteUtil.toHexString( - contractDetails_.get(new DataWord(key_12).toWrapper()).getData())); + contractDetails_.get(new DataWordImpl(key_12).toWrapper()).getData())); assertEquals( ByteUtil.toHexString(val_13), ByteUtil.toHexString( - contractDetails_.get(new DataWord(key_13).toWrapper()).getData())); + contractDetails_.get(new DataWordImpl(key_13).toWrapper()).getData())); } @Test public void testExternalStorageSerialization() { - Address address = AionAddress.wrap(RandomUtils.nextBytes(Address.SIZE)); + Address address = Address.wrap(RandomUtils.nextBytes(Address.SIZE)); byte[] code = RandomUtils.nextBytes(512); - Map<DataWord, DataWord> elements = new HashMap<>(); + Map<DataWordImpl, DataWordImpl> elements = new HashMap<>(); AionRepositoryImpl repository = AionRepositoryImpl.createForTesting(repoConfig); - IByteArrayKeyValueDatabase externalStorage = repository.getDetailsDatabase(); + ByteArrayKeyValueDatabase externalStorage = repository.getDetailsDatabase(); AionContractDetailsImpl original = new AionContractDetailsImpl(0, 1000000); @@ -257,8 +257,8 @@ public void testExternalStorageSerialization() { original.externalStorage = true; for (int i = 0; i < IN_MEMORY_STORAGE_LIMIT / 64 + 10; i++) { - DataWord key = new DataWord(RandomUtils.nextBytes(16)); - DataWord value = new DataWord(RandomUtils.nextBytes(16)); + DataWordImpl key = new DataWordImpl(RandomUtils.nextBytes(16)); + DataWordImpl value = new DataWordImpl(RandomUtils.nextBytes(16)); elements.put(key, value); original.put(key.toWrapper(), wrapValueForPut(value)); @@ -276,26 +276,26 @@ public void testExternalStorageSerialization() { assertTrue(address.equals(deserialized.getAddress())); assertEquals(ByteUtil.toHexString(code), ByteUtil.toHexString(deserialized.getCode())); - for (DataWord key : elements.keySet()) { + for (DataWordImpl key : elements.keySet()) { assertEquals( elements.get(key).toWrapper(), wrapValueFromGet(deserialized.get(key.toWrapper()))); } - DataWord deletedKey = elements.keySet().iterator().next(); + DataWordImpl deletedKey = elements.keySet().iterator().next(); deserialized.delete(deletedKey.toWrapper()); - deserialized.delete(new DataWord(RandomUtils.nextBytes(16)).toWrapper()); + deserialized.delete(new DataWordImpl(RandomUtils.nextBytes(16)).toWrapper()); } @Test public void testExternalStorageTransition() { - Address address = AionAddress.wrap(RandomUtils.nextBytes(Address.SIZE)); + Address address = Address.wrap(RandomUtils.nextBytes(Address.SIZE)); byte[] code = RandomUtils.nextBytes(512); - Map<DataWord, DataWord> elements = new HashMap<>(); + Map<DataWordImpl, DataWordImpl> elements = new HashMap<>(); AionRepositoryImpl repository = AionRepositoryImpl.createForTesting(repoConfig); - IByteArrayKeyValueDatabase externalStorage = repository.getDetailsDatabase(); + ByteArrayKeyValueDatabase externalStorage = repository.getDetailsDatabase(); AionContractDetailsImpl original = new AionContractDetailsImpl(0, 1000000); original.setExternalStorageDataSource(externalStorage); @@ -303,8 +303,8 @@ public void testExternalStorageTransition() { original.setCode(code); for (int i = 0; i < IN_MEMORY_STORAGE_LIMIT / 64 + 10; i++) { - DataWord key = new DataWord(RandomUtils.nextBytes(16)); - DataWord value = new DataWord(RandomUtils.nextBytes(16)); + DataWordImpl key = new DataWordImpl(RandomUtils.nextBytes(16)); + DataWordImpl value = new DataWordImpl(RandomUtils.nextBytes(16)); elements.put(key, value); original.put(key.toWrapper(), wrapValueForPut(value)); @@ -313,12 +313,12 @@ public void testExternalStorageTransition() { original.syncStorage(); assertTrue(!externalStorage.isEmpty()); - IContractDetails deserialized = deserialize(original.getEncoded(), externalStorage); + ContractDetails deserialized = deserialize(original.getEncoded(), externalStorage); // adds keys for in-memory storage limit overflow for (int i = 0; i < 10; i++) { - DataWord key = new DataWord(RandomUtils.nextBytes(16)); - DataWord value = new DataWord(RandomUtils.nextBytes(16)); + DataWordImpl key = new DataWordImpl(RandomUtils.nextBytes(16)); + DataWordImpl value = new DataWordImpl(RandomUtils.nextBytes(16)); elements.put(key, value); @@ -330,20 +330,20 @@ public void testExternalStorageTransition() { deserialized = deserialize(deserialized.getEncoded(), externalStorage); - for (DataWord key : elements.keySet()) { + for (DataWordImpl key : elements.keySet()) { assertEquals( elements.get(key).toWrapper(), wrapValueFromGet(deserialized.get(key.toWrapper()))); } } - private static ByteArrayWrapper wrapValueForPut(DataWord value) { + private static ByteArrayWrapper wrapValueForPut(DataWordImpl value) { return (value.isZero()) ? new ByteArrayWrapper(value.getData()) : new ByteArrayWrapper(value.getNoLeadZeroesData()); } private static ByteArrayWrapper wrapValueFromGet(ByteArrayWrapper value) { - return new ByteArrayWrapper(new DataWord(value.getData()).getData()); + return new ByteArrayWrapper(new DataWordImpl(value.getData()).getData()); } } diff --git a/modAionImpl/test/org/aion/db/DoubleDataWordTest.java b/modAionImpl/test/org/aion/db/DoubleDataWordTest.java index 444bbd952d..e67727b022 100644 --- a/modAionImpl/test/org/aion/db/DoubleDataWordTest.java +++ b/modAionImpl/test/org/aion/db/DoubleDataWordTest.java @@ -5,21 +5,17 @@ import java.util.Properties; import java.util.Random; -import org.aion.base.db.IContractDetails; -import org.aion.base.db.IPruneConfig; -import org.aion.base.db.IRepository; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.db.IRepositoryConfig; -import org.aion.base.type.AionAddress; +import org.aion.interfaces.db.*; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; import org.aion.crypto.ECKeyFac; import org.aion.db.impl.DBVendor; import org.aion.db.impl.DatabaseFactory; import org.aion.mcf.config.CfgPrune; import org.aion.mcf.core.AccountState; import org.aion.mcf.db.IBlockStoreBase; -import org.aion.mcf.vm.types.DataWord; import org.aion.mcf.vm.types.DoubleDataWord; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.db.AionRepositoryCache; import org.aion.zero.impl.db.AionRepositoryImpl; import org.aion.zero.impl.db.ContractDetailsAion; @@ -29,28 +25,28 @@ /** Tests the DoubleDataWord class, mainly that it integrates well with the db. */ public class DoubleDataWordTest { - private IRepositoryConfig repoConfig; - private IRepository repo; - private IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> track; + private RepositoryConfig repoConfig; + private Repository repo; + private RepositoryCache<AccountState, IBlockStoreBase<?, ?>> track; private Random rand; private Address addr; @Before public void setup() { this.repoConfig = - new IRepositoryConfig() { + new RepositoryConfig() { @Override public String getDbPath() { return ""; } @Override - public IPruneConfig getPruneConfig() { + public PruneConfig getPruneConfig() { return new CfgPrune(false); } @Override - public IContractDetails contractDetailsImpl() { + public ContractDetails contractDetailsImpl() { return ContractDetailsAion.createForTesting(0, 1000000).getDetails(); } @@ -66,7 +62,7 @@ public Properties getDatabaseConfig(String db_name) { this.repo = AionRepositoryImpl.createForTesting(repoConfig); this.track = new AionRepositoryCache(repo); this.rand = new Random(); - this.addr = AionAddress.wrap(ECKeyFac.inst().create().getAddress()); + this.addr = Address.wrap(ECKeyFac.inst().create().getAddress()); } @After @@ -87,14 +83,14 @@ public void tearDown() { public void testPrefixSuffixAmbiguity() { // doubleKeyFirst has its first 16 bytes identical to singleKey // doubleKeyLast has its last 16 bytes identical to singleKey - byte[] singleKey = new byte[DataWord.BYTES]; + byte[] singleKey = new byte[DataWordImpl.BYTES]; byte[] doubleKeyFirst = new byte[DoubleDataWord.BYTES]; byte[] doubleKeyLast = new byte[DoubleDataWord.BYTES]; rand.nextBytes(singleKey); - System.arraycopy(singleKey, 0, doubleKeyFirst, 0, DataWord.BYTES); - System.arraycopy(singleKey, 0, doubleKeyLast, DataWord.BYTES, DataWord.BYTES); + System.arraycopy(singleKey, 0, doubleKeyFirst, 0, DataWordImpl.BYTES); + System.arraycopy(singleKey, 0, doubleKeyLast, DataWordImpl.BYTES, DataWordImpl.BYTES); - byte[] singleVal = new byte[DataWord.BYTES]; + byte[] singleVal = new byte[DataWordImpl.BYTES]; byte[] doubleValFirst = new byte[DoubleDataWord.BYTES]; byte[] doubleValLast = new byte[DoubleDataWord.BYTES]; singleVal[0] = (byte) 0x1; @@ -102,7 +98,7 @@ public void testPrefixSuffixAmbiguity() { doubleValLast[0] = (byte) 0x3; track.addStorageRow( - addr, new DataWord(singleKey).toWrapper(), new DataWord(singleVal).toWrapper()); + addr, new DataWordImpl(singleKey).toWrapper(), new DataWordImpl(singleVal).toWrapper()); track.addStorageRow( addr, new DoubleDataWord(doubleKeyFirst).toWrapper(), @@ -114,7 +110,7 @@ public void testPrefixSuffixAmbiguity() { track.flush(); byte[] singleRes = - track.getStorageValue(addr, new DataWord(singleKey).toWrapper()).getData(); + track.getStorageValue(addr, new DataWordImpl(singleKey).toWrapper()).getData(); byte[] doubleResFirst = track.getStorageValue(addr, new DoubleDataWord(doubleKeyFirst).toWrapper()) .getData(); @@ -133,21 +129,21 @@ public void testPrefixSuffixAmbiguity() { */ @Test public void testPrefixSuffixAmbiguity2() { - byte[] singKey = new byte[DataWord.BYTES]; + byte[] singKey = new byte[DataWordImpl.BYTES]; rand.nextBytes(singKey); - byte[] singVal = new byte[DataWord.BYTES]; + byte[] singVal = new byte[DataWordImpl.BYTES]; singVal[0] = (byte) 0xAC; track.addStorageRow( - addr, new DataWord(singKey).toWrapper(), new DataWord(singVal).toWrapper()); + addr, new DataWordImpl(singKey).toWrapper(), new DataWordImpl(singVal).toWrapper()); track.flush(); byte[] doubleKeyPrefix = new byte[DoubleDataWord.BYTES]; byte[] doubleKeySuffix = new byte[DoubleDataWord.BYTES]; - System.arraycopy(singKey, 0, doubleKeyPrefix, 0, DataWord.BYTES); - System.arraycopy(singKey, 0, doubleKeySuffix, DataWord.BYTES, DataWord.BYTES); + System.arraycopy(singKey, 0, doubleKeyPrefix, 0, DataWordImpl.BYTES); + System.arraycopy(singKey, 0, doubleKeySuffix, DataWordImpl.BYTES, DataWordImpl.BYTES); - byte[] singRes = track.getStorageValue(addr, new DataWord(singKey).toWrapper()).getData(); + byte[] singRes = track.getStorageValue(addr, new DataWordImpl(singKey).toWrapper()).getData(); assertArrayEquals(singVal, singRes); assertNull(track.getStorageValue(addr, new DoubleDataWord(doubleKeyPrefix).toWrapper())); assertNull(track.getStorageValue(addr, new DoubleDataWord(doubleKeySuffix).toWrapper())); @@ -159,9 +155,9 @@ public void testPrefixSuffixAmbiguity2() { */ @Test public void testMixOfSingleAndDoubleDataWordsInRepo() { - byte[] key16 = new byte[DataWord.BYTES]; + byte[] key16 = new byte[DataWordImpl.BYTES]; byte[] key32 = new byte[DoubleDataWord.BYTES]; - byte[] val16 = new byte[DataWord.BYTES]; + byte[] val16 = new byte[DataWordImpl.BYTES]; byte[] val32 = new byte[DoubleDataWord.BYTES]; rand.nextBytes(key16); rand.nextBytes(key32); @@ -169,14 +165,14 @@ public void testMixOfSingleAndDoubleDataWordsInRepo() { rand.nextBytes(val32); track.addStorageRow( - addr, new DataWord(key16).toWrapper(), new DoubleDataWord(val32).toWrapper()); + addr, new DataWordImpl(key16).toWrapper(), new DoubleDataWord(val32).toWrapper()); track.addStorageRow( - addr, new DoubleDataWord(key32).toWrapper(), new DataWord(val16).toWrapper()); + addr, new DoubleDataWord(key32).toWrapper(), new DataWordImpl(val16).toWrapper()); assertArrayEquals( val16, track.getStorageValue(addr, new DoubleDataWord(key32).toWrapper()).getData()); assertArrayEquals( - val32, track.getStorageValue(addr, new DataWord(key16).toWrapper()).getData()); + val32, track.getStorageValue(addr, new DataWordImpl(key16).toWrapper()).getData()); } } diff --git a/modAionImpl/test/org/aion/equihash/EquihashSolutionsGenerationTest210_9.java b/modAionImpl/test/org/aion/equihash/EquihashSolutionsGenerationTest210_9.java index d63b9bb827..7ddfcd4e0e 100644 --- a/modAionImpl/test/org/aion/equihash/EquihashSolutionsGenerationTest210_9.java +++ b/modAionImpl/test/org/aion/equihash/EquihashSolutionsGenerationTest210_9.java @@ -375,7 +375,7 @@ public void testMine_wBlockData(AionBlock block) { // use real method for mine call when(spy.mine(block, header.getNonce())).thenCallRealMethod(); - Solution sol = spy.mine(block, block.getNonce()); + SolutionImpl sol = spy.mine(block, block.getNonce()); assertNotNull(sol); assertArrayEquals(header.getNonce(), sol.getNonce()); diff --git a/modAionImpl/test/org/aion/equihash/benchmark/BatchHeaderBenchmark.java b/modAionImpl/test/org/aion/equihash/benchmark/BatchHeaderBenchmark.java index 4bc2a19c0a..a9ab61e458 100644 --- a/modAionImpl/test/org/aion/equihash/benchmark/BatchHeaderBenchmark.java +++ b/modAionImpl/test/org/aion/equihash/benchmark/BatchHeaderBenchmark.java @@ -1,6 +1,6 @@ // package org.aion.equihash.benchmark; // -// import org.aion.base.type.AionAddress; +// import org.aion.types.Address; // import org.aion.base.util.ByteUtil; // import org.aion.equihash.EquiUtils; // import org.aion.equihash.OptimizedEquiValidator; diff --git a/modAionImpl/test/org/aion/zero/impl/AionHubTest.java b/modAionImpl/test/org/aion/zero/impl/AionHubTest.java index 17d99693d4..ccc9e2588b 100644 --- a/modAionImpl/test/org/aion/zero/impl/AionHubTest.java +++ b/modAionImpl/test/org/aion/zero/impl/AionHubTest.java @@ -8,7 +8,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.aion.base.db.IByteArrayKeyValueDatabase; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; import org.aion.log.AionLoggerFactory; import org.aion.mcf.core.ImportResult; import org.aion.mcf.trie.TrieImpl; @@ -155,7 +155,7 @@ public void MockHubInst_wStartRecovery() { // delete some world state root entries from the database TrieImpl trie = (TrieImpl) repo.getWorldState(); - IByteArrayKeyValueDatabase database = repo.getStateDatabase(); + ByteArrayKeyValueDatabase database = repo.getStateDatabase(); repo.flush(); for (byte[] key : statesToDelete) { diff --git a/modAionImpl/test/org/aion/zero/impl/BlockchainAccountStateBenchmark.java b/modAionImpl/test/org/aion/zero/impl/BlockchainAccountStateBenchmark.java index 22211fce14..5985c65eae 100644 --- a/modAionImpl/test/org/aion/zero/impl/BlockchainAccountStateBenchmark.java +++ b/modAionImpl/test/org/aion/zero/impl/BlockchainAccountStateBenchmark.java @@ -9,14 +9,14 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteUtil; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.crypto.HashUtil; import org.aion.db.impl.DBVendor; import org.aion.db.utils.FileUtils; import org.aion.mcf.core.ImportResult; -import org.aion.vm.api.interfaces.Address; + +import org.aion.util.bytes.ByteUtil; import org.aion.zero.impl.types.AionBlock; import org.aion.zero.impl.types.AionTxInfo; import org.aion.zero.types.AionTransaction; @@ -157,13 +157,13 @@ public void testAccountState() { private static AionBlock createBundleAndCheck( StandaloneBlockchain bc, ECKey key, AionBlock parentBlock) { - BigInteger accountNonce = bc.getRepository().getNonce(new AionAddress(key.getAddress())); + BigInteger accountNonce = bc.getRepository().getNonce(new Address(key.getAddress())); List<AionTransaction> transactions = new ArrayList<>(); // create 400 transactions per bundle // byte[] nonce, Address to, byte[] value, byte[] data, long nrg, long nrgPrice for (int i = 0; i < 400; i++) { - Address destAddr = new AionAddress(HashUtil.h256(accountNonce.toByteArray())); + Address destAddr = new Address(HashUtil.h256(accountNonce.toByteArray())); AionTransaction sendTransaction = new AionTransaction( accountNonce.toByteArray(), @@ -231,13 +231,13 @@ public void testExpandOneAccountStorage() throws InterruptedException { public static Pair<AionBlock, byte[]> createContract( StandaloneBlockchain bc, ECKey key, AionBlock parentBlock) { - BigInteger accountNonce = bc.getRepository().getNonce(new AionAddress(key.getAddress())); + BigInteger accountNonce = bc.getRepository().getNonce(new Address(key.getAddress())); // deploy AionTransaction creationTx = new AionTransaction( accountNonce.toByteArray(), - AionAddress.EMPTY_ADDRESS(), + null, BigInteger.ZERO.toByteArray(), ByteUtil.hexStringToBytes(STATE_EXPANSION_BYTECODE), 1000000, @@ -253,7 +253,7 @@ private static AionBlock createContractBundle( final ECKey key, final AionBlock parentBlock, final Address contractAddress) { - BigInteger accountNonce = bc.getRepository().getNonce(new AionAddress(key.getAddress())); + BigInteger accountNonce = bc.getRepository().getNonce(new Address(key.getAddress())); List<AionTransaction> transactions = new ArrayList<>(); // command diff --git a/modAionImpl/test/org/aion/zero/impl/BlockchainAccountStateTest.java b/modAionImpl/test/org/aion/zero/impl/BlockchainAccountStateTest.java index 101b177453..d564f10b93 100644 --- a/modAionImpl/test/org/aion/zero/impl/BlockchainAccountStateTest.java +++ b/modAionImpl/test/org/aion/zero/impl/BlockchainAccountStateTest.java @@ -5,11 +5,11 @@ import java.math.BigInteger; import java.util.ArrayList; import java.util.List; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.crypto.HashUtil; import org.aion.mcf.core.ImportResult; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.impl.types.AionBlock; import org.aion.zero.types.AionTransaction; import org.junit.Ignore; @@ -44,13 +44,13 @@ public void testAccountState() { private static AionBlock createBundleAndCheck( StandaloneBlockchain bc, ECKey key, AionBlock parentBlock) { - BigInteger accountNonce = bc.getRepository().getNonce(new AionAddress(key.getAddress())); + BigInteger accountNonce = bc.getRepository().getNonce(new Address(key.getAddress())); List<AionTransaction> transactions = new ArrayList<>(); // create 400 transactions per bundle // byte[] nonce, Address to, byte[] value, byte[] data, long nrg, long nrgPrice for (int i = 0; i < 400; i++) { - Address destAddr = new AionAddress(HashUtil.h256(accountNonce.toByteArray())); + Address destAddr = new Address(HashUtil.h256(accountNonce.toByteArray())); AionTransaction sendTransaction = new AionTransaction( accountNonce.toByteArray(), @@ -108,13 +108,13 @@ public void testExpandAccountStorage() { private static AionBlock createContractBundle( StandaloneBlockchain bc, ECKey key, AionBlock parentBlock) { - BigInteger accountNonce = bc.getRepository().getNonce(new AionAddress(key.getAddress())); + BigInteger accountNonce = bc.getRepository().getNonce(new Address(key.getAddress())); List<AionTransaction> transactions = new ArrayList<>(); // create 400 transactions per bundle // byte[] nonce, Address to, byte[] value, byte[] data, long nrg, long nrgPrice for (int i = 0; i < 400; i++) { - Address destAddr = new AionAddress(HashUtil.h256(accountNonce.toByteArray())); + Address destAddr = new Address(HashUtil.h256(accountNonce.toByteArray())); AionTransaction sendTransaction = new AionTransaction( accountNonce.toByteArray(), diff --git a/modAionImpl/test/org/aion/zero/impl/BlockchainConcurrentImportTest.java b/modAionImpl/test/org/aion/zero/impl/BlockchainConcurrentImportTest.java index 32b03aa83b..2c0017866e 100644 --- a/modAionImpl/test/org/aion/zero/impl/BlockchainConcurrentImportTest.java +++ b/modAionImpl/test/org/aion/zero/impl/BlockchainConcurrentImportTest.java @@ -17,10 +17,10 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import org.aion.base.type.Hash256; import org.aion.crypto.ECKey; import org.aion.log.AionLoggerFactory; import org.aion.mcf.core.ImportResult; +import org.aion.types.Hash256; import org.aion.zero.impl.db.AionBlockStore; import org.aion.zero.impl.db.AionRepositoryImpl; import org.aion.zero.impl.types.AionBlock; diff --git a/modAionImpl/test/org/aion/zero/impl/BlockchainDataRecoveryTest.java b/modAionImpl/test/org/aion/zero/impl/BlockchainDataRecoveryTest.java index 26acb67e03..5746efd391 100644 --- a/modAionImpl/test/org/aion/zero/impl/BlockchainDataRecoveryTest.java +++ b/modAionImpl/test/org/aion/zero/impl/BlockchainDataRecoveryTest.java @@ -7,13 +7,13 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import org.aion.base.db.IByteArrayKeyValueDatabase; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.Hex; import org.aion.crypto.ECKey; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; import org.aion.log.AionLoggerFactory; import org.aion.mcf.core.ImportResult; import org.aion.mcf.trie.TrieImpl; +import org.aion.util.bytes.ByteUtil; +import org.aion.util.conversions.Hex; import org.aion.zero.impl.db.AionRepositoryImpl; import org.aion.zero.impl.types.AionBlock; import org.aion.zero.types.AionTransaction; @@ -76,7 +76,7 @@ public void testRecoverWorldStateWithPartialWorldState() { // delete some world state root entries from the database TrieImpl trie = (TrieImpl) repo.getWorldState(); - IByteArrayKeyValueDatabase database = repo.getStateDatabase(); + ByteArrayKeyValueDatabase database = repo.getStateDatabase(); // 1: direct recovery call @@ -155,7 +155,7 @@ public void testRecoverWorldStateWithStartFromGenesis() { // delete some world state root entries from the database TrieImpl trie = (TrieImpl) repo.getWorldState(); - IByteArrayKeyValueDatabase database = repo.getStateDatabase(); + ByteArrayKeyValueDatabase database = repo.getStateDatabase(); // 1: direct recovery call @@ -236,7 +236,7 @@ public void testRecoverWorldStateWithoutGenesis() { // delete some world state root entries from the database TrieImpl trie = (TrieImpl) repo.getWorldState(); - IByteArrayKeyValueDatabase database = repo.getStateDatabase(); + ByteArrayKeyValueDatabase database = repo.getStateDatabase(); // 1: direct recovery call @@ -367,7 +367,7 @@ public void testRecoverWorldState_wDeletedBlock() { // delete some world state root entries from the database TrieImpl trie = (TrieImpl) repo.getWorldState(); - IByteArrayKeyValueDatabase database = repo.getStateDatabase(); + ByteArrayKeyValueDatabase database = repo.getStateDatabase(); // 1: direct recovery call @@ -477,7 +477,7 @@ public void testRecoverIndexWithPartialIndex_MainChain() { assertThat(bestBlock.getNumber()).isEqualTo(NUMBER_OF_BLOCKS); // delete index entries from the database - IByteArrayKeyValueDatabase indexDatabase = repo.getIndexDatabase(); + ByteArrayKeyValueDatabase indexDatabase = repo.getIndexDatabase(); // 1: direct recovery call @@ -637,7 +637,7 @@ public void testRecoverIndexWithPartialIndex_ShorterSideChain() { assertThat(bestBlock.getHash()).isEqualTo(mainChainBlock.getHash()); // delete index entries from the database - IByteArrayKeyValueDatabase indexDatabase = repo.getIndexDatabase(); + ByteArrayKeyValueDatabase indexDatabase = repo.getIndexDatabase(); // 1: direct recovery call @@ -769,7 +769,7 @@ public void testRecoverIndexWithStartFromGenesis() { assertThat(bestBlock.getNumber()).isEqualTo(NUMBER_OF_BLOCKS); // delete index entries from the database - IByteArrayKeyValueDatabase indexDatabase = repo.getIndexDatabase(); + ByteArrayKeyValueDatabase indexDatabase = repo.getIndexDatabase(); // 1: direct recovery call @@ -881,7 +881,7 @@ public void testRecoverIndexWithoutGenesis() { AionBlock bestBlock = chain.getBestBlock(); assertThat(bestBlock.getNumber()).isEqualTo(NUMBER_OF_BLOCKS); - IByteArrayKeyValueDatabase indexDatabase = repo.getIndexDatabase(); + ByteArrayKeyValueDatabase indexDatabase = repo.getIndexDatabase(); // 1: direct recovery call @@ -958,7 +958,7 @@ public void testRecoverIndexWithStartFromGenesisWithoutSize() { assertThat(bestBlock.getNumber()).isEqualTo(NUMBER_OF_BLOCKS); // delete index entries from the database - IByteArrayKeyValueDatabase indexDatabase = repo.getIndexDatabase(); + ByteArrayKeyValueDatabase indexDatabase = repo.getIndexDatabase(); // 1: direct recovery call @@ -1127,7 +1127,7 @@ public void testRecoverIndex_wDeletedBlock() { repo.getBlockDatabase().delete(middle.getHash()); // delete index entries from the database - IByteArrayKeyValueDatabase indexDatabase = repo.getIndexDatabase(); + ByteArrayKeyValueDatabase indexDatabase = repo.getIndexDatabase(); // 1: direct recovery call diff --git a/modAionImpl/test/org/aion/zero/impl/BlockchainEnergyTest.java b/modAionImpl/test/org/aion/zero/impl/BlockchainEnergyTest.java index 38fef24bc1..8ecc0911de 100644 --- a/modAionImpl/test/org/aion/zero/impl/BlockchainEnergyTest.java +++ b/modAionImpl/test/org/aion/zero/impl/BlockchainEnergyTest.java @@ -6,10 +6,9 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteUtil; +import org.aion.types.Address; import org.aion.mcf.core.ImportResult; -import org.aion.vm.api.interfaces.Address; +import org.aion.util.bytes.ByteUtil; import org.aion.zero.impl.types.AionBlock; import org.aion.zero.types.AionTransaction; import org.junit.Test; @@ -35,7 +34,7 @@ public void testConsistentEnergyUsage() { public void testEnergyUsageRecorded() { final int DEFAULT_TX_AMOUNT = 21000; final Address RECEIPT_ADDR = - AionAddress.wrap( + Address.wrap( "CAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFE"); StandaloneBlockchain.Bundle bundle = diff --git a/modAionImpl/test/org/aion/zero/impl/BlockchainForkingTest.java b/modAionImpl/test/org/aion/zero/impl/BlockchainForkingTest.java index 6951051449..79cf9aa8ad 100644 --- a/modAionImpl/test/org/aion/zero/impl/BlockchainForkingTest.java +++ b/modAionImpl/test/org/aion/zero/impl/BlockchainForkingTest.java @@ -5,10 +5,10 @@ import java.math.BigInteger; import java.util.Collections; import java.util.List; -import org.aion.base.util.BIUtil; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; import org.aion.crypto.ECKey; import org.aion.mcf.core.ImportResult; +import org.aion.util.biginteger.BIUtil; import org.aion.zero.impl.blockchain.ChainConfiguration; import org.aion.zero.impl.types.AionBlock; import org.junit.Test; diff --git a/modAionImpl/test/org/aion/zero/impl/BlockchainImplementationTest.java b/modAionImpl/test/org/aion/zero/impl/BlockchainImplementationTest.java index eaefd5bf81..d143843d65 100644 --- a/modAionImpl/test/org/aion/zero/impl/BlockchainImplementationTest.java +++ b/modAionImpl/test/org/aion/zero/impl/BlockchainImplementationTest.java @@ -5,7 +5,7 @@ import java.util.ArrayList; import java.util.List; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; import org.aion.crypto.ECKey; import org.aion.mcf.config.CfgPrune; import org.aion.mcf.core.ImportResult; diff --git a/modAionImpl/test/org/aion/zero/impl/BlockchainIndexIntegrityTest.java b/modAionImpl/test/org/aion/zero/impl/BlockchainIndexIntegrityTest.java index c14eaa226c..77ef4808e5 100644 --- a/modAionImpl/test/org/aion/zero/impl/BlockchainIndexIntegrityTest.java +++ b/modAionImpl/test/org/aion/zero/impl/BlockchainIndexIntegrityTest.java @@ -8,12 +8,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.aion.base.db.IByteArrayKeyValueDatabase; -import org.aion.base.util.ByteUtil; + +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; import org.aion.log.AionLoggerFactory; import org.aion.mcf.core.ImportResult; import org.aion.mcf.ds.DataSourceArray; import org.aion.mcf.ds.ObjectDataSource; +import org.aion.util.bytes.ByteUtil; import org.aion.zero.impl.db.AionBlockStore; import org.aion.zero.impl.db.AionRepositoryImpl; import org.aion.zero.impl.types.AionBlock; @@ -62,7 +63,7 @@ public void testIndexIntegrityWithoutGenesis() { chain.getRepository().flush(); AionRepositoryImpl repo = (AionRepositoryImpl) chain.getRepository(); - IByteArrayKeyValueDatabase indexDatabase = repo.getIndexDatabase(); + ByteArrayKeyValueDatabase indexDatabase = repo.getIndexDatabase(); // deleting the genesis index indexDatabase.delete(ByteUtil.intToBytes(0)); @@ -104,7 +105,7 @@ public void testIndexIntegrityWithoutLevel() { chain.getRepository().flush(); AionRepositoryImpl repo = (AionRepositoryImpl) chain.getRepository(); - IByteArrayKeyValueDatabase indexDatabase = repo.getIndexDatabase(); + ByteArrayKeyValueDatabase indexDatabase = repo.getIndexDatabase(); // deleting the level 2 index indexDatabase.delete(ByteUtil.intToBytes(2)); @@ -148,7 +149,7 @@ public void testIndexIntegrityWithRecovery() { chain.getRepository().flush(); AionRepositoryImpl repo = (AionRepositoryImpl) chain.getRepository(); - IByteArrayKeyValueDatabase indexDatabase = repo.getIndexDatabase(); + ByteArrayKeyValueDatabase indexDatabase = repo.getIndexDatabase(); // corrupting the index at level 2 DataSourceArray<List<AionBlockStore.BlockInfo>> index = diff --git a/modAionImpl/test/org/aion/zero/impl/BlockchainIntegrationTest.java b/modAionImpl/test/org/aion/zero/impl/BlockchainIntegrationTest.java index 82d7e1e666..7850785c49 100644 --- a/modAionImpl/test/org/aion/zero/impl/BlockchainIntegrationTest.java +++ b/modAionImpl/test/org/aion/zero/impl/BlockchainIntegrationTest.java @@ -5,17 +5,17 @@ import java.math.BigInteger; import java.util.Arrays; import java.util.Collections; -import org.aion.base.db.IRepository; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteUtil; + +import org.aion.interfaces.db.Repository; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.crypto.HashUtil; import org.aion.mcf.core.ImportResult; -import org.aion.vm.api.interfaces.Address; + +import org.aion.util.bytes.ByteUtil; import org.aion.zero.impl.blockchain.ChainConfiguration; import org.aion.zero.impl.types.AionBlock; import org.aion.zero.types.AionTransaction; -import org.junit.Ignore; import org.junit.Test; /** @@ -54,7 +54,7 @@ public void testSimpleBlockchainLoad() { StandaloneBlockchain.Bundle b = (new StandaloneBlockchain.Builder()).withDefaultAccounts().build(); for (ECKey k : b.privateKeys) { - assertThat(b.bc.getRepository().getBalance(AionAddress.wrap(k.getAddress()))) + assertThat(b.bc.getRepository().getBalance(Address.wrap(k.getAddress()))) .isNotEqualTo(BigInteger.ZERO); } assertThat(b.privateKeys.size()).isEqualTo(10); @@ -73,7 +73,7 @@ public void testCreateNewEmptyBlock() { public void testSimpleFailedTransactionInsufficientBalance() { // generate a recipient final Address receiverAddress = - AionAddress.wrap( + Address.wrap( ByteUtil.hexStringToBytes( "CAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFE")); @@ -108,7 +108,7 @@ public void testSimpleFailedTransactionInsufficientBalance() { public void testSimpleOneTokenBalanceTransfer() { // generate a recipient final Address receiverAddress = - AionAddress.wrap( + Address.wrap( ByteUtil.hexStringToBytes( "CAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFE")); @@ -121,7 +121,7 @@ public void testSimpleOneTokenBalanceTransfer() { final ECKey sender = bundle.privateKeys.get(0); final BigInteger senderInitialBalance = - bc.getRepository().getBalance(AionAddress.wrap(sender.getAddress())); + bc.getRepository().getBalance(Address.wrap(sender.getAddress())); AionTransaction tx = new AionTransaction( @@ -142,10 +142,10 @@ public void testSimpleOneTokenBalanceTransfer() { assertThat(connection).isEqualTo(ImportResult.IMPORTED_BEST); // to be sure, perform some DB tests - IRepository repo = bc.getRepository(); + Repository repo = bc.getRepository(); assertThat(repo.getBalance(receiverAddress)).isEqualTo(BigInteger.valueOf(100)); - assertThat(repo.getBalance(AionAddress.wrap(sender.getAddress()))) + assertThat(repo.getBalance(Address.wrap(sender.getAddress()))) .isEqualTo( senderInitialBalance .subtract(BigInteger.valueOf(21000)) @@ -174,7 +174,7 @@ public void testAppendIncorrectTimestampBlock() { public void testPruningEnabledBalanceTransfer() { // generate a recipient final Address receiverAddress = - AionAddress.wrap( + Address.wrap( ByteUtil.hexStringToBytes( "CAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFE")); @@ -231,7 +231,7 @@ public void testDeployCryptoKitties() { AionTransaction contractDeploymentTx = new AionTransaction( BigInteger.ZERO.toByteArray(), - AionAddress.EMPTY_ADDRESS(), + null, BigInteger.ZERO.toByteArray(), ByteUtil.hexStringToBytes(cryptoKittiesCode), 4699999L, diff --git a/modAionImpl/test/org/aion/zero/impl/BlockchainRewardTest.java b/modAionImpl/test/org/aion/zero/impl/BlockchainRewardTest.java index 987ec84fda..ee4399d44e 100644 --- a/modAionImpl/test/org/aion/zero/impl/BlockchainRewardTest.java +++ b/modAionImpl/test/org/aion/zero/impl/BlockchainRewardTest.java @@ -6,7 +6,8 @@ import java.util.Collections; import org.aion.mcf.blockchain.IBlockConstants; import org.aion.mcf.core.ImportResult; -import org.aion.vm.api.interfaces.Address; + +import org.aion.types.Address; import org.aion.zero.api.BlockConstants; import org.aion.zero.impl.types.AionBlock; import org.junit.Ignore; diff --git a/modAionImpl/test/org/aion/zero/impl/BlockchainTestUtils.java b/modAionImpl/test/org/aion/zero/impl/BlockchainTestUtils.java index 414741bce7..3b766a7363 100644 --- a/modAionImpl/test/org/aion/zero/impl/BlockchainTestUtils.java +++ b/modAionImpl/test/org/aion/zero/impl/BlockchainTestUtils.java @@ -7,12 +7,12 @@ import java.util.List; import java.util.Map; import java.util.Random; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; import org.aion.crypto.HashUtil; import org.aion.mcf.core.ImportResult; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.impl.db.AionRepositoryImpl; import org.aion.zero.impl.types.AionBlock; import org.aion.zero.types.AionTransaction; @@ -46,7 +46,7 @@ public static List<AionTransaction> generateTransactions( // get the current nonce for each account Map<ECKey, BigInteger> nonces = new HashMap<>(); for (ECKey key : accounts) { - nonces.put(key, repo.getNonce(new AionAddress(key.getAddress()))); + nonces.put(key, repo.getNonce(new Address(key.getAddress()))); } List<AionTransaction> transactions = new ArrayList<>(); @@ -59,7 +59,7 @@ public static List<AionTransaction> generateTransactions( // generate a random Aion account address byte[] aionBytes = HashUtil.h256(accountNonce.toByteArray()); aionBytes[0] = (byte) 0xa0; // the Aion prefix - Address destAddr = new AionAddress(aionBytes); + Address destAddr = new Address(aionBytes); AionTransaction newTx = new AionTransaction( accountNonce.toByteArray(), diff --git a/modAionImpl/test/org/aion/zero/impl/ConsensusTest.java b/modAionImpl/test/org/aion/zero/impl/ConsensusTest.java index afa63a4770..719f6f7324 100644 --- a/modAionImpl/test/org/aion/zero/impl/ConsensusTest.java +++ b/modAionImpl/test/org/aion/zero/impl/ConsensusTest.java @@ -7,13 +7,13 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; import org.aion.mcf.core.ImportResult; import org.aion.util.bytes.ByteUtil; import org.aion.util.conversions.Hex; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.impl.types.AionBlock; import org.aion.zero.impl.types.AionBlockSummary; import org.aion.zero.types.AionTransaction; @@ -37,11 +37,11 @@ */ public class ConsensusTest { private static final Address CONTRACT = - AionAddress.wrap("a04272bb5f935fb170baf2998cb25dd15cc5794e7c5bac7241bec00c4971c7f8"); + Address.wrap("a04272bb5f935fb170baf2998cb25dd15cc5794e7c5bac7241bec00c4971c7f8"); private static final Address OWNER = - AionAddress.wrap("a05577af5a82aa86bb2f4247e3f809bd0d396d45ec3c4602d5824962d21b1679"); + Address.wrap("a05577af5a82aa86bb2f4247e3f809bd0d396d45ec3c4602d5824962d21b1679"); private static final Address OTHER = - AionAddress.wrap("a05577af5a82aa86bb2f4247e3f809bd0d396d45ec3c4602d5824962d21b1678"); + Address.wrap("a05577af5a82aa86bb2f4247e3f809bd0d396d45ec3c4602d5824962d21b1678"); private static final byte[] PRIVATE_KEY = Hex.decode( "81e071e5bf2c155f641641d88b5956af52c768fbb90968979b20858d65d71f32aa935b67ac46480caaefcdd56dd31862e578694a99083e9fad88cb6df89fc7cb"); diff --git a/modAionImpl/test/org/aion/zero/impl/GenesisSpecificationTest.java b/modAionImpl/test/org/aion/zero/impl/GenesisSpecificationTest.java index 9a02d2c6a0..28ca2d3c64 100644 --- a/modAionImpl/test/org/aion/zero/impl/GenesisSpecificationTest.java +++ b/modAionImpl/test/org/aion/zero/impl/GenesisSpecificationTest.java @@ -6,11 +6,11 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteUtil; +import org.aion.types.Address; import org.aion.crypto.HashUtil; import org.aion.mcf.core.AccountState; -import org.aion.vm.api.interfaces.Address; + +import org.aion.util.bytes.ByteUtil; import org.aion.zero.exceptions.HeaderStructureException; import org.junit.Test; @@ -72,17 +72,17 @@ public void overrideGenesisBlockTest() throws HeaderStructureException { AccountState defaultAccountState = new AccountState(overrideValue, overrideValue); HashSet<Address> accountStateSet = new HashSet<>(); - accountStateSet.add(AionAddress.wrap(overrideHash)); + accountStateSet.add(Address.wrap(overrideHash)); genesisBuilder .withParentHash(overrideHash) - .withCoinbase(AionAddress.wrap(overrideAddress)) + .withCoinbase(Address.wrap(overrideAddress)) .withDifficulty(overrideValue.toByteArray()) .withEnergyLimit(overrideValue.longValue()) .withNonce(overrideHash) .withNumber(overrideValue.longValue()) .withTimestamp(overrideValue.longValue()) - .addPreminedAccount(AionAddress.wrap(overrideAddress), defaultAccountState); + .addPreminedAccount(Address.wrap(overrideAddress), defaultAccountState); AionGenesis genesis = genesisBuilder.build(); diff --git a/modAionImpl/test/org/aion/zero/impl/GenesisTestNetJsonTest.java b/modAionImpl/test/org/aion/zero/impl/GenesisTestNetJsonTest.java index e498b758c4..db2a43097e 100644 --- a/modAionImpl/test/org/aion/zero/impl/GenesisTestNetJsonTest.java +++ b/modAionImpl/test/org/aion/zero/impl/GenesisTestNetJsonTest.java @@ -7,10 +7,9 @@ import java.io.IOException; import java.math.BigInteger; import java.util.Map; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteUtil; +import org.aion.types.Address; import org.aion.mcf.core.AccountState; -import org.aion.vm.api.interfaces.Address; +import org.aion.util.bytes.ByteUtil; import org.aion.zero.exceptions.HeaderStructureException; import org.junit.BeforeClass; import org.junit.Test; @@ -51,32 +50,32 @@ public void testNetJsonLoad() throws Exception { Address[] accString = new Address[] { - AionAddress.wrap( + Address.wrap( "0xa0483412e8c8e769df037231d336e96f7f6d843cf7224c3d8fbe0ec7cdc12ac6"), - AionAddress.wrap( + Address.wrap( "0xa0353561f8b6b5a8b56d535647a4ddd7278e80c2494e3314d1f0605470f56925"), - AionAddress.wrap( + Address.wrap( "0xa0274c1858ca50576f4d4d18b719787b76bb454c33749172758a620555bf4586"), - AionAddress.wrap( + Address.wrap( "0xa06691a968e8fe22dc79b6dd503d44cb96d9a523ae265b63c6752389be90185d"), - AionAddress.wrap( + Address.wrap( "0xa0a95b372efe55c77a75364407f0403dfefd3131519ca980b2d92b1d2d7297a7"), - AionAddress.wrap( + Address.wrap( "0xa07262e1d026fca027e5f4f38668303b74a2bba8bd07470fa39d0f3b03f882f1"), - AionAddress.wrap( + Address.wrap( "0xa08f92c80e34c95b8b2e8cb208d47e3e6048b7e0ea44d866e33e865c365b1417"), - AionAddress.wrap( + Address.wrap( "0xa022045f22772463c94e08d1fd32f7b251d4c9cfd1c92e039f1517b906776283"), - AionAddress.wrap( + Address.wrap( "0xa04c282f636feff4d6b35174fc2d8e05c23df4b1d59508712712627184dd8a93"), - AionAddress.wrap( + Address.wrap( "0xa0f8654c63ae53598cf42435f53b1ebd9b7df6cbceba10af235aa2393f03034c"), - AionAddress.wrap( + Address.wrap( "0xa0f3defb01b531c5a28680eb928e79ea18bc155f1060e1d923d660d74883518b") }; Address tokenAddress = - new AionAddress( + new Address( "0xa02198c9192bb89e9b2e8536d96cbf287fde80625afcf1a5e5632e23c1260d61"); for (Address a : accString) { @@ -96,7 +95,7 @@ public void testNetJsonLoad() throws Exception { assertThat(genesis.getDifficultyBI()).isEqualTo(BigInteger.valueOf(16)); assertThat(genesis.getCoinbase()) .isEqualTo( - new AionAddress( + new Address( "0x0000000000000000000000000000000000000000000000000000000000000000")); assertThat(genesis.getTimestamp()).isEqualTo(1497536993L); assertThat(genesis.getParentHash()) diff --git a/modAionImpl/test/org/aion/zero/impl/MockRepositoryConfig.java b/modAionImpl/test/org/aion/zero/impl/MockRepositoryConfig.java index a86cd506e7..0025a19c35 100644 --- a/modAionImpl/test/org/aion/zero/impl/MockRepositoryConfig.java +++ b/modAionImpl/test/org/aion/zero/impl/MockRepositoryConfig.java @@ -1,18 +1,18 @@ package org.aion.zero.impl; import java.util.Properties; -import org.aion.base.db.IContractDetails; -import org.aion.base.db.IPruneConfig; -import org.aion.base.db.IRepositoryConfig; import org.aion.db.impl.DBVendor; import org.aion.db.impl.DatabaseFactory; +import org.aion.interfaces.db.ContractDetails; +import org.aion.interfaces.db.PruneConfig; +import org.aion.interfaces.db.RepositoryConfig; import org.aion.mcf.config.CfgPrune; import org.aion.zero.impl.db.ContractDetailsAion; -public class MockRepositoryConfig implements IRepositoryConfig { +public class MockRepositoryConfig implements RepositoryConfig { private DBVendor vendor = DBVendor.MOCKDB; - private IPruneConfig pruneConfig = new CfgPrune(false); + private PruneConfig pruneConfig = new CfgPrune(false); @Override public String getDbPath() { @@ -20,12 +20,12 @@ public String getDbPath() { } @Override - public IPruneConfig getPruneConfig() { + public PruneConfig getPruneConfig() { return pruneConfig; } @Override - public IContractDetails contractDetailsImpl() { + public ContractDetails contractDetailsImpl() { return ContractDetailsAion.createForTesting(0, 1000000).getDetails(); } @@ -41,7 +41,7 @@ public MockRepositoryConfig(DBVendor vendor) { this.vendor = vendor; } - public MockRepositoryConfig(IPruneConfig _pruneConfig) { + public MockRepositoryConfig(PruneConfig _pruneConfig) { this.pruneConfig = _pruneConfig; } } diff --git a/modAionImpl/test/org/aion/zero/impl/PendingStateTest.java b/modAionImpl/test/org/aion/zero/impl/PendingStateTest.java index 2aaede7a99..d6f13165ee 100644 --- a/modAionImpl/test/org/aion/zero/impl/PendingStateTest.java +++ b/modAionImpl/test/org/aion/zero/impl/PendingStateTest.java @@ -3,10 +3,10 @@ import static org.junit.Assert.assertEquals; import java.math.BigInteger; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.mcf.blockchain.TxResponse; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.impl.config.CfgAion; import org.aion.zero.types.AionTransaction; import org.junit.Test; @@ -27,7 +27,7 @@ public void TestAddPendingTransactionSuccess() { AionHub hub = AionHub.createForTesting(CfgAion.inst(), bc, bc.getRepository()); - Address to = new AionAddress(bundle.privateKeys.get(0).getAddress()); + Address to = new Address(bundle.privateKeys.get(0).getAddress()); ECKey signer = bundle.privateKeys.get(1); // Successful transaction @@ -60,7 +60,7 @@ public void TestAddPendingTransactionInvalidNrgPrice() { AionHub hub = AionHub.createForTesting(CfgAion.inst(), bc, bc.getRepository()); - Address to = new AionAddress(bundle.privateKeys.get(0).getAddress()); + Address to = new Address(bundle.privateKeys.get(0).getAddress()); ECKey signer = bundle.privateKeys.get(1); // Invalid Nrg Price transaction diff --git a/modAionImpl/test/org/aion/zero/impl/StandaloneBlockchainTest.java b/modAionImpl/test/org/aion/zero/impl/StandaloneBlockchainTest.java index b2843d0a2a..3f31be47db 100644 --- a/modAionImpl/test/org/aion/zero/impl/StandaloneBlockchainTest.java +++ b/modAionImpl/test/org/aion/zero/impl/StandaloneBlockchainTest.java @@ -4,7 +4,7 @@ import java.math.BigInteger; import java.util.Collections; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.zero.impl.types.AionBlock; import org.junit.Test; @@ -21,7 +21,7 @@ public void testStandaloneBlockchainGenerateAccounts() { assertThat(bundle.privateKeys.size()).isEqualTo(10); for (ECKey k : bundle.privateKeys) { - assertThat(bundle.bc.getRepository().getBalance(new AionAddress(k.getAddress()))) + assertThat(bundle.bc.getRepository().getBalance(new Address(k.getAddress()))) .isGreaterThan(BigInteger.ZERO); } } diff --git a/modAionImpl/test/org/aion/zero/impl/blockchain/AionTxExecSummaryTest.java b/modAionImpl/test/org/aion/zero/impl/blockchain/AionTxExecSummaryTest.java index 96496f86ae..a07ee791df 100644 --- a/modAionImpl/test/org/aion/zero/impl/blockchain/AionTxExecSummaryTest.java +++ b/modAionImpl/test/org/aion/zero/impl/blockchain/AionTxExecSummaryTest.java @@ -4,10 +4,10 @@ import java.math.BigInteger; import java.util.Collections; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.crypto.HashUtil; import org.aion.mcf.vm.types.Bloom; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.types.AionTransaction; import org.aion.zero.types.AionTxExecSummary; import org.aion.zero.types.AionTxReceipt; @@ -17,7 +17,7 @@ public class AionTxExecSummaryTest { private Address defaultAddress = - AionAddress.wrap("CAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFE"); + Address.wrap("CAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFE"); @Test public void testRLPEncoding() { diff --git a/modAionImpl/test/org/aion/zero/impl/blockchain/PendingTxCacheTest.java b/modAionImpl/test/org/aion/zero/impl/blockchain/PendingTxCacheTest.java index d2d3ea5b24..874f72e11d 100644 --- a/modAionImpl/test/org/aion/zero/impl/blockchain/PendingTxCacheTest.java +++ b/modAionImpl/test/org/aion/zero/impl/blockchain/PendingTxCacheTest.java @@ -7,12 +7,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.aion.base.type.AionAddress; -import org.aion.base.type.ITransaction; -import org.aion.base.util.ByteUtil; + +import org.aion.interfaces.tx.Transaction; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; -import org.aion.vm.api.interfaces.Address; + +import org.aion.util.bytes.ByteUtil; import org.aion.zero.types.AionTransaction; import org.junit.Before; import org.junit.Test; @@ -44,8 +45,8 @@ private List<AionTransaction> getMockTransaction(int startNonce, int num, int ke AionTransaction tx = new AionTransaction( BigInteger.valueOf(i).toByteArray(), - AionAddress.wrap(key.get(keyIndex).getAddress()), - AionAddress.wrap( + Address.wrap(key.get(keyIndex).getAddress()), + Address.wrap( "0000000000000000000000000000000000000000000000000000000000000001"), ByteUtil.hexStringToBytes("1"), ByteUtil.hexStringToBytes("1"), @@ -74,8 +75,8 @@ private List<AionTransaction> getMockBigTransaction( AionTransaction tx = new AionTransaction( BigInteger.valueOf(i).toByteArray(), - AionAddress.wrap(key.get(keyIndex).getAddress()), - AionAddress.wrap( + Address.wrap(key.get(keyIndex).getAddress()), + Address.wrap( "0000000000000000000000000000000000000000000000000000000000000001"), ByteUtil.hexStringToBytes("1"), ByteUtil.hexStringToBytes(data), @@ -95,7 +96,7 @@ public void addCacheTxTest() { PendingTxCache cache = new PendingTxCache(1); List<AionTransaction> txn = getMockTransaction(0, 10, 0); - for (ITransaction tx : txn) { + for (Transaction tx : txn) { cache.addCacheTx((AionTransaction) tx); } @@ -109,7 +110,7 @@ public void addCacheTxTest2() { List<AionTransaction> txn = getMockTransaction(0, 10, 0); txn.addAll(getMockTransaction(0, 10, 1)); - for (ITransaction tx : txn) { + for (Transaction tx : txn) { cache.addCacheTx((AionTransaction) tx); } @@ -140,14 +141,14 @@ public void addCacheTxTest4() { List<AionTransaction> txn = getMockTransaction(0, 10, 0); txn.addAll(getMockTransaction(5, 10, 0)); - for (ITransaction tx : txn) { + for (Transaction tx : txn) { cache.addCacheTx((AionTransaction) tx); } assertTrue(cache.cacheTxSize() == 15); Map<BigInteger, AionTransaction> cacheMap = - cache.getCacheTx(AionAddress.wrap(key.get(0).getAddress())); + cache.getCacheTx(Address.wrap(key.get(0).getAddress())); assertTrue(cacheMap.size() == 15); } @@ -159,20 +160,20 @@ public void flushTest1() { List<AionTransaction> txn = getMockTransaction(0, 10, 0); List<AionTransaction> newCache; - for (ITransaction tx : txn) { + for (Transaction tx : txn) { cache.addCacheTx((AionTransaction) tx); } assertTrue(cache.cacheTxSize() == 10); Map<Address, BigInteger> map = new HashMap<>(); - map.put(AionAddress.wrap(key.get(0).getAddress()), BigInteger.TWO); + map.put(Address.wrap(key.get(0).getAddress()), BigInteger.TWO); newCache = cache.flush(map); assertTrue(newCache.size() == 1); Map<BigInteger, AionTransaction> cacheMap = - cache.getCacheTx(AionAddress.wrap(key.get(0).getAddress())); + cache.getCacheTx(Address.wrap(key.get(0).getAddress())); assertTrue(cacheMap.size() == 8); } @@ -183,18 +184,18 @@ public void flushTest2() { List<AionTransaction> txn = getMockTransaction(0, 10, 0); - for (ITransaction tx : txn) { + for (Transaction tx : txn) { cache.addCacheTx((AionTransaction) tx); } assertTrue(cache.cacheTxSize() == 10); Map<Address, BigInteger> map = new HashMap<>(); - map.put(AionAddress.wrap(key.get(1).getAddress()), BigInteger.TWO); + map.put(Address.wrap(key.get(1).getAddress()), BigInteger.TWO); cache.flush(map); Map<BigInteger, AionTransaction> cacheMap = - cache.getCacheTx(AionAddress.wrap(key.get(0).getAddress())); + cache.getCacheTx(Address.wrap(key.get(0).getAddress())); assertTrue(cacheMap.size() == 10); } @@ -208,7 +209,7 @@ public void flushTest3() { int singleTxSize = txn.get(0).getEncoded().length; int txSize = 0; - for (ITransaction tx : txn) { + for (Transaction tx : txn) { cache.addCacheTx((AionTransaction) tx); txSize += tx.getEncoded().length; } @@ -216,13 +217,13 @@ public void flushTest3() { assertTrue(cache.cacheTxSize() == 10); Map<Address, BigInteger> map = new HashMap<>(); - map.put(AionAddress.wrap(key.get(0).getAddress()), BigInteger.TWO); + map.put(Address.wrap(key.get(0).getAddress()), BigInteger.TWO); cache.flush(map); assertTrue(cache.cacheSize() == (txSize - (singleTxSize << 1))); Map<BigInteger, AionTransaction> cacheMap = - cache.getCacheTx(AionAddress.wrap(key.get(0).getAddress())); + cache.getCacheTx(Address.wrap(key.get(0).getAddress())); assertTrue(cacheMap.size() == 8); } @@ -237,7 +238,7 @@ public void flushTest4() { int singleTxSize = txn.get(0).getEncoded().length; int txSize = 0; - for (ITransaction tx : txn) { + for (Transaction tx : txn) { cache.addCacheTx((AionTransaction) tx); txSize += tx.getEncoded().length; } @@ -245,17 +246,17 @@ public void flushTest4() { assertTrue(cache.cacheTxSize() == 20); Map<Address, BigInteger> map = new HashMap<>(); - map.put(AionAddress.wrap(key.get(0).getAddress()), BigInteger.TWO); - map.put(AionAddress.wrap(key.get(1).getAddress()), BigInteger.TWO); + map.put(Address.wrap(key.get(0).getAddress()), BigInteger.TWO); + map.put(Address.wrap(key.get(1).getAddress()), BigInteger.TWO); cache.flush(map); assertTrue(cache.cacheSize() == (txSize - (singleTxSize << 2))); Map<BigInteger, AionTransaction> cacheMap = - cache.getCacheTx(AionAddress.wrap(key.get(0).getAddress())); + cache.getCacheTx(Address.wrap(key.get(0).getAddress())); assertTrue(cacheMap.size() == 8); - cacheMap = cache.getCacheTx(AionAddress.wrap(key.get(1).getAddress())); + cacheMap = cache.getCacheTx(Address.wrap(key.get(1).getAddress())); assertTrue(cacheMap.size() == 8); } @@ -268,24 +269,24 @@ public void flushTest5() { List<AionTransaction> txn = getMockTransaction(0, input, 0); txn.addAll(getMockTransaction(0, input, 1)); - for (ITransaction tx : txn) { + for (Transaction tx : txn) { cache.addCacheTx((AionTransaction) tx); } assertTrue(cache.cacheTxSize() == 20000); Map<Address, BigInteger> map = new HashMap<>(); - map.put(AionAddress.wrap(key.get(0).getAddress()), BigInteger.valueOf(input + 1)); - map.put(AionAddress.wrap(key.get(1).getAddress()), BigInteger.valueOf(input + 1)); + map.put(Address.wrap(key.get(0).getAddress()), BigInteger.valueOf(input + 1)); + map.put(Address.wrap(key.get(1).getAddress()), BigInteger.valueOf(input + 1)); cache.flush(map); assertTrue(cache.cacheSize() == 0); Map<BigInteger, AionTransaction> cacheMap = - cache.getCacheTx(AionAddress.wrap(key.get(0).getAddress())); + cache.getCacheTx(Address.wrap(key.get(0).getAddress())); assertTrue(cacheMap.size() == 0); - cacheMap = cache.getCacheTx(AionAddress.wrap(key.get(1).getAddress())); + cacheMap = cache.getCacheTx(Address.wrap(key.get(1).getAddress())); assertTrue(cacheMap.size() == 0); } @@ -295,12 +296,12 @@ public void maxPendingSizeTest1() { PendingTxCache cache = new PendingTxCache(1); List<AionTransaction> txn = getMockTransaction(0, 680, 0); - for (ITransaction tx : txn) { + for (Transaction tx : txn) { cache.addCacheTx((AionTransaction) tx); } Map<BigInteger, AionTransaction> cacheMap = - cache.getCacheTx(AionAddress.wrap(key.get(0).getAddress())); + cache.getCacheTx(Address.wrap(key.get(0).getAddress())); assertTrue(cacheMap.size() == 659); } @@ -310,7 +311,7 @@ public void maxPendingSizeTest2() { PendingTxCache cache = new PendingTxCache(1); List<AionTransaction> txn = getMockTransaction(0, 659, 0); - for (ITransaction tx : txn) { + for (Transaction tx : txn) { cache.addCacheTx((AionTransaction) tx); } @@ -321,7 +322,7 @@ public void maxPendingSizeTest2() { assertTrue(cache.cacheTxSize() == 659); Map<BigInteger, AionTransaction> cacheMap = - cache.getCacheTx(AionAddress.wrap(key.get(0).getAddress())); + cache.getCacheTx(Address.wrap(key.get(0).getAddress())); assertTrue(cacheMap.size() == 659); } @@ -342,7 +343,7 @@ public void maxPendingSizeTest3() { assertTrue(cache.cacheTxSize() == 658); Map<BigInteger, AionTransaction> cacheMap = - cache.getCacheTx(AionAddress.wrap(key.get(0).getAddress())); + cache.getCacheTx(Address.wrap(key.get(0).getAddress())); assertTrue(cacheMap.size() == 658); } @@ -363,7 +364,7 @@ public void maxPendingSizeTest4() { assertTrue(cache.cacheTxSize() == 652); Map<BigInteger, AionTransaction> cacheMap = - cache.getCacheTx(AionAddress.wrap(key.get(0).getAddress())); + cache.getCacheTx(Address.wrap(key.get(0).getAddress())); assertTrue(cacheMap.size() == 652); } @@ -381,7 +382,7 @@ public void maxPendingSizeTest5() { assertTrue(cache.cacheTxSize() == 659); Map<BigInteger, AionTransaction> cacheMap = - cache.getCacheTx(AionAddress.wrap(key.get(0).getAddress())); + cache.getCacheTx(Address.wrap(key.get(0).getAddress())); assertTrue(cache.cacheTxSize() == 659); AionTransaction tx = getMockBigTransaction(100, 1, 0, 2000).get(0); @@ -404,7 +405,7 @@ public void maxPendingSizeTest6() { assertTrue(cache.cacheTxSize() == 659); Map<BigInteger, AionTransaction> cacheMap = - cache.getCacheTx(AionAddress.wrap(key.get(0).getAddress())); + cache.getCacheTx(Address.wrap(key.get(0).getAddress())); assertTrue(cacheMap.size() == 659); AionTransaction tx = getMockBigTransaction(100, 1, 0, 199_500).get(0); @@ -426,7 +427,7 @@ public void maxPendingSizeTest7() { assertTrue(cache.cacheTxSize() == 659); Map<BigInteger, AionTransaction> cacheMap = - cache.getCacheTx(AionAddress.wrap(key.get(0).getAddress())); + cache.getCacheTx(Address.wrap(key.get(0).getAddress())); assertTrue(cacheMap.size() == 659); AionTransaction tx = getMockBigTransaction(100, 1, 0, 199_500).get(0); @@ -470,8 +471,8 @@ public void benchmark1() { System.out.println("flush starting"); Map<Address, BigInteger> flushMap = new HashMap<>(); - flushMap.put(AionAddress.wrap(key.get(0).getAddress()), BigInteger.valueOf(remove)); - flushMap.put(AionAddress.wrap(key.get(1).getAddress()), BigInteger.valueOf(remove)); + flushMap.put(Address.wrap(key.get(0).getAddress()), BigInteger.valueOf(remove)); + flushMap.put(Address.wrap(key.get(1).getAddress()), BigInteger.valueOf(remove)); t1 = System.currentTimeMillis(); cache.flush(flushMap); @@ -479,10 +480,10 @@ public void benchmark1() { System.out.println("flush took " + t2 + " ms"); Map<BigInteger, AionTransaction> cacheMap = - cache.getCacheTx(AionAddress.wrap(key.get(0).getAddress())); + cache.getCacheTx(Address.wrap(key.get(0).getAddress())); assertTrue(cacheMap.size() == input - remove); - cacheMap = cache.getCacheTx(AionAddress.wrap(key.get(1).getAddress())); + cacheMap = cache.getCacheTx(Address.wrap(key.get(1).getAddress())); assertTrue(cacheMap.size() == input - remove); } } diff --git a/modAionImpl/test/org/aion/zero/impl/cli/CliTest.java b/modAionImpl/test/org/aion/zero/impl/cli/CliTest.java index cddcdadeb6..dec15c8d98 100644 --- a/modAionImpl/test/org/aion/zero/impl/cli/CliTest.java +++ b/modAionImpl/test/org/aion/zero/impl/cli/CliTest.java @@ -26,11 +26,11 @@ import java.util.Set; import junitparams.JUnitParamsRunner; import junitparams.Parameters; -import org.aion.base.util.Hex; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; import org.aion.mcf.account.Keystore; import org.aion.mcf.config.Cfg; +import org.aion.util.conversions.Hex; import org.aion.zero.impl.cli.Cli.ReturnType; import org.aion.zero.impl.cli.Cli.TaskPriority; import org.aion.zero.impl.config.CfgAion; diff --git a/modAionImpl/test/org/aion/zero/impl/core/BloomFilterTest.java b/modAionImpl/test/org/aion/zero/impl/core/BloomFilterTest.java index af5d1b1b13..69ddb6f1c4 100644 --- a/modAionImpl/test/org/aion/zero/impl/core/BloomFilterTest.java +++ b/modAionImpl/test/org/aion/zero/impl/core/BloomFilterTest.java @@ -3,10 +3,10 @@ import static com.google.common.truth.Truth.assertThat; import java.math.BigInteger; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.crypto.HashUtil; import org.aion.mcf.vm.types.Bloom; -import org.aion.vm.api.interfaces.Address; + import org.junit.Test; /** @@ -26,7 +26,7 @@ public void testSimpleAddSearchBloom() { @Test public void testContainsAddress() { Address addr = - new AionAddress("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); + new Address("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); Bloom bloom = BloomFilter.create(addr.toBytes()); assertThat(BloomFilter.containsAddress(bloom, addr)).isTrue(); } @@ -49,7 +49,7 @@ public void testContainsEvent2() { @Test public void testCompositeBloomFiltering() { Address addr = - new AionAddress("BEEBEEBEEBEEBEEBEEBEEBEEBEEBEEBEEBEEBEEBEEBEEBEEBEEBEEBEEBEEFFFF"); + new Address("BEEBEEBEEBEEBEEBEEBEEBEEBEEBEEBEEBEEBEEBEEBEEBEEBEEBEEBEEBEEFFFF"); byte[] someEvent = HashUtil.h256(BigInteger.ONE.toByteArray()); byte[] anotherEvent = HashUtil.h256(BigInteger.TWO.toByteArray()); diff --git a/modAionImpl/test/org/aion/zero/impl/db/AionRepositoryImplTest.java b/modAionImpl/test/org/aion/zero/impl/db/AionRepositoryImplTest.java index 80e45a7549..45594d7d69 100644 --- a/modAionImpl/test/org/aion/zero/impl/db/AionRepositoryImplTest.java +++ b/modAionImpl/test/org/aion/zero/impl/db/AionRepositoryImplTest.java @@ -5,15 +5,10 @@ import java.math.BigInteger; import java.util.Optional; import java.util.Properties; -import org.aion.base.db.IByteArrayKeyValueDatabase; -import org.aion.base.db.IContractDetails; -import org.aion.base.db.IPruneConfig; -import org.aion.base.db.IRepository; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.db.IRepositoryConfig; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; +import org.aion.interfaces.db.*; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.crypto.HashUtil; import org.aion.db.impl.DBVendor; import org.aion.db.impl.DatabaseFactory; @@ -21,8 +16,8 @@ import org.aion.mcf.core.AccountState; import org.aion.mcf.db.IBlockStoreBase; import org.aion.mcf.trie.TrieNodeResult; -import org.aion.mcf.vm.types.DataWord; -import org.aion.vm.api.interfaces.Address; + +import org.aion.util.bytes.ByteUtil; import org.aion.zero.db.AionContractDetailsImpl; import org.aion.zero.impl.sync.DatabaseType; import org.junit.FixMethodOrder; @@ -32,20 +27,20 @@ @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class AionRepositoryImplTest { - protected IRepositoryConfig repoConfig = - new IRepositoryConfig() { + protected RepositoryConfig repoConfig = + new RepositoryConfig() { @Override public String getDbPath() { return ""; } @Override - public IPruneConfig getPruneConfig() { + public PruneConfig getPruneConfig() { return new CfgPrune(false); } @Override - public IContractDetails contractDetailsImpl() { + public ContractDetails contractDetailsImpl() { return ContractDetailsAion.createForTesting(0, 1000000).getDetails(); } @@ -70,9 +65,9 @@ public void testAccountStateUpdate() { AionRepositoryImpl repository = AionRepositoryImpl.createForTesting(repoConfig); byte[] originalRoot = repository.getRoot(); - Address defaultAccount = AionAddress.wrap(ByteUtil.hexStringToBytes(value1)); + Address defaultAccount = Address.wrap(ByteUtil.hexStringToBytes(value1)); - IRepositoryCache track = repository.startTracking(); + RepositoryCache track = repository.startTracking(); track.addBalance(defaultAccount, BigInteger.valueOf(1)); track.flush(); @@ -86,9 +81,9 @@ public void testAccountStateUpdate() { @Test public void testAccountAddCodeStorage() { AionRepositoryImpl repository = AionRepositoryImpl.createForTesting(repoConfig); - IRepositoryCache track = repository.startTracking(); + RepositoryCache track = repository.startTracking(); - Address defaultAccount = AionAddress.wrap(ByteUtil.hexStringToBytes(value1)); + Address defaultAccount = Address.wrap(ByteUtil.hexStringToBytes(value1)); track.addBalance(defaultAccount, BigInteger.valueOf(1)); byte[] originalRoot = repository.getRoot(); @@ -107,9 +102,9 @@ public void testAccountAddCodeStorage() { @Test public void testAccountStateUpdateStorageRow() { AionRepositoryImpl repository = AionRepositoryImpl.createForTesting(repoConfig); - IRepositoryCache track = repository.startTracking(); + RepositoryCache track = repository.startTracking(); - Address defaultAccount = AionAddress.wrap(ByteUtil.hexStringToBytes(value1)); + Address defaultAccount = Address.wrap(ByteUtil.hexStringToBytes(value1)); track.addBalance(defaultAccount, BigInteger.valueOf(1)); // Consider the original root the one after an account has been added @@ -117,13 +112,13 @@ public void testAccountStateUpdateStorageRow() { byte[] key = HashUtil.blake128("hello".getBytes()); byte[] value = HashUtil.blake128("world".getBytes()); track.addStorageRow( - defaultAccount, new DataWord(key).toWrapper(), new DataWord(value).toWrapper()); + defaultAccount, new DataWordImpl(key).toWrapper(), new DataWordImpl(value).toWrapper()); track.flush(); byte[] retrievedValue = repository - .getStorageValue(defaultAccount, new DataWord(key).toWrapper()) + .getStorageValue(defaultAccount, new DataWordImpl(key).toWrapper()) .getNoLeadZeroesData(); assertThat(retrievedValue).isEqualTo(value); @@ -136,9 +131,9 @@ public void testAccountStateUpdateStorageRow() { @Test public void testAccountStateUpdateStorageRowFlush() { AionRepositoryImpl repository = AionRepositoryImpl.createForTesting(repoConfig); - IRepositoryCache track = repository.startTracking(); + RepositoryCache track = repository.startTracking(); - Address defaultAccount = AionAddress.wrap(ByteUtil.hexStringToBytes(value1)); + Address defaultAccount = Address.wrap(ByteUtil.hexStringToBytes(value1)); track.addBalance(defaultAccount, BigInteger.valueOf(1)); // Consider the original root the one after an account has been added @@ -146,7 +141,7 @@ public void testAccountStateUpdateStorageRowFlush() { byte[] key = HashUtil.blake128("hello".getBytes()); byte[] value = HashUtil.blake128("world".getBytes()); track.addStorageRow( - defaultAccount, new DataWord(key).toWrapper(), new DataWord(value).toWrapper()); + defaultAccount, new DataWordImpl(key).toWrapper(), new DataWordImpl(value).toWrapper()); // does not call parent's flush track.flush(); @@ -154,15 +149,15 @@ public void testAccountStateUpdateStorageRowFlush() { repository.flush(); /** Verify that the account has been flushed */ - IByteArrayKeyValueDatabase detailsDB = repository.getDetailsDatabase(); + ByteArrayKeyValueDatabase detailsDB = repository.getDetailsDatabase(); Optional<byte[]> serializedDetails = detailsDB.get(defaultAccount.toBytes()); assertThat(serializedDetails.isPresent()).isEqualTo(true); AionContractDetailsImpl details = new AionContractDetailsImpl(0, 1000000); details.decode(serializedDetails.get()); - assertThat(details.get(new DataWord(key).toWrapper())) - .isEqualTo(new DataWord(value).toWrapper()); + assertThat(details.get(new DataWordImpl(key).toWrapper())) + .isEqualTo(new DataWordImpl(value).toWrapper()); } /** Repo track test suite */ @@ -174,9 +169,9 @@ public void testAccountStateUpdateStorageRowFlush() { @Test public void testRepoTrackUpdateStorageRow() { final AionRepositoryImpl repository = AionRepositoryImpl.createForTesting(repoConfig); - final IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> repoTrack = + final RepositoryCache<AccountState, IBlockStoreBase<?, ?>> repoTrack = repository.startTracking(); - final Address defaultAccount = AionAddress.wrap(ByteUtil.hexStringToBytes(value1)); + final Address defaultAccount = Address.wrap(ByteUtil.hexStringToBytes(value1)); final byte[] key = HashUtil.blake128("hello".getBytes()); final byte[] value = HashUtil.blake128("world".getBytes()); @@ -185,16 +180,16 @@ public void testRepoTrackUpdateStorageRow() { final byte[] originalRoot = repository.getRoot(); repoTrack.addStorageRow( - defaultAccount, new DataWord(key).toWrapper(), new DataWord(value).toWrapper()); + defaultAccount, new DataWordImpl(key).toWrapper(), new DataWordImpl(value).toWrapper()); ByteArrayWrapper retrievedStorageValue = - repoTrack.getStorageValue(defaultAccount, new DataWord(key).toWrapper()); - assertThat(retrievedStorageValue).isEqualTo(new DataWord(value).toWrapper()); + repoTrack.getStorageValue(defaultAccount, new DataWordImpl(key).toWrapper()); + assertThat(retrievedStorageValue).isEqualTo(new DataWordImpl(value).toWrapper()); // commit changes, then check that the root has updated repoTrack.flush(); - assertThat(repository.getStorageValue(defaultAccount, new DataWord(key).toWrapper())) + assertThat(repository.getStorageValue(defaultAccount, new DataWordImpl(key).toWrapper())) .isEqualTo(retrievedStorageValue); final byte[] newRoot = repository.getRoot(); @@ -203,14 +198,14 @@ public void testRepoTrackUpdateStorageRow() { @Test public void testSyncToPreviousRootNoFlush() { - final Address FIRST_ACC = AionAddress.wrap(value2); - final Address SECOND_ACC = AionAddress.wrap(value3); + final Address FIRST_ACC = Address.wrap(value2); + final Address SECOND_ACC = Address.wrap(value3); final AionRepositoryImpl repository = AionRepositoryImpl.createForTesting(repoConfig); byte[] originalRoot = repository.getRoot(); // now create a new account - IRepositoryCache track = repository.startTracking(); + RepositoryCache track = repository.startTracking(); track.addBalance(FIRST_ACC, BigInteger.ONE); track.flush(); @@ -249,11 +244,11 @@ public void testSyncToPreviousRootNoFlush() { @Test public void testSyncToPreviousRootWithFlush() { - final Address FIRST_ACC = AionAddress.wrap(value2); + final Address FIRST_ACC = Address.wrap(value2); AionRepositoryImpl repository = AionRepositoryImpl.createForTesting(repoConfig); byte[] originalRoot = repository.getRoot(); - IRepositoryCache track = repository.startTracking(); + RepositoryCache track = repository.startTracking(); track.addBalance(FIRST_ACC, BigInteger.ONE); track.flush(); byte[] newRoot = repository.getRoot(); @@ -287,11 +282,11 @@ public void testSyncToPreviousRootWithFlush() { public void test17NodePreviousRootTest() { // not that it matters since things are going to be hashed, but at least // the root node should point to a node that contains references to both - final Address DOG_ACC = AionAddress.wrap("00000000000000000000000000000dog".getBytes()); - final Address DOGE_ACC = AionAddress.wrap("0000000000000000000000000000doge".getBytes()); + final Address DOG_ACC = Address.wrap("00000000000000000000000000000dog".getBytes()); + final Address DOGE_ACC = Address.wrap("0000000000000000000000000000doge".getBytes()); AionRepositoryImpl repository = AionRepositoryImpl.createForTesting(repoConfig); - IRepositoryCache track = repository.startTracking(); + RepositoryCache track = repository.startTracking(); track.addBalance(DOG_ACC, BigInteger.ONE); track.addBalance(DOGE_ACC, BigInteger.ONE); track.flush(); @@ -319,10 +314,10 @@ public void testGetSnapshotToRoot() { AionRepositoryImpl repository = AionRepositoryImpl.createForTesting(repoConfig); // make some changes to the repository - final Address account1 = AionAddress.wrap(value1); - final Address account2 = AionAddress.wrap(value2); - final Address account3 = AionAddress.wrap(value3); - IRepositoryCache track = repository.startTracking(); + final Address account1 = Address.wrap(value1); + final Address account2 = Address.wrap(value2); + final Address account3 = Address.wrap(value3); + RepositoryCache track = repository.startTracking(); track.addBalance(account1, BigInteger.ONE); track.addBalance(account2, BigInteger.TWO); track.addBalance(account3, BigInteger.TEN); @@ -330,7 +325,7 @@ public void testGetSnapshotToRoot() { repository.flush(); // get snapshot to root - IRepository snapshot = repository.getSnapshotTo(repository.getRoot()); + Repository snapshot = repository.getSnapshotTo(repository.getRoot()); // check that the same values are retrieved assertThat(repository.getBalance(account1)).isEqualTo(snapshot.getBalance(account1)); @@ -377,7 +372,7 @@ public void testGetSnapshotToRoot() { @Test public void testImportTrieNode() { AionRepositoryImpl repository = AionRepositoryImpl.createForTesting(repoConfig); - IByteArrayKeyValueDatabase db = repository.getStateDatabase(); + ByteArrayKeyValueDatabase db = repository.getStateDatabase(); byte[] nodeKey = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, diff --git a/modAionImpl/test/org/aion/zero/impl/db/BlockInfoTest.java b/modAionImpl/test/org/aion/zero/impl/db/BlockInfoTest.java index 3a91a98b14..0781331dcc 100644 --- a/modAionImpl/test/org/aion/zero/impl/db/BlockInfoTest.java +++ b/modAionImpl/test/org/aion/zero/impl/db/BlockInfoTest.java @@ -6,9 +6,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; +import org.aion.types.ByteArrayWrapper; import org.aion.crypto.HashUtil; +import org.aion.util.bytes.ByteUtil; import org.junit.Test; /** Test suite for {@link org.aion.zero.impl.db.AionBlockStore.BlockInfo} serialization */ diff --git a/modAionImpl/test/org/aion/zero/impl/db/FlushCopiesTest.java b/modAionImpl/test/org/aion/zero/impl/db/FlushCopiesTest.java index cac69c9444..850aabe2d1 100644 --- a/modAionImpl/test/org/aion/zero/impl/db/FlushCopiesTest.java +++ b/modAionImpl/test/org/aion/zero/impl/db/FlushCopiesTest.java @@ -5,14 +5,14 @@ import static org.junit.Assert.assertNotSame; import java.math.BigInteger; -import org.aion.base.db.IContractDetails; -import org.aion.base.db.IRepository; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.interfaces.db.ContractDetails; +import org.aion.interfaces.db.Repository; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.mcf.core.AccountState; -import org.aion.mcf.vm.types.DataWord; +import org.aion.mcf.vm.types.DataWordImpl; import org.aion.mcf.vm.types.DoubleDataWord; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.db.AionRepositoryCache; import org.aion.zero.impl.StandaloneBlockchain; import org.apache.commons.lang3.RandomUtils; @@ -21,11 +21,11 @@ import org.junit.Test; /** - * Tests the {@link org.aion.zero.db.AionRepositoryCache#flushCopiesTo(IRepository, boolean)} + * Tests the {@link org.aion.zero.db.AionRepositoryCache#flushCopiesTo(Repository, boolean)} * method. */ public class FlushCopiesTest { - private IRepository repository; + private Repository repository; @Before public void setup() { @@ -45,7 +45,7 @@ public void tearDown() { public void testAccountStateObjectReference() { AionRepositoryCache repositoryChild = (AionRepositoryCache) this.repository.startTracking(); - Address account = randomAionAddress(); + Address account = randomAddress(); BigInteger nonce = BigInteger.TEN; BigInteger balance = BigInteger.valueOf(11223344); byte[] code = new byte[100]; @@ -79,7 +79,7 @@ public void testAccountStateObjectReference() { public void testContractDetailsObjectReference() { AionRepositoryCache repositoryChild = (AionRepositoryCache) this.repository.startTracking(); - Address account = randomAionAddress(); + Address account = randomAddress(); BigInteger nonce = BigInteger.TEN; BigInteger balance = BigInteger.valueOf(11223344); byte[] code = new byte[100]; @@ -88,7 +88,7 @@ public void testContractDetailsObjectReference() { // Create a new account state in the child, flush to the parent without clearing child // state. - ByteArrayWrapper key = new DataWord(5).toWrapper(); + ByteArrayWrapper key = new DataWordImpl(5).toWrapper(); ByteArrayWrapper value = new DoubleDataWord(13429765314L).toWrapper(); repositoryChild.createAccount(account); @@ -99,8 +99,8 @@ public void testContractDetailsObjectReference() { repositoryChild.flushCopiesTo(this.repository, false); // Compare object references. - IContractDetails detailsInChild = repositoryChild.getContractDetails(account); - IContractDetails detailsInParent = this.repository.getContractDetails(account); + ContractDetails detailsInChild = repositoryChild.getContractDetails(account); + ContractDetails detailsInParent = this.repository.getContractDetails(account); // These references must be different. assertNotSame(detailsInChild, detailsInParent); @@ -115,7 +115,7 @@ public void testContractDetailsObjectReference() { public void testSiblingStateModificationsAreIndependentOfOneAnother() { AionRepositoryCache firstChild = (AionRepositoryCache) this.repository.startTracking(); - Address account = randomAionAddress(); + Address account = randomAddress(); BigInteger firstNonce = BigInteger.TEN; BigInteger firstBalance = BigInteger.valueOf(11223344); byte[] code = new byte[100]; @@ -126,7 +126,7 @@ public void testSiblingStateModificationsAreIndependentOfOneAnother() { // Create a new account state in the child, flush to the parent without clearing child // state. - ByteArrayWrapper firstKey = new DataWord(5).toWrapper(); + ByteArrayWrapper firstKey = new DataWordImpl(5).toWrapper(); ByteArrayWrapper firstValue = new DoubleDataWord(13429765314L).toWrapper(); firstChild.createAccount(account); @@ -143,7 +143,7 @@ public void testSiblingStateModificationsAreIndependentOfOneAnother() { BigInteger secondNonce = firstNonce.multiply(BigInteger.TWO); ByteArrayWrapper secondKey = new DoubleDataWord(289356).toWrapper(); - ByteArrayWrapper secondValue = new DataWord(23674).toWrapper(); + ByteArrayWrapper secondValue = new DataWordImpl(23674).toWrapper(); secondChild.setNonce(account, secondNonce); secondChild.addBalance(account, firstBalance); @@ -156,9 +156,9 @@ public void testSiblingStateModificationsAreIndependentOfOneAnother() { assertArrayEquals(firstStorageHash, firstChild.getContractDetails(account).getStorageHash()); } - private Address randomAionAddress() { + private Address randomAddress() { byte[] bytes = RandomUtils.nextBytes(Address.SIZE); bytes[0] = (byte) 0xa0; - return new AionAddress(bytes); + return new Address(bytes); } } diff --git a/modAionImpl/test/org/aion/zero/impl/db/PendingBlockStoreTest.java b/modAionImpl/test/org/aion/zero/impl/db/PendingBlockStoreTest.java index 860c088bbd..4873c1d636 100644 --- a/modAionImpl/test/org/aion/zero/impl/db/PendingBlockStoreTest.java +++ b/modAionImpl/test/org/aion/zero/impl/db/PendingBlockStoreTest.java @@ -11,7 +11,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; import org.aion.db.impl.DBVendor; import org.aion.db.impl.DatabaseFactory.Props; import org.aion.log.AionLoggerFactory; diff --git a/modAionImpl/test/org/aion/zero/impl/sync/TaskImportBlocksTest.java b/modAionImpl/test/org/aion/zero/impl/sync/TaskImportBlocksTest.java index 686d848fb8..7c69ce3275 100644 --- a/modAionImpl/test/org/aion/zero/impl/sync/TaskImportBlocksTest.java +++ b/modAionImpl/test/org/aion/zero/impl/sync/TaskImportBlocksTest.java @@ -39,10 +39,10 @@ import java.util.stream.Collectors; import junitparams.JUnitParamsRunner; import junitparams.Parameters; -import org.aion.base.db.IContractDetails; -import org.aion.base.db.IPruneConfig; -import org.aion.base.db.IRepositoryConfig; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.interfaces.db.ContractDetails; +import org.aion.interfaces.db.PruneConfig; +import org.aion.interfaces.db.RepositoryConfig; +import org.aion.types.ByteArrayWrapper; import org.aion.crypto.ECKey; import org.aion.db.impl.DBVendor; import org.aion.db.impl.DatabaseFactory; @@ -231,16 +231,16 @@ public void testFilterBatch_wPruningRestrictions() { builder.withValidatorConfiguration("simple") .withDefaultAccounts(accounts) .withRepoConfig( - new IRepositoryConfig() { + new RepositoryConfig() { @Override public String getDbPath() { return ""; } @Override - public IPruneConfig getPruneConfig() { + public PruneConfig getPruneConfig() { // top pruning without archiving - return new IPruneConfig() { + return new PruneConfig() { @Override public boolean isEnabled() { return true; @@ -264,7 +264,7 @@ public int getArchiveRate() { } @Override - public IContractDetails contractDetailsImpl() { + public ContractDetails contractDetailsImpl() { return ContractDetailsAion.createForTesting(0, 1000000) .getDetails(); } diff --git a/modAionImpl/test/org/aion/zero/impl/sync/msg/ResponseTrieDataTest.java b/modAionImpl/test/org/aion/zero/impl/sync/msg/ResponseTrieDataTest.java index d575b1e54c..06e9e4385a 100644 --- a/modAionImpl/test/org/aion/zero/impl/sync/msg/ResponseTrieDataTest.java +++ b/modAionImpl/test/org/aion/zero/impl/sync/msg/ResponseTrieDataTest.java @@ -17,7 +17,7 @@ import java.util.Map; import junitparams.JUnitParamsRunner; import junitparams.Parameters; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; import org.aion.rlp.RLP; import org.aion.zero.impl.sync.DatabaseType; import org.junit.Test; diff --git a/modAionImpl/test/org/aion/zero/impl/types/A0BlockHeaderTest.java b/modAionImpl/test/org/aion/zero/impl/types/A0BlockHeaderTest.java index a3230a5628..e94bb98e65 100644 --- a/modAionImpl/test/org/aion/zero/impl/types/A0BlockHeaderTest.java +++ b/modAionImpl/test/org/aion/zero/impl/types/A0BlockHeaderTest.java @@ -2,9 +2,9 @@ import static com.google.common.truth.Truth.assertThat; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteUtil; +import org.aion.types.Address; import org.aion.crypto.HashUtil; +import org.aion.util.bytes.ByteUtil; import org.aion.zero.exceptions.HeaderStructureException; import org.aion.zero.types.A0BlockHeader; import org.junit.Test; @@ -36,7 +36,7 @@ public void testBlockHeaderFromSafeBuilder() throws Exception { A0BlockHeader.Builder builder = new A0BlockHeader.Builder(); // partial build - builder.withCoinbase(AionAddress.wrap(COINBASE)) + builder.withCoinbase(Address.wrap(COINBASE)) .withStateRoot(STATE_ROOT) .withTxTrieRoot(TRIE_ROOT) .withExtraData(EXTRA_DATA) @@ -73,7 +73,7 @@ public void testBlockHeaderFromUnsafeSource() throws Exception { // partial build builder.fromUnsafeSource() .withStateRoot(STATE_ROOT) - .withCoinbase(AionAddress.wrap(COINBASE)) + .withCoinbase(Address.wrap(COINBASE)) .withTxTrieRoot(TRIE_ROOT) .withExtraData(EXTRA_DATA) .withReceiptTrieRoot(RECEIPT_ROOT) @@ -109,7 +109,7 @@ public void testBlockHeaderFromRLP() throws Exception { A0BlockHeader.Builder builder = new A0BlockHeader.Builder(); builder.fromUnsafeSource() - .withCoinbase(AionAddress.wrap(COINBASE)) + .withCoinbase(Address.wrap(COINBASE)) .withTxTrieRoot(TRIE_ROOT) .withExtraData(EXTRA_DATA) .withReceiptTrieRoot(RECEIPT_ROOT) diff --git a/modAionImpl/test/org/aion/zero/impl/valid/AionPOWRuleTest.java b/modAionImpl/test/org/aion/zero/impl/valid/AionPOWRuleTest.java index b4db90afdd..b0109b1f32 100644 --- a/modAionImpl/test/org/aion/zero/impl/valid/AionPOWRuleTest.java +++ b/modAionImpl/test/org/aion/zero/impl/valid/AionPOWRuleTest.java @@ -6,8 +6,8 @@ import java.math.BigInteger; import java.util.ArrayList; import java.util.List; -import org.aion.base.util.ByteUtil; import org.aion.mcf.blockchain.valid.IValidRule; +import org.aion.util.bytes.ByteUtil; import org.aion.zero.types.A0BlockHeader; import org.junit.Before; import org.junit.Test; diff --git a/modAionImpl/test/org/aion/zero/impl/valid/EquihashSolutionRuleTest.java b/modAionImpl/test/org/aion/zero/impl/valid/EquihashSolutionRuleTest.java index 203a7ad0fb..58d1346229 100644 --- a/modAionImpl/test/org/aion/zero/impl/valid/EquihashSolutionRuleTest.java +++ b/modAionImpl/test/org/aion/zero/impl/valid/EquihashSolutionRuleTest.java @@ -1,6 +1,6 @@ // package org.aion.zero.impl.valid; // -// import org.aion.base.util.ByteArrayWrapper; +// import org.aion.types.ByteArrayWrapper; // import org.aion.equihash.EquiUtils; // import org.aion.equihash.EquiValidator; // import org.aion.equihash.Equihash; diff --git a/modAionImpl/test/org/aion/zero/impl/vm/AvmBulkTransactionTest.java b/modAionImpl/test/org/aion/zero/impl/vm/AvmBulkTransactionTest.java index b569486011..c06a828480 100644 --- a/modAionImpl/test/org/aion/zero/impl/vm/AvmBulkTransactionTest.java +++ b/modAionImpl/test/org/aion/zero/impl/vm/AvmBulkTransactionTest.java @@ -11,13 +11,13 @@ import org.aion.avm.api.ABIEncoder; import org.aion.avm.core.dappreading.JarBuilder; import org.aion.avm.core.util.CodeAndArguments; -import org.aion.base.type.AionAddress; -import org.aion.base.vm.VirtualMachineSpecs; +import org.aion.interfaces.vm.VirtualMachineSpecs; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; import org.aion.mcf.core.ImportResult; import org.aion.vm.VirtualMachineProvider; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.impl.StandaloneBlockchain; import org.aion.zero.impl.vm.contracts.Statefulness; import org.aion.zero.impl.types.AionBlock; @@ -133,7 +133,7 @@ public void sendContractCreationAndCallTransactionsInBulkTest() { // Grab the address of the newly deployed contract. Address deployedContract = - AionAddress.wrap(initialSummary.getReceipts().get(0).getTransactionOutput()); + Address.wrap(initialSummary.getReceipts().get(0).getTransactionOutput()); int numAvmCreateTransactions = 10; int numAvmCallTransactions = 10; @@ -178,7 +178,7 @@ public void sendContractCreationAndCallTransactionsInBulkTest() { // The first batch are creates, so grab the new contract addresses. if (i < numAvmCreateTransactions) { contracts.add( - AionAddress.wrap( + Address.wrap( blockSummary .getSummaries() .get(i) @@ -210,7 +210,7 @@ private AionTransaction makeAvmContractCreateTransaction(ECKey sender, BigIntege AionTransaction transaction = newTransaction( nonce, - AionAddress.wrap(sender.getAddress()), + Address.wrap(sender.getAddress()), null, BigInteger.ZERO, jar, @@ -226,7 +226,7 @@ private AionTransaction makeAvmContractCallTransaction( AionTransaction transaction = newTransaction( nonce, - AionAddress.wrap(sender.getAddress()), + Address.wrap(sender.getAddress()), contract, BigInteger.ZERO, abiEncodeMethodCall("incrementCounter"), @@ -239,13 +239,13 @@ private AionTransaction makeAvmContractCallTransaction( private AionTransaction makeValueTransferTransaction( ECKey sender, ECKey beneficiary, BigInteger value, BigInteger nonce) { - Address senderAddress = AionAddress.wrap(sender.getAddress()); + Address senderAddress = Address.wrap(sender.getAddress()); AionTransaction transaction = newTransaction( nonce, senderAddress, - AionAddress.wrap(beneficiary.getAddress()), + Address.wrap(beneficiary.getAddress()), value, new byte[0], 2_000_000, @@ -257,7 +257,7 @@ private AionTransaction makeValueTransferTransaction( private int getDeployedStatefulnessCountValue( ECKey sender, BigInteger nonce, Address contract) { - Address senderAddress = AionAddress.wrap(sender.getAddress()); + Address senderAddress = Address.wrap(sender.getAddress()); AionTransaction transaction = newTransaction( @@ -314,7 +314,7 @@ private BigInteger getNonce(Address address) { } private BigInteger getNonce(ECKey address) { - return getNonce(AionAddress.wrap(address.getAddress())); + return getNonce(Address.wrap(address.getAddress())); } private BigInteger getBalance(Address address) { @@ -322,7 +322,7 @@ private BigInteger getBalance(Address address) { } private BigInteger getBalance(ECKey address) { - return getBalance(AionAddress.wrap(address.getAddress())); + return getBalance(Address.wrap(address.getAddress())); } private ECKey getRandomAccount() { diff --git a/modAionImpl/test/org/aion/zero/impl/vm/AvmHelloWorldTest.java b/modAionImpl/test/org/aion/zero/impl/vm/AvmHelloWorldTest.java index cad8d2b896..558df4541e 100644 --- a/modAionImpl/test/org/aion/zero/impl/vm/AvmHelloWorldTest.java +++ b/modAionImpl/test/org/aion/zero/impl/vm/AvmHelloWorldTest.java @@ -9,12 +9,12 @@ import org.aion.avm.core.NodeEnvironment; import org.aion.avm.core.dappreading.JarBuilder; import org.aion.avm.core.util.CodeAndArguments; -import org.aion.base.type.AionAddress; -import org.aion.base.vm.VirtualMachineSpecs; +import org.aion.interfaces.vm.VirtualMachineSpecs; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.mcf.core.ImportResult; import org.aion.vm.VirtualMachineProvider; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.impl.StandaloneBlockchain; import org.aion.zero.impl.vm.contracts.AvmHelloWorld; import org.aion.zero.impl.types.AionBlock; @@ -64,7 +64,7 @@ public void testDeployContract() { byte[] jar = getJarBytes(); AionTransaction transaction = newTransaction( BigInteger.ZERO, - AionAddress.wrap(deployerKey.getAddress()), + Address.wrap(deployerKey.getAddress()), null, jar, 5_000_000); @@ -86,7 +86,7 @@ public void testDeployAndCallContract() { byte[] jar = getJarBytes(); AionTransaction transaction = newTransaction( BigInteger.ZERO, - AionAddress.wrap(deployerKey.getAddress()), + Address.wrap(deployerKey.getAddress()), null, jar, 5_000_000); @@ -101,11 +101,11 @@ public void testDeployAndCallContract() { assertEquals(NodeEnvironment.CONTRACT_PREFIX, receipt.getTransactionOutput()[0]); assertTrue(receipt.isSuccessful()); - Address contract = AionAddress.wrap(receipt.getTransactionOutput()); + Address contract = Address.wrap(receipt.getTransactionOutput()); byte[] call = getCallArguments(); transaction = newTransaction( BigInteger.ONE, - AionAddress.wrap(deployerKey.getAddress()), + Address.wrap(deployerKey.getAddress()), contract, call, 2_000_000); diff --git a/modAionImpl/test/org/aion/zero/impl/vm/Benchmark.java b/modAionImpl/test/org/aion/zero/impl/vm/Benchmark.java index 3028fba0b7..b1e78c5c7d 100644 --- a/modAionImpl/test/org/aion/zero/impl/vm/Benchmark.java +++ b/modAionImpl/test/org/aion/zero/impl/vm/Benchmark.java @@ -32,10 +32,9 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.Hex; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; import org.aion.crypto.ECKeyFac.ECKeyType; @@ -44,11 +43,12 @@ import org.aion.log.LogEnum; import org.aion.mcf.core.AccountState; import org.aion.mcf.db.IBlockStoreBase; -import org.aion.mcf.vm.types.DataWord; +import org.aion.util.bytes.ByteUtil; +import org.aion.util.conversions.Hex; import org.aion.vm.BulkExecutor; import org.aion.vm.ExecutionBatch; import org.aion.vm.PostExecutionWork; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.impl.db.AionRepositoryImpl; import org.aion.zero.impl.types.AionBlock; import org.aion.zero.impl.vm.contracts.ContractUtils; @@ -62,7 +62,7 @@ public class Benchmark { private static AionBlock block = createDummyBlock(); private static AionRepositoryImpl db = AionRepositoryImpl.inst(); - private static IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> repo = db.startTracking(); + private static RepositoryCache<AccountState, IBlockStoreBase<?, ?>> repo = db.startTracking(); private static ECKey key; private static Address owner; @@ -84,17 +84,17 @@ private static void prepare() throws IOException { // create owner account ECKeyFac.setType(ECKeyType.ED25519); key = ECKeyFac.inst().create(); - owner = AionAddress.wrap(key.getAddress()); + owner = Address.wrap(key.getAddress()); repo.createAccount(owner); repo.addBalance(owner, BigInteger.valueOf(1_000_000_000L)); // create transaction byte[] deployer = ContractUtils.getContractDeployer("BenchmarkERC20.sol", "FixedSupplyToken"); - byte[] nonce = DataWord.ZERO.getData(); + byte[] nonce = DataWordImpl.ZERO.getData(); Address from = owner; Address to = null; - byte[] value = DataWord.ZERO.getData(); + byte[] value = DataWordImpl.ZERO.getData(); long nrg = 1_000_000L; long nrgPrice = 1L; AionTransaction tx = new AionTransaction(nonce, from, to, value, deployer, nrg, nrgPrice); @@ -131,15 +131,15 @@ private static List<AionTransaction> signTransactions(int num) { recipients.add(recipient); // transfer token to random people - byte[] nonce = new DataWord(ownerNonce + i).getData(); + byte[] nonce = new DataWordImpl(ownerNonce + i).getData(); Address from = owner; Address to = contract; - byte[] value = DataWord.ZERO.getData(); + byte[] value = DataWordImpl.ZERO.getData(); byte[] data = ByteUtil.merge( Hex.decode("fbb001d6" + "000000000000000000000000"), recipient, - DataWord.ONE.getData()); + DataWordImpl.ONE.getData()); long nrg = 1_000_000L; long nrgPrice = 1L; AionTransaction tx = new AionTransaction(nonce, from, to, value, data, nrg, nrgPrice); @@ -220,10 +220,10 @@ private static void verifyState(int num) { long ownerNonce = repo.getNonce(owner).longValue(); for (int i = 0; i < recipients.size(); i++) { - byte[] nonce = new DataWord(ownerNonce + i).getData(); + byte[] nonce = new DataWordImpl(ownerNonce + i).getData(); Address from = owner; Address to = contract; - byte[] value = DataWord.ZERO.getData(); + byte[] value = DataWordImpl.ZERO.getData(); byte[] data = ByteUtil.merge( Hex.decode("70a08231" + "000000000000000000000000"), recipients.get(i)); @@ -244,7 +244,7 @@ private static void verifyState(int num) { AionTxExecSummary summary = exec.execute().get(0); assertFalse(summary.isFailed()); - assertEquals(1, new DataWord(summary.getReceipt().getTransactionOutput()).longValue()); + assertEquals(1, new DataWordImpl(summary.getReceipt().getTransactionOutput()).longValue()); } } @@ -271,7 +271,7 @@ private static AionBlock createDummyBlock() { byte[] parentHash = new byte[32]; byte[] coinbase = RandomUtils.nextBytes(Address.SIZE); byte[] logsBloom = new byte[0]; - byte[] difficulty = new DataWord(0x1000000L).getData(); + byte[] difficulty = new DataWordImpl(0x1000000L).getData(); long number = 1; long timestamp = System.currentTimeMillis() / 1000; byte[] extraData = new byte[0]; @@ -285,7 +285,7 @@ private static AionBlock createDummyBlock() { // TODO: set a dummy limit of 5000000 for now return new AionBlock( parentHash, - AionAddress.wrap(coinbase), + Address.wrap(coinbase), logsBloom, difficulty, number, diff --git a/modAionImpl/test/org/aion/zero/impl/vm/ContractIntegTest.java b/modAionImpl/test/org/aion/zero/impl/vm/ContractIntegTest.java index 99c4691784..b5259f3c56 100644 --- a/modAionImpl/test/org/aion/zero/impl/vm/ContractIntegTest.java +++ b/modAionImpl/test/org/aion/zero/impl/vm/ContractIntegTest.java @@ -34,20 +34,20 @@ import java.math.BigInteger; import java.util.Arrays; import java.util.Collections; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.Hex; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.fastvm.FastVmResultCode; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.aion.mcf.vm.Constants; -import org.aion.mcf.vm.types.DataWord; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.util.bytes.ByteUtil; +import org.aion.util.conversions.Hex; import org.aion.vm.BulkExecutor; import org.aion.vm.ExecutionBatch; import org.aion.vm.PostExecutionWork; -import org.aion.vm.api.interfaces.Address; + import org.aion.vm.api.interfaces.ResultCode; import org.aion.zero.db.AionRepositoryCache; import org.aion.zero.impl.StandaloneBlockchain; @@ -82,7 +82,7 @@ public void setup() { (new Builder()).withValidatorConfiguration("simple").withDefaultAccounts().build(); blockchain = bundle.bc; deployerKey = bundle.privateKeys.get(0); - deployer = new AionAddress(deployerKey.getAddress()); + deployer = new Address(deployerKey.getAddress()); deployerBalance = Builder.DEFAULT_BALANCE; deployerNonce = BigInteger.ZERO; } @@ -116,7 +116,7 @@ public void testEmptyContract() throws IOException { assertEquals(BigInteger.ZERO, blockchain.getRepository().getNonce(deployer)); AionBlock block = makeBlock(tx); - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); ExecutionBatch details = new ExecutionBatch(block, Collections.singletonList(tx)); BulkExecutor exec = @@ -157,7 +157,7 @@ public void testContractDeployCodeIsEmpty() { assertEquals(BigInteger.ZERO, blockchain.getRepository().getNonce(deployer)); AionBlock block = makeBlock(tx); - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); BulkExecutor exec = getNewExecutor(tx, block, repo); AionTxExecSummary summary = exec.execute().get(0); assertEquals("", summary.getReceipt().getError()); // "" == SUCCESS @@ -194,7 +194,7 @@ public void testContractDeployCodeIsNonsensical() { assertEquals(BigInteger.ZERO, blockchain.getRepository().getNonce(deployer)); AionBlock block = makeBlock(tx); - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); BulkExecutor exec = getNewExecutor(tx, block, repo); AionTxExecSummary summary = exec.execute().get(0); assertEquals("OUT_OF_NRG", summary.getReceipt().getError()); @@ -232,7 +232,7 @@ public void testTransferValueToNonPayableConstructor() throws IOException { assertEquals(BigInteger.ZERO, blockchain.getRepository().getNonce(deployer)); AionBlock block = makeBlock(tx); - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); BulkExecutor exec = getNewExecutor(tx, block, repo); AionTxExecSummary summary = exec.execute().get(0); assertEquals("REVERT", summary.getReceipt().getError()); @@ -271,7 +271,7 @@ public void testTransferValueToPayableConstructor() throws IOException { assertEquals(BigInteger.ZERO, blockchain.getRepository().getNonce(deployer)); AionBlock block = makeBlock(tx); - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); BulkExecutor exec = getNewExecutor(tx, block, repo); AionTxExecSummary summary = exec.execute().get(0); assertEquals("", summary.getReceipt().getError()); @@ -305,7 +305,7 @@ public void testTransferValueToPayableConstructorInsufficientFunds() throws IOEx assertEquals(BigInteger.ZERO, blockchain.getRepository().getNonce(deployer)); AionBlock block = makeBlock(tx); - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); BulkExecutor exec = getNewExecutor(tx, block, repo); AionTxExecSummary summary = exec.execute().get(0); assertEquals("INSUFFICIENT_BALANCE", summary.getReceipt().getError()); @@ -338,7 +338,7 @@ public void testConstructorIsCalledOnCodeDeployment() throws IOException { new AionTransaction( nonce.toByteArray(), null, value.toByteArray(), deployCode, nrg, nrgPrice); - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); nonce = nonce.add(BigInteger.ONE); Address contract = deployContract(repo, tx, contractName, null, value, nrg, nrgPrice, nonce); @@ -378,15 +378,15 @@ public void testCallFunction() throws IOException { AionTransaction tx = new AionTransaction( nonce.toByteArray(), null, value.toByteArray(), deployCode, nrg, nrgPrice); - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); nonce = nonce.add(BigInteger.ONE); Address contract = deployContract(repo, tx, contractName, null, value, nrg, nrgPrice, nonce); // ---------- This command will perform addition. ---------- int num = 53475374; - byte[] input = ByteUtil.merge(Hex.decode("f601704f"), new DataWord(num).getData()); - input = ByteUtil.merge(input, new DataWord(1).getData()); + byte[] input = ByteUtil.merge(Hex.decode("f601704f"), new DataWordImpl(num).getData()); + input = ByteUtil.merge(input, new DataWordImpl(1).getData()); tx = new AionTransaction( nonce.toByteArray(), @@ -408,11 +408,11 @@ public void testCallFunction() throws IOException { // Since input takes in uint8 we only want the last byte of num. Output size is well-defined // at 128 bits, or 16 bytes. int expectedResult = 1111 + (num & 0xFF); - assertEquals(expectedResult, new DataWord(summary.getResult()).intValue()); + assertEquals(expectedResult, new DataWordImpl(summary.getResult()).intValue()); // --------- This command will perform subtraction. ---------- - input = ByteUtil.merge(Hex.decode("f601704f"), new DataWord(num).getData()); - input = ByteUtil.merge(input, new DataWord(0).getData()); + input = ByteUtil.merge(Hex.decode("f601704f"), new DataWordImpl(num).getData()); + input = ByteUtil.merge(input, new DataWordImpl(0).getData()); nonce = nonce.add(BigInteger.ONE); tx = new AionTransaction( @@ -435,7 +435,7 @@ public void testCallFunction() throws IOException { // Since input takes in uint8 we only want the last byte of num. Output size is well-defined // at 128 bits, or 16 bytes. expectedResult = 1111 - (num & 0xFF); - assertEquals(expectedResult, new DataWord(summary.getResult()).intValue()); + assertEquals(expectedResult, new DataWordImpl(summary.getResult()).intValue()); } @Test @@ -449,7 +449,7 @@ public void testOverWithdrawFromContract() throws IOException { AionTransaction tx = new AionTransaction( nonce.toByteArray(), null, value.toByteArray(), deployCode, nrg, nrgPrice); - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); nonce = nonce.add(BigInteger.ONE); Address contract = deployContract(repo, tx, contractName, null, value, nrg, nrgPrice, nonce); @@ -459,7 +459,7 @@ public void testOverWithdrawFromContract() throws IOException { repo = blockchain.getRepository().startTracking(); // Contract has no funds, try to withdraw just 1 coin. - byte[] input = ByteUtil.merge(Hex.decode("9424bba3"), new DataWord(1).getData()); + byte[] input = ByteUtil.merge(Hex.decode("9424bba3"), new DataWordImpl(1).getData()); tx = new AionTransaction( nonce.toByteArray(), @@ -496,7 +496,7 @@ public void testWithdrawFromContract() throws IOException { AionTransaction tx = new AionTransaction( nonce.toByteArray(), null, value.toByteArray(), deployCode, nrg, nrgPrice); - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); nonce = nonce.add(BigInteger.ONE); Address contract = deployContract(repo, tx, contractName, null, value, nrg, nrgPrice, nonce); @@ -504,7 +504,7 @@ public void testWithdrawFromContract() throws IOException { BigInteger deployerBalance = repo.getBalance(deployer); // Contract has 2^32 coins, let's withdraw them. - byte[] input = ByteUtil.merge(Hex.decode("9424bba3"), new DataWord(value).getData()); + byte[] input = ByteUtil.merge(Hex.decode("9424bba3"), new DataWordImpl(value).getData()); tx = new AionTransaction( nonce.toByteArray(), @@ -540,7 +540,7 @@ public void testSendContractFundsToOtherAddress() throws IOException { AionTransaction tx = new AionTransaction( nonce.toByteArray(), null, value.toByteArray(), deployCode, nrg, nrgPrice); - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); nonce = nonce.add(BigInteger.ONE); Address contract = deployContract(repo, tx, contractName, null, value, nrg, nrgPrice, nonce); @@ -548,12 +548,12 @@ public void testSendContractFundsToOtherAddress() throws IOException { BigInteger deployerBalance = repo.getBalance(deployer); // Create a new account to be our fund recipient. - Address recipient = new AionAddress(RandomUtils.nextBytes(Address.SIZE)); + Address recipient = new Address(RandomUtils.nextBytes(Address.SIZE)); repo.createAccount(recipient); // Contract has 2^13 coins, let's withdraw them. byte[] input = ByteUtil.merge(Hex.decode("8c50612c"), recipient.toBytes()); - input = ByteUtil.merge(input, new DataWord(value).getData()); + input = ByteUtil.merge(input, new DataWordImpl(value).getData()); tx = new AionTransaction( nonce.toByteArray(), @@ -591,7 +591,7 @@ public void testSendContractFundsToNonexistentAddress() throws IOException { AionTransaction tx = new AionTransaction( nonce.toByteArray(), null, value.toByteArray(), deployCode, nrg, nrgPrice); - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); nonce = nonce.add(BigInteger.ONE); Address contract = deployContract(repo, tx, contractName, null, value, nrg, nrgPrice, nonce); @@ -599,11 +599,11 @@ public void testSendContractFundsToNonexistentAddress() throws IOException { BigInteger deployerBalance = repo.getBalance(deployer); // Create a new account to be our fund recipient. - Address recipient = new AionAddress(RandomUtils.nextBytes(Address.SIZE)); + Address recipient = new Address(RandomUtils.nextBytes(Address.SIZE)); // Contract has 2^13 coins, let's withdraw them. byte[] input = ByteUtil.merge(Hex.decode("8c50612c"), recipient.toBytes()); - input = ByteUtil.merge(input, new DataWord(value).getData()); + input = ByteUtil.merge(input, new DataWordImpl(value).getData()); tx = new AionTransaction( nonce.toByteArray(), @@ -642,7 +642,7 @@ public void testCallContractViaAnotherContract() throws IOException { AionTransaction tx = new AionTransaction( nonce.toByteArray(), null, value.toByteArray(), deployCode, nrg, nrgPrice); - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); nonce = nonce.add(BigInteger.ONE); Address multiFeatureContract = deployContract(repo, tx, contractName, null, value, nrg, nrgPrice, nonce); @@ -660,7 +660,7 @@ public void testCallContractViaAnotherContract() throws IOException { nonce = nonce.add(BigInteger.ONE); Address callerContract = deployContract(repo, tx, contractName, null, value, nrg, nrgPrice, nonce); - Address recipient = new AionAddress(RandomUtils.nextBytes(Address.SIZE)); + Address recipient = new Address(RandomUtils.nextBytes(Address.SIZE)); deployerBalance = repo.getBalance(deployer); deployerNonce = repo.getNonce(deployer); @@ -696,7 +696,7 @@ public void testCallContractViaAnotherContract() throws IOException { value = BigInteger.TWO.pow(20); input = ByteUtil.merge(Hex.decode("57a60e6b"), recipient.toBytes()); - input = ByteUtil.merge(input, new DataWord(value).getData()); + input = ByteUtil.merge(input, new DataWordImpl(value).getData()); nonce = nonce.add(BigInteger.ONE); tx = new AionTransaction( @@ -732,7 +732,7 @@ public void testRecursiveStackoverflow() throws IOException { AionTransaction tx = new AionTransaction( nonce.toByteArray(), null, value.toByteArray(), deployCode, nrg, nrgPrice); - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); nonce = nonce.add(BigInteger.ONE); Address contract = deployContract(repo, tx, contractName, null, value, nrg, nrgPrice, nonce); @@ -743,7 +743,7 @@ public void testRecursiveStackoverflow() throws IOException { // First recurse 1 time less than the max and verify this is ok. int numRecurses = Constants.MAX_CALL_DEPTH - 1; byte[] input = ByteUtil.merge(Hex.decode("2d7df21a"), contract.toBytes()); - input = ByteUtil.merge(input, new DataWord(numRecurses + 1).getData()); + input = ByteUtil.merge(input, new DataWordImpl(numRecurses + 1).getData()); tx = new AionTransaction( nonce.toByteArray(), @@ -774,7 +774,7 @@ public void testRecursiveStackoverflow() throws IOException { // Now recurse the max amount of times and ensure we fail. numRecurses = Constants.MAX_CALL_DEPTH; input = ByteUtil.merge(Hex.decode("2d7df21a"), contract.toBytes()); - input = ByteUtil.merge(input, new DataWord(numRecurses + 1).getData()); + input = ByteUtil.merge(input, new DataWordImpl(numRecurses + 1).getData()); nonce = nonce.add(BigInteger.ONE); tx = new AionTransaction( @@ -810,12 +810,12 @@ public void testCallPrecompiledContract() { // AionTransaction tx = // new AionTransaction( // nonce.toByteArray(), - // AionAddress.wrap(ContractFactoryMock.CALL_ME), + // Address.wrap(ContractFactoryMock.CALL_ME), // value.toByteArray(), // tagToSend.getBytes(), // nrg, // nrgPrice); - // IRepositoryCache repo = blockchain.getRepository().startTracking(); + // RepositoryCache repo = blockchain.getRepository().startTracking(); // // tx.sign(deployerKey); // assertFalse(tx.isContractCreationTransaction()); @@ -893,7 +893,7 @@ public void testCallPrecompiledViaSmartContract() throws IOException { // new AionTransaction( // nonce.toByteArray(), null, value.toByteArray(), deployCode, nrg, // nrgPrice); - // IRepositoryCache repo = blockchain.getRepository().startTracking(); + // RepositoryCache repo = blockchain.getRepository().startTracking(); // nonce = nonce.add(BigInteger.ONE); // Address contract = // deployContract(repo, tx, contractName, null, value, nrg, nrgPrice, nonce); @@ -909,7 +909,7 @@ public void testCallPrecompiledViaSmartContract() throws IOException { // byte[] input = // ByteUtil.merge( // Hex.decode("783efb98"), - // AionAddress.wrap(ContractFactoryMock.CALL_ME).toBytes()); + // Address.wrap(ContractFactoryMock.CALL_ME).toBytes()); // input = ByteUtil.merge(input, msg.getBytes()); // // tx = @@ -952,7 +952,7 @@ public void testCallPrecompiledViaSmartContract() throws IOException { * contract deployer and returns the address of the contract once finished. */ private Address deployContract( - IRepositoryCache repo, + RepositoryCache repo, AionTransaction tx, String contractName, String contractFilename, @@ -1021,7 +1021,7 @@ private byte[] getBodyCode(String contractName) throws IOException { * after the contract whose name is contractName is deployed to it. */ private void checkStateOfNewContract( - IRepositoryCache repo, + RepositoryCache repo, String contractName, Address contractAddr, byte[] output, @@ -1047,7 +1047,7 @@ private void checkStateOfNewContract( * deployed to it. */ private void checkStateOfNewContract( - IRepositoryCache repo, + RepositoryCache repo, String contractName, String contractFilename, Address contractAddr, @@ -1076,7 +1076,7 @@ private void checkStateOfNewContract( * <p>D is default starting amount U is energy used P is energy price V is value transferred */ private void checkStateOfDeployer( - IRepositoryCache repo, + RepositoryCache repo, AionTxExecSummary summary, long nrgPrice, BigInteger value, @@ -1091,7 +1091,7 @@ private void checkStateOfDeployer( * Checks the state of the deployer after a failed attempt to deploy a contract. In this case we * expect the deployer's nonce to still be zero and their balance still default and unchanged. */ - private void checkStateOfDeployerOnBadDeploy(IRepositoryCache repo) { + private void checkStateOfDeployerOnBadDeploy(RepositoryCache repo) { assertEquals(BigInteger.ZERO, repo.getNonce(deployer)); assertEquals(Builder.DEFAULT_BALANCE, repo.getBalance(deployer)); @@ -1102,21 +1102,21 @@ private void checkStateOfDeployerOnBadDeploy(IRepositoryCache repo) { * call to the fastVM and this output is of variable length not predefined length. */ private byte[] extractOutput(byte[] rawOutput) { - int headerLen = new DataWord(Arrays.copyOfRange(rawOutput, 0, DataWord.BYTES)).intValue(); + int headerLen = new DataWordImpl(Arrays.copyOfRange(rawOutput, 0, DataWordImpl.BYTES)).intValue(); int outputLen = - new DataWord( + new DataWordImpl( Arrays.copyOfRange( rawOutput, - (DataWord.BYTES * 2) - headerLen, - DataWord.BYTES * 2)) + (DataWordImpl.BYTES * 2) - headerLen, + DataWordImpl.BYTES * 2)) .intValue(); byte[] output = new byte[outputLen]; - System.arraycopy(rawOutput, DataWord.BYTES * 2, output, 0, outputLen); + System.arraycopy(rawOutput, DataWordImpl.BYTES * 2, output, 0, outputLen); return output; } private BulkExecutor getNewExecutor( - AionTransaction tx, IAionBlock block, IRepositoryCache repo) { + AionTransaction tx, IAionBlock block, RepositoryCache repo) { ExecutionBatch details = new ExecutionBatch(block, Collections.singletonList(tx)); return new BulkExecutor( details, repo, false, true, block.getNrgLimit(), LOGGER_VM, getPostExecutionWork()); diff --git a/modAionImpl/test/org/aion/zero/impl/vm/FvmBulkTransactionTest.java b/modAionImpl/test/org/aion/zero/impl/vm/FvmBulkTransactionTest.java index d88b8493fa..de83fa9684 100644 --- a/modAionImpl/test/org/aion/zero/impl/vm/FvmBulkTransactionTest.java +++ b/modAionImpl/test/org/aion/zero/impl/vm/FvmBulkTransactionTest.java @@ -8,12 +8,12 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.mcf.core.ImportResult; -import org.aion.mcf.vm.types.DataWord; +import org.aion.mcf.vm.types.DataWordImpl; import org.aion.util.conversions.Hex; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.impl.StandaloneBlockchain; import org.aion.zero.impl.types.AionBlock; import org.aion.zero.impl.types.AionBlockSummary; @@ -152,7 +152,7 @@ private AionTransaction makeFvmContractCreateTransaction(ECKey sender, BigIntege AionTransaction transaction = newTransaction( nonce, - AionAddress.wrap(sender.getAddress()), + Address.wrap(sender.getAddress()), null, BigInteger.ZERO, contractBytes, @@ -172,7 +172,7 @@ private AionTransaction makeFvmContractCallTransaction( AionTransaction transaction = newTransaction( nonce, - AionAddress.wrap(sender.getAddress()), + Address.wrap(sender.getAddress()), contract, BigInteger.ZERO, callBytes, @@ -191,7 +191,7 @@ private int getDeployedTickerCountValue(ECKey sender, BigInteger nonce, Address AionTransaction transaction = newTransaction( nonce, - AionAddress.wrap(sender.getAddress()), + Address.wrap(sender.getAddress()), contract, BigInteger.ZERO, callBytes, @@ -202,7 +202,7 @@ private int getDeployedTickerCountValue(ECKey sender, BigInteger nonce, Address AionBlockSummary summary = sendTransactionsInBulkInSingleBlock(Collections.singletonList(transaction)); - return new DataWord(summary.getReceipts().get(0).getTransactionOutput()).intValue(); + return new DataWordImpl(summary.getReceipts().get(0).getTransactionOutput()).intValue(); } private AionTransaction newTransaction( @@ -230,7 +230,7 @@ private BigInteger getNonce(Address address) { } private BigInteger getNonce(ECKey address) { - return getNonce(AionAddress.wrap(address.getAddress())); + return getNonce(Address.wrap(address.getAddress())); } private BigInteger getBalance(Address address) { @@ -238,6 +238,6 @@ private BigInteger getBalance(Address address) { } private BigInteger getBalance(ECKey address) { - return getBalance(AionAddress.wrap(address.getAddress())); + return getBalance(Address.wrap(address.getAddress())); } } diff --git a/modAionImpl/test/org/aion/zero/impl/vm/InternalTransactionTest.java b/modAionImpl/test/org/aion/zero/impl/vm/InternalTransactionTest.java index 50861adee3..96a9f321dd 100644 --- a/modAionImpl/test/org/aion/zero/impl/vm/InternalTransactionTest.java +++ b/modAionImpl/test/org/aion/zero/impl/vm/InternalTransactionTest.java @@ -30,16 +30,17 @@ import java.math.BigInteger; import java.util.Collections; import java.util.List; -import org.aion.base.util.ByteUtil; import org.aion.crypto.ECKey; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.aion.mcf.core.ImportResult; -import org.aion.mcf.vm.types.DataWord; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; +import org.aion.util.bytes.ByteUtil; import org.aion.vm.BulkExecutor; import org.aion.vm.ExecutionBatch; import org.aion.vm.PostExecutionWork; -import org.aion.vm.api.interfaces.Address; + import org.aion.vm.api.interfaces.InternalTransactionInterface; import org.aion.zero.impl.BlockContext; import org.aion.zero.impl.StandaloneBlockchain; @@ -167,7 +168,7 @@ public void testLogs() throws InterruptedException { ByteUtil.merge( ByteUtil.hexStringToBytes("0x2d7df21a"), addressB.toBytes(), - new DataWord(80_000).getData()), + new DataWordImpl(80_000).getData()), 1_000_000L, 1L); tx4.sign(deployerAccount); @@ -194,7 +195,7 @@ public void testLogs() throws InterruptedException { ByteUtil.merge( ByteUtil.hexStringToBytes("0x2d7df21a"), addressB.toBytes(), - new DataWord(20_000).getData()), + new DataWordImpl(20_000).getData()), 1_000_000L, 1L); tx6.sign(deployerAccount); @@ -265,7 +266,7 @@ public void testRecursiveCall() throws InterruptedException { addressA, new byte[0], ByteUtil.merge( - ByteUtil.hexStringToBytes("0xec779964"), new DataWord(2).getData()), + ByteUtil.hexStringToBytes("0xec779964"), new DataWordImpl(2).getData()), 1_000_000L, 1L); tx2.sign(deployerAccount); diff --git a/modAionImpl/test/org/aion/zero/impl/vm/OldTxExecutorTest.java b/modAionImpl/test/org/aion/zero/impl/vm/OldTxExecutorTest.java index 15f3f0e2f4..5c41d4d1d6 100644 --- a/modAionImpl/test/org/aion/zero/impl/vm/OldTxExecutorTest.java +++ b/modAionImpl/test/org/aion/zero/impl/vm/OldTxExecutorTest.java @@ -30,22 +30,22 @@ import java.math.BigInteger; import java.util.Collections; import java.util.List; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; -import org.aion.base.util.Hex; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; import org.aion.crypto.ECKeyFac; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.aion.mcf.core.AccountState; import org.aion.mcf.db.IBlockStoreBase; -import org.aion.mcf.vm.types.DataWord; import org.aion.solidity.CompilationResult; import org.aion.solidity.Compiler; import org.aion.solidity.Compiler.Options; +import org.aion.util.conversions.Hex; import org.aion.vm.BulkExecutor; import org.aion.vm.ExecutionBatch; import org.aion.vm.PostExecutionWork; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.impl.StandaloneBlockchain; import org.aion.zero.impl.db.AionRepositoryImpl; import org.aion.zero.impl.types.AionBlock; @@ -92,25 +92,25 @@ public void testCallTransaction() throws IOException { String deployer = cr.contracts.get("Ticker").bin; // deployer String contract = deployer.substring(deployer.indexOf("60506040", 1)); // contract - byte[] txNonce = DataWord.ZERO.getData(); + byte[] txNonce = DataWordImpl.ZERO.getData(); Address from = - AionAddress.wrap( + Address.wrap( Hex.decode( "1111111111111111111111111111111111111111111111111111111111111111")); Address to = - AionAddress.wrap( + Address.wrap( Hex.decode( "2222222222222222222222222222222222222222222222222222222222222222")); - byte[] value = DataWord.ZERO.getData(); + byte[] value = DataWordImpl.ZERO.getData(); byte[] data = Hex.decode("c0004213"); - long nrg = new DataWord(100000L).longValue(); - long nrgPrice = DataWord.ONE.longValue(); + long nrg = new DataWordImpl(100000L).longValue(); + long nrgPrice = DataWordImpl.ONE.longValue(); AionTransaction tx = new AionTransaction(txNonce, from, to, value, data, nrg, nrgPrice); AionBlock block = createDummyBlock(); AionRepositoryImpl repo = blockchain.getRepository(); - IRepositoryCache cache = repo.startTracking(); + RepositoryCache cache = repo.startTracking(); cache.addBalance(from, BigInteger.valueOf(100_000).multiply(tx.nrgPrice().value())); cache.createAccount(to); cache.saveCode(to, Hex.decode(contract)); @@ -143,13 +143,13 @@ public void testCreateTransaction() throws IOException { String deployer = cr.contracts.get("Ticker").bin; System.out.println(deployer); - byte[] txNonce = DataWord.ZERO.getData(); + byte[] txNonce = DataWordImpl.ZERO.getData(); Address from = - AionAddress.wrap( + Address.wrap( Hex.decode( "1111111111111111111111111111111111111111111111111111111111111111")); - Address to = AionAddress.EMPTY_ADDRESS(); - byte[] value = DataWord.ZERO.getData(); + Address to = null; + byte[] value = DataWordImpl.ZERO.getData(); byte[] data = Hex.decode(deployer); long nrg = 500_000L; long nrgPrice = 1; @@ -158,7 +158,7 @@ public void testCreateTransaction() throws IOException { AionBlock block = createDummyBlock(); AionRepositoryImpl repoTop = blockchain.getRepository(); - IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> repo = repoTop.startTracking(); + RepositoryCache<AccountState, IBlockStoreBase<?, ?>> repo = repoTop.startTracking(); repo.addBalance(from, BigInteger.valueOf(500_000L).multiply(tx.nrgPrice().value())); ExecutionBatch details = new ExecutionBatch(block, Collections.singletonList(tx)); @@ -189,26 +189,26 @@ public void testPerformance() throws IOException { String deployer = cr.contracts.get("Ticker").bin; // deployer String contract = deployer.substring(deployer.indexOf("60506040", 1)); // contract - byte[] txNonce = DataWord.ZERO.getData(); + byte[] txNonce = DataWordImpl.ZERO.getData(); Address from = - AionAddress.wrap( + Address.wrap( Hex.decode( "1111111111111111111111111111111111111111111111111111111111111111")); Address to = - AionAddress.wrap( + Address.wrap( Hex.decode( "2222222222222222222222222222222222222222222222222222222222222222")); - byte[] value = DataWord.ZERO.getData(); + byte[] value = DataWordImpl.ZERO.getData(); byte[] data = Hex.decode("c0004213"); - long nrg = new DataWord(100000L).longValue(); - long nrgPrice = DataWord.ONE.longValue(); + long nrg = new DataWordImpl(100000L).longValue(); + long nrgPrice = DataWordImpl.ONE.longValue(); AionTransaction tx = new AionTransaction(txNonce, from, to, value, data, nrg, nrgPrice); tx.sign(ECKeyFac.inst().create()); AionBlock block = createDummyBlock(); AionRepositoryImpl repoTop = blockchain.getRepository(); - IRepositoryCache repo = repoTop.startTracking(); + RepositoryCache repo = repoTop.startTracking(); repo.addBalance(from, BigInteger.valueOf(100_000).multiply(tx.nrgPrice().value())); repo.createAccount(to); repo.saveCode(to, Hex.decode(contract)); @@ -235,25 +235,25 @@ public void testPerformance() throws IOException { @Test public void testBasicTransactionCost() { - byte[] txNonce = DataWord.ZERO.getData(); + byte[] txNonce = DataWordImpl.ZERO.getData(); Address from = - AionAddress.wrap( + Address.wrap( Hex.decode( "1111111111111111111111111111111111111111111111111111111111111111")); Address to = - AionAddress.wrap( + Address.wrap( Hex.decode( "2222222222222222222222222222222222222222222222222222222222222222")); - byte[] value = DataWord.ONE.getData(); + byte[] value = DataWordImpl.ONE.getData(); byte[] data = new byte[0]; - long nrg = new DataWord(100000L).longValue(); - long nrgPrice = DataWord.ONE.longValue(); + long nrg = new DataWordImpl(100000L).longValue(); + long nrgPrice = DataWordImpl.ONE.longValue(); AionTransaction tx = new AionTransaction(txNonce, from, to, value, data, nrg, nrgPrice); AionBlock block = createDummyBlock(); AionRepositoryImpl repoTop = blockchain.getRepository(); - IRepositoryCache repo = repoTop.startTracking(); + RepositoryCache repo = repoTop.startTracking(); repo.addBalance(from, BigInteger.valueOf(1_000_000_000L)); repo.flush(); @@ -277,7 +277,7 @@ private static AionBlock createDummyBlock() { byte[] parentHash = new byte[32]; byte[] coinbase = RandomUtils.nextBytes(Address.SIZE); byte[] logsBloom = new byte[0]; - byte[] difficulty = new DataWord(0x1000000L).getData(); + byte[] difficulty = new DataWordImpl(0x1000000L).getData(); long number = 1; long timestamp = System.currentTimeMillis() / 1000; byte[] extraData = new byte[0]; @@ -291,7 +291,7 @@ private static AionBlock createDummyBlock() { // TODO: set a dummy limit of 5000000 for now return new AionBlock( parentHash, - AionAddress.wrap(coinbase), + Address.wrap(coinbase), logsBloom, difficulty, number, diff --git a/modAionImpl/test/org/aion/zero/impl/vm/OpcodeIntegTest.java b/modAionImpl/test/org/aion/zero/impl/vm/OpcodeIntegTest.java index 8718b962fe..949e40fe62 100644 --- a/modAionImpl/test/org/aion/zero/impl/vm/OpcodeIntegTest.java +++ b/modAionImpl/test/org/aion/zero/impl/vm/OpcodeIntegTest.java @@ -33,18 +33,19 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.Hex; + +import org.aion.interfaces.db.RepositoryCache; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; -import org.aion.mcf.vm.types.DataWord; +import org.aion.util.bytes.ByteUtil; +import org.aion.util.conversions.Hex; import org.aion.vm.BulkExecutor; import org.aion.vm.ExecutionBatch; import org.aion.vm.PostExecutionWork; -import org.aion.vm.api.interfaces.Address; + import org.aion.vm.api.interfaces.IExecutionLog; import org.aion.vm.api.interfaces.InternalTransactionInterface; import org.aion.zero.impl.BlockContext; @@ -76,7 +77,7 @@ public void setup() { .build(); blockchain = bundle.bc; deployerKey = bundle.privateKeys.get(0); - deployer = new AionAddress(deployerKey.getAddress()); + deployer = new Address(deployerKey.getAddress()); deployerBalance = Builder.DEFAULT_BALANCE; } @@ -92,13 +93,13 @@ public void tearDown() { @Test public void testNoRevert() throws IOException { - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); Address D = deployContract(repo, "F", "F.sol", BigInteger.ZERO); long nrg = 1_000_000; long nrgPrice = 1; BigInteger nonce = BigInteger.ONE; - byte[] input = ByteUtil.merge(Hex.decode("f854bb89"), new DataWord(6).getData()); + byte[] input = ByteUtil.merge(Hex.decode("f854bb89"), new DataWordImpl(6).getData()); AionTransaction tx = new AionTransaction( nonce.toByteArray(), @@ -121,29 +122,29 @@ public void testNoRevert() throws IOException { // Check that the logs from our internal transactions are as we expect. List<IExecutionLog> logs = summary.getReceipt().getLogInfoList(); assertEquals(12, logs.size()); - assertArrayEquals(new DataWord(0).getData(), logs.get(0).getData()); - assertArrayEquals(new DataWord(6).getData(), logs.get(1).getData()); - assertArrayEquals(new DataWord(5).getData(), logs.get(2).getData()); - assertArrayEquals(new DataWord(4).getData(), logs.get(3).getData()); - assertArrayEquals(new DataWord(3).getData(), logs.get(4).getData()); - assertArrayEquals(new DataWord(2).getData(), logs.get(5).getData()); - assertArrayEquals(new DataWord(1).getData(), logs.get(6).getData()); - assertArrayEquals(new DataWord(1).getData(), logs.get(7).getData()); - assertArrayEquals(new DataWord(1).getData(), logs.get(8).getData()); - assertArrayEquals(new DataWord(1).getData(), logs.get(9).getData()); - assertArrayEquals(new DataWord(1).getData(), logs.get(10).getData()); - assertArrayEquals(new DataWord(1).getData(), logs.get(11).getData()); + assertArrayEquals(new DataWordImpl(0).getData(), logs.get(0).getData()); + assertArrayEquals(new DataWordImpl(6).getData(), logs.get(1).getData()); + assertArrayEquals(new DataWordImpl(5).getData(), logs.get(2).getData()); + assertArrayEquals(new DataWordImpl(4).getData(), logs.get(3).getData()); + assertArrayEquals(new DataWordImpl(3).getData(), logs.get(4).getData()); + assertArrayEquals(new DataWordImpl(2).getData(), logs.get(5).getData()); + assertArrayEquals(new DataWordImpl(1).getData(), logs.get(6).getData()); + assertArrayEquals(new DataWordImpl(1).getData(), logs.get(7).getData()); + assertArrayEquals(new DataWordImpl(1).getData(), logs.get(8).getData()); + assertArrayEquals(new DataWordImpl(1).getData(), logs.get(9).getData()); + assertArrayEquals(new DataWordImpl(1).getData(), logs.get(10).getData()); + assertArrayEquals(new DataWordImpl(1).getData(), logs.get(11).getData()); } @Test public void testRevertAtBottomLevel() throws IOException { - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); Address D = deployContract(repo, "F", "F.sol", BigInteger.ZERO); long nrg = 1_000_000; long nrgPrice = 1; BigInteger nonce = BigInteger.ONE; - byte[] input = ByteUtil.merge(Hex.decode("8256cff3"), new DataWord(5).getData()); + byte[] input = ByteUtil.merge(Hex.decode("8256cff3"), new DataWordImpl(5).getData()); AionTransaction tx = new AionTransaction( nonce.toByteArray(), @@ -166,25 +167,25 @@ public void testRevertAtBottomLevel() throws IOException { // Check that the logs from our internal transactions are as we expect. List<IExecutionLog> logs = summary.getReceipt().getLogInfoList(); assertEquals(8, logs.size()); - assertArrayEquals(new DataWord(0).getData(), logs.get(0).getData()); - assertArrayEquals(new DataWord(5).getData(), logs.get(1).getData()); - assertArrayEquals(new DataWord(4).getData(), logs.get(2).getData()); - assertArrayEquals(new DataWord(3).getData(), logs.get(3).getData()); - assertArrayEquals(new DataWord(2).getData(), logs.get(4).getData()); - assertArrayEquals(new DataWord(2).getData(), logs.get(5).getData()); - assertArrayEquals(new DataWord(2).getData(), logs.get(6).getData()); - assertArrayEquals(new DataWord(2).getData(), logs.get(7).getData()); + assertArrayEquals(new DataWordImpl(0).getData(), logs.get(0).getData()); + assertArrayEquals(new DataWordImpl(5).getData(), logs.get(1).getData()); + assertArrayEquals(new DataWordImpl(4).getData(), logs.get(2).getData()); + assertArrayEquals(new DataWordImpl(3).getData(), logs.get(3).getData()); + assertArrayEquals(new DataWordImpl(2).getData(), logs.get(4).getData()); + assertArrayEquals(new DataWordImpl(2).getData(), logs.get(5).getData()); + assertArrayEquals(new DataWordImpl(2).getData(), logs.get(6).getData()); + assertArrayEquals(new DataWordImpl(2).getData(), logs.get(7).getData()); } @Test public void testRevertAtMidLevel() throws IOException { - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); Address D = deployContract(repo, "F", "F.sol", BigInteger.ZERO); long nrg = 1_000_000; long nrgPrice = 1; BigInteger nonce = BigInteger.ONE; - byte[] input = ByteUtil.merge(Hex.decode("10462fd0"), new DataWord(7).getData()); + byte[] input = ByteUtil.merge(Hex.decode("10462fd0"), new DataWordImpl(7).getData()); AionTransaction tx = new AionTransaction( nonce.toByteArray(), @@ -207,21 +208,21 @@ public void testRevertAtMidLevel() throws IOException { // Check that the logs from our internal transactions are as we expect. List<IExecutionLog> logs = summary.getReceipt().getLogInfoList(); assertEquals(8, logs.size()); - assertArrayEquals(new DataWord(0).getData(), logs.get(0).getData()); - assertArrayEquals(new DataWord(7).getData(), logs.get(1).getData()); - assertArrayEquals(new DataWord(6).getData(), logs.get(2).getData()); - assertArrayEquals(new DataWord(5).getData(), logs.get(3).getData()); - assertArrayEquals(new DataWord(4).getData(), logs.get(4).getData()); - assertArrayEquals(new DataWord(4).getData(), logs.get(5).getData()); - assertArrayEquals(new DataWord(4).getData(), logs.get(6).getData()); - assertArrayEquals(new DataWord(4).getData(), logs.get(7).getData()); + assertArrayEquals(new DataWordImpl(0).getData(), logs.get(0).getData()); + assertArrayEquals(new DataWordImpl(7).getData(), logs.get(1).getData()); + assertArrayEquals(new DataWordImpl(6).getData(), logs.get(2).getData()); + assertArrayEquals(new DataWordImpl(5).getData(), logs.get(3).getData()); + assertArrayEquals(new DataWordImpl(4).getData(), logs.get(4).getData()); + assertArrayEquals(new DataWordImpl(4).getData(), logs.get(5).getData()); + assertArrayEquals(new DataWordImpl(4).getData(), logs.get(6).getData()); + assertArrayEquals(new DataWordImpl(4).getData(), logs.get(7).getData()); } // ======================================= test CALLCODE ======================================= @Test public void testCallcodeStorage() throws IOException { - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); BigInteger n = new BigInteger("7638523"); Address D = deployContract(repo, "D", "D.sol", BigInteger.ZERO); Address E = deployContract(repo, "E", "D.sol", BigInteger.ZERO); @@ -232,7 +233,7 @@ public void testCallcodeStorage() throws IOException { long nrgPrice = 1; BigInteger nonce = BigInteger.TWO; byte[] input = ByteUtil.merge(Hex.decode("5cce9fc2"), E.toBytes()); // use CALLCODE on E. - input = ByteUtil.merge(input, new DataWord(n).getData()); // pass in 'n' also. + input = ByteUtil.merge(input, new DataWordImpl(n).getData()); // pass in 'n' also. AionTransaction tx = new AionTransaction( @@ -301,7 +302,7 @@ public void testCallcodeStorage() throws IOException { @Test public void testCallcodeActors() throws IOException { - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); Address D = deployContract(repo, "D", "D.sol", BigInteger.ZERO); Address E = deployContract(repo, "E", "D.sol", BigInteger.ZERO); @@ -312,7 +313,7 @@ public void testCallcodeActors() throws IOException { long nrgPrice = 1; BigInteger nonce = BigInteger.TWO; byte[] input = ByteUtil.merge(Hex.decode("5cce9fc2"), E.toBytes()); // use CALLCODE on E. - input = ByteUtil.merge(input, new DataWord(0).getData()); // pass in 'n' also. + input = ByteUtil.merge(input, new DataWordImpl(0).getData()); // pass in 'n' also. AionTransaction tx = new AionTransaction( @@ -343,7 +344,7 @@ public void testCallcodeActors() throws IOException { @Test public void testCallcodeValueTransfer() throws IOException { - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); Address D = deployContract(repo, "D", "D.sol", BigInteger.ZERO); Address E = deployContract(repo, "E", "D.sol", BigInteger.ZERO); @@ -357,7 +358,7 @@ public void testCallcodeValueTransfer() throws IOException { BigInteger value = new BigInteger("2387653"); BigInteger nonce = BigInteger.TWO; byte[] input = ByteUtil.merge(Hex.decode("5cce9fc2"), E.toBytes()); // use CALLCODE on E. - input = ByteUtil.merge(input, new DataWord(0).getData()); // pass in 'n' also. + input = ByteUtil.merge(input, new DataWordImpl(0).getData()); // pass in 'n' also. AionTransaction tx = new AionTransaction( @@ -386,7 +387,7 @@ public void testCallcodeValueTransfer() throws IOException { @Test public void testDelegateCallStorage() throws IOException { - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); Address D = deployContract(repo, "D", "D.sol", BigInteger.ZERO); Address E = deployContract(repo, "E", "D.sol", BigInteger.ZERO); BigInteger n = new BigInteger("23786523"); @@ -398,7 +399,7 @@ public void testDelegateCallStorage() throws IOException { BigInteger nonce = BigInteger.TWO; byte[] input = ByteUtil.merge(Hex.decode("32817e1d"), E.toBytes()); // use DELEGATECALL on E. - input = ByteUtil.merge(input, new DataWord(n).getData()); // pass in 'n' also. + input = ByteUtil.merge(input, new DataWordImpl(n).getData()); // pass in 'n' also. AionTransaction tx = new AionTransaction( @@ -461,7 +462,7 @@ public void testDelegateCallStorage() throws IOException { @Test public void testDelegateCallActors() throws IOException { - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); Address D = deployContract(repo, "D", "D.sol", BigInteger.ZERO); Address E = deployContract(repo, "E", "D.sol", BigInteger.ZERO); BigInteger n = new BigInteger("23786523"); @@ -473,7 +474,7 @@ public void testDelegateCallActors() throws IOException { BigInteger nonce = BigInteger.TWO; byte[] input = ByteUtil.merge(Hex.decode("32817e1d"), E.toBytes()); // use DELEGATECALL on E. - input = ByteUtil.merge(input, new DataWord(n).getData()); // pass in 'n' also. + input = ByteUtil.merge(input, new DataWordImpl(n).getData()); // pass in 'n' also. AionTransaction tx = new AionTransaction( @@ -500,7 +501,7 @@ public void testDelegateCallActors() throws IOException { @Test public void testDelegateCallValueTransfer() throws IOException { - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); Address D = deployContract(repo, "D", "D.sol", BigInteger.ZERO); Address E = deployContract(repo, "E", "D.sol", BigInteger.ZERO); BigInteger n = new BigInteger("23786523"); @@ -516,7 +517,7 @@ public void testDelegateCallValueTransfer() throws IOException { BigInteger nonce = BigInteger.TWO; byte[] input = ByteUtil.merge(Hex.decode("32817e1d"), E.toBytes()); // use DELEGATECALL on E. - input = ByteUtil.merge(input, new DataWord(n).getData()); // pass in 'n' also. + input = ByteUtil.merge(input, new DataWordImpl(n).getData()); // pass in 'n' also. AionTransaction tx = new AionTransaction( @@ -545,7 +546,7 @@ public void testDelegateCallValueTransfer() throws IOException { @Test public void testOpcodesActors() throws IOException { - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); Address callerContract = deployContract(repo, "Caller", "Opcodes.sol", BigInteger.ZERO); Address calleeContract = deployContract(repo, "Callee", "Opcodes.sol", BigInteger.ZERO); @@ -592,9 +593,9 @@ public void testOpcodesActors() throws IOException { @Test public void testSuicideRecipientExists() throws IOException { - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); BigInteger balance = new BigInteger("32522224"); - Address recipient = new AionAddress(RandomUtils.nextBytes(Address.SIZE)); + Address recipient = new Address(RandomUtils.nextBytes(Address.SIZE)); repo.createAccount(recipient); Address contract = deployContract(repo, "Suicide", "Suicide.sol", BigInteger.ZERO); @@ -639,9 +640,9 @@ public void testSuicideRecipientExists() throws IOException { @Test public void testSuicideRecipientNewlyCreated() throws IOException { - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); BigInteger balance = new BigInteger("32522224"); - Address recipient = new AionAddress(RandomUtils.nextBytes(Address.SIZE)); + Address recipient = new Address(RandomUtils.nextBytes(Address.SIZE)); Address contract = deployContract(repo, "Suicide", "Suicide.sol", BigInteger.ZERO); repo.addBalance(contract, balance); @@ -691,7 +692,7 @@ public void testSuicideRecipientNewlyCreated() throws IOException { * Deploys the contract named contractName in the file named contractFilename with value value. */ private Address deployContract( - IRepositoryCache repo, String contractName, String contractFilename, BigInteger value) + RepositoryCache repo, String contractName, String contractFilename, BigInteger value) throws IOException { byte[] deployCode = ContractUtils.getContractDeployer(contractFilename, contractName); @@ -715,7 +716,7 @@ private Address deployContract( * <p>Returns the address of the newly deployed contract. */ private Address deployContract( - IRepositoryCache repo, + RepositoryCache repo, AionTransaction tx, String contractName, String contractFilename, @@ -758,7 +759,7 @@ private Address deployContract( * equal to whatever value was transferred to it when deployed. */ private void checkStateOfNewContract( - IRepositoryCache repo, + RepositoryCache repo, String contractName, String contractFilename, Address contractAddr, @@ -777,7 +778,7 @@ private void checkStateOfNewContract( * the prior balance minus the tx cost and the value transferred. */ private void checkStateOfDeployer( - IRepositoryCache repo, + RepositoryCache repo, BigInteger priorBalance, long nrgUsed, long nrgPrice, @@ -801,12 +802,12 @@ private void verifyLogData(byte[] data, Address owner, Address caller, Address o assertArrayEquals( Arrays.copyOfRange(data, Address.SIZE * 2, Address.SIZE * 3), origin.toBytes()); assertArrayEquals( - Arrays.copyOfRange(data, data.length - DataWord.BYTES, data.length), - DataWord.ZERO.getData()); + Arrays.copyOfRange(data, data.length - DataWordImpl.BYTES, data.length), + DataWordImpl.ZERO.getData()); } private BulkExecutor getNewExecutor( - AionTransaction tx, IAionBlock block, IRepositoryCache repo) { + AionTransaction tx, IAionBlock block, RepositoryCache repo) { ExecutionBatch details = new ExecutionBatch(block, Collections.singletonList(tx)); return new BulkExecutor( details, repo, false, true, block.getNrgLimit(), LOGGER_VM, getPostExecutionWork()); diff --git a/modAionImpl/test/org/aion/zero/impl/vm/SolidityTypeTest.java b/modAionImpl/test/org/aion/zero/impl/vm/SolidityTypeTest.java index 1628ee32ff..dc9deca062 100644 --- a/modAionImpl/test/org/aion/zero/impl/vm/SolidityTypeTest.java +++ b/modAionImpl/test/org/aion/zero/impl/vm/SolidityTypeTest.java @@ -30,13 +30,11 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.Hex; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; -import org.aion.mcf.vm.types.DataWord; import org.aion.solidity.CompilationResult; import org.aion.solidity.Compiler; import org.aion.solidity.Compiler.Options; @@ -50,10 +48,12 @@ import org.aion.solidity.SolidityType.IntType; import org.aion.solidity.SolidityType.StaticArrayType; import org.aion.solidity.SolidityType.StringType; +import org.aion.util.bytes.ByteUtil; +import org.aion.util.conversions.Hex; import org.aion.vm.BulkExecutor; import org.aion.vm.ExecutionBatch; import org.aion.vm.PostExecutionWork; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.impl.StandaloneBlockchain; import org.aion.zero.impl.db.AionRepositoryImpl; import org.aion.zero.impl.types.AionBlock; @@ -87,23 +87,23 @@ public void tearDown() { } private AionTransaction createTransaction(byte[] callData) { - byte[] txNonce = DataWord.ZERO.getData(); + byte[] txNonce = DataWordImpl.ZERO.getData(); Address from = - AionAddress.wrap( + Address.wrap( Hex.decode( "1111111111111111111111111111111111111111111111111111111111111111")); Address to = - AionAddress.wrap( + Address.wrap( Hex.decode( "2222222222222222222222222222222222222222222222222222222222222222")); - byte[] value = DataWord.ZERO.getData(); + byte[] value = DataWordImpl.ZERO.getData(); byte[] data = callData; - long nrg = new DataWord(100000L).longValue(); - long nrgPrice = DataWord.ONE.longValue(); + long nrg = new DataWordImpl(100000L).longValue(); + long nrgPrice = DataWordImpl.ONE.longValue(); return new AionTransaction(txNonce, from, to, value, data, nrg, nrgPrice); } - private IRepositoryCache createRepository(AionTransaction tx) throws IOException { + private RepositoryCache createRepository(AionTransaction tx) throws IOException { Result r = Compiler.getInstance() .compile(ContractUtils.readContract("SolidityType.sol"), Options.BIN); @@ -112,7 +112,7 @@ private IRepositoryCache createRepository(AionTransaction tx) throws IOException String contract = deployer.substring(deployer.indexOf("60506040", 1)); AionRepositoryImpl repo = blockchain.getRepository(); - IRepositoryCache track = repo.startTracking(); + RepositoryCache track = repo.startTracking(); track.addBalance( tx.getSenderAddress(), tx.nrgPrice().value().multiply(BigInteger.valueOf(500_000L))); @@ -129,7 +129,7 @@ public void testBool() throws IOException { AionTransaction tx = createTransaction(ByteUtil.merge(Hex.decode("e8dde232"), params)); AionBlock block = createDummyBlock(); - IRepositoryCache repo = createRepository(tx); + RepositoryCache repo = createRepository(tx); BulkExecutor exec = getNewExecutor(tx, block, repo); AionTxReceipt receipt = exec.execute().get(0).getReceipt(); @@ -147,7 +147,7 @@ public void testInt() throws IOException { AionTransaction tx = createTransaction(ByteUtil.merge(Hex.decode("6761755c"), params)); AionBlock block = createDummyBlock(); - IRepositoryCache repo = createRepository(tx); + RepositoryCache repo = createRepository(tx); BulkExecutor exec = getNewExecutor(tx, block, repo); AionTxReceipt receipt = exec.execute().get(0).getReceipt(); @@ -166,7 +166,7 @@ public void testAddress() throws IOException { AionTransaction tx = createTransaction(ByteUtil.merge(Hex.decode("42f45790"), params)); AionBlock block = createDummyBlock(); - IRepositoryCache repo = createRepository(tx); + RepositoryCache repo = createRepository(tx); BulkExecutor exec = getNewExecutor(tx, block, repo); AionTxReceipt receipt = exec.execute().get(0).getReceipt(); @@ -185,7 +185,7 @@ public void testFixedBytes1() throws IOException { AionTransaction tx = createTransaction(ByteUtil.merge(Hex.decode("faa068d1"), params)); AionBlock block = createDummyBlock(); - IRepositoryCache repo = createRepository(tx); + RepositoryCache repo = createRepository(tx); BulkExecutor exec = getNewExecutor(tx, block, repo); AionTxReceipt receipt = exec.execute().get(0).getReceipt(); @@ -204,7 +204,7 @@ public void testFixedBytes2() throws IOException { AionTransaction tx = createTransaction(ByteUtil.merge(Hex.decode("877b277f"), params)); AionBlock block = createDummyBlock(); - IRepositoryCache repo = createRepository(tx); + RepositoryCache repo = createRepository(tx); BulkExecutor exec = getNewExecutor(tx, block, repo); AionTxReceipt receipt = exec.execute().get(0).getReceipt(); @@ -224,7 +224,7 @@ public void testString1() throws IOException { AionTransaction tx = createTransaction(ByteUtil.merge(Hex.decode("61cb5a01"), params)); AionBlock block = createDummyBlock(); - IRepositoryCache repo = createRepository(tx); + RepositoryCache repo = createRepository(tx); BulkExecutor exec = getNewExecutor(tx, block, repo); AionTxReceipt receipt = exec.execute().get(0).getReceipt(); @@ -244,7 +244,7 @@ public void testString2() throws IOException { AionTransaction tx = createTransaction(ByteUtil.merge(Hex.decode("61cb5a01"), params)); AionBlock block = createDummyBlock(); - IRepositoryCache repo = createRepository(tx); + RepositoryCache repo = createRepository(tx); BulkExecutor exec = getNewExecutor(tx, block, repo); AionTxReceipt receipt = exec.execute().get(0).getReceipt(); @@ -264,7 +264,7 @@ public void testBytes1() throws IOException { AionTransaction tx = createTransaction(ByteUtil.merge(Hex.decode("61cb5a01"), params)); AionBlock block = createDummyBlock(); - IRepositoryCache repo = createRepository(tx); + RepositoryCache repo = createRepository(tx); BulkExecutor exec = getNewExecutor(tx, block, repo); AionTxReceipt receipt = exec.execute().get(0).getReceipt(); @@ -286,7 +286,7 @@ public void testBytes2() throws IOException { AionTransaction tx = createTransaction(ByteUtil.merge(Hex.decode("61cb5a01"), params)); AionBlock block = createDummyBlock(); - IRepositoryCache repo = createRepository(tx); + RepositoryCache repo = createRepository(tx); BulkExecutor exec = getNewExecutor(tx, block, repo); AionTxReceipt receipt = exec.execute().get(0).getReceipt(); @@ -309,7 +309,7 @@ public void testStaticArray1() throws IOException { AionTransaction tx = createTransaction(ByteUtil.merge(Hex.decode("97e934e2"), params)); AionBlock block = createDummyBlock(); - IRepositoryCache repo = createRepository(tx); + RepositoryCache repo = createRepository(tx); BulkExecutor exec = getNewExecutor(tx, block, repo); AionTxReceipt receipt = exec.execute().get(0).getReceipt(); @@ -336,7 +336,7 @@ public void testStaticArray2() throws IOException { AionTransaction tx = createTransaction(ByteUtil.merge(Hex.decode("e4bef5c9"), params)); AionBlock block = createDummyBlock(); - IRepositoryCache repo = createRepository(tx); + RepositoryCache repo = createRepository(tx); BulkExecutor exec = getNewExecutor(tx, block, repo); AionTxReceipt receipt = exec.execute().get(0).getReceipt(); @@ -364,7 +364,7 @@ public void testDynamicArray1() throws IOException { AionTransaction tx = createTransaction(ByteUtil.merge(Hex.decode("8c0c5523"), params)); AionBlock block = createDummyBlock(); - IRepositoryCache repo = createRepository(tx); + RepositoryCache repo = createRepository(tx); BulkExecutor exec = getNewExecutor(tx, block, repo); AionTxReceipt receipt = exec.execute().get(0).getReceipt(); @@ -392,7 +392,7 @@ public void testDynamicArray2() throws IOException { AionTransaction tx = createTransaction(ByteUtil.merge(Hex.decode("97c3b2db"), params)); AionBlock block = createDummyBlock(); - IRepositoryCache repo = createRepository(tx); + RepositoryCache repo = createRepository(tx); BulkExecutor exec = getNewExecutor(tx, block, repo); AionTxReceipt receipt = exec.execute().get(0).getReceipt(); @@ -407,7 +407,7 @@ public void testDynamicArray2() throws IOException { } private BulkExecutor getNewExecutor( - AionTransaction tx, IAionBlock block, IRepositoryCache repo) { + AionTransaction tx, IAionBlock block, RepositoryCache repo) { ExecutionBatch details = new ExecutionBatch(block, Collections.singletonList(tx)); return new BulkExecutor( details, repo, false, true, block.getNrgLimit(), LOGGER_VM, getPostExecutionWork()); @@ -417,7 +417,7 @@ private static AionBlock createDummyBlock() { byte[] parentHash = new byte[32]; byte[] coinbase = RandomUtils.nextBytes(Address.SIZE); byte[] logsBloom = new byte[0]; - byte[] difficulty = new DataWord(0x1000000L).getData(); + byte[] difficulty = new DataWordImpl(0x1000000L).getData(); long number = 1; long timestamp = System.currentTimeMillis() / 1000; byte[] extraData = new byte[0]; @@ -431,7 +431,7 @@ private static AionBlock createDummyBlock() { // TODO: set a dummy limit of 5000000 for now return new AionBlock( parentHash, - AionAddress.wrap(coinbase), + Address.wrap(coinbase), logsBloom, difficulty, number, diff --git a/modAionImpl/test/org/aion/zero/impl/vm/StatefulnessTest.java b/modAionImpl/test/org/aion/zero/impl/vm/StatefulnessTest.java index 8d54a0a20e..3a06d9668c 100644 --- a/modAionImpl/test/org/aion/zero/impl/vm/StatefulnessTest.java +++ b/modAionImpl/test/org/aion/zero/impl/vm/StatefulnessTest.java @@ -5,17 +5,16 @@ import java.math.BigInteger; import java.util.Arrays; -import java.util.Collections; import org.aion.avm.api.ABIEncoder; import org.aion.avm.core.NodeEnvironment; import org.aion.avm.core.dappreading.JarBuilder; import org.aion.avm.core.util.CodeAndArguments; -import org.aion.base.type.AionAddress; -import org.aion.base.vm.VirtualMachineSpecs; +import org.aion.interfaces.vm.VirtualMachineSpecs; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.mcf.core.ImportResult; import org.aion.vm.VirtualMachineProvider; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.impl.StandaloneBlockchain; import org.aion.zero.impl.vm.contracts.Statefulness; import org.aion.zero.impl.types.AionBlock; @@ -28,7 +27,6 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; -import org.junit.Ignore; import org.junit.Test; // These tests are ignored for now because in order for them to pass we need the clock drift buffer @@ -61,7 +59,7 @@ public void setup() { .build(); this.blockchain = bundle.bc; this.deployerKey = bundle.privateKeys.get(0); - this.deployer = AionAddress.wrap(this.deployerKey.getAddress()); + this.deployer = Address.wrap(this.deployerKey.getAddress()); } @After @@ -89,7 +87,7 @@ public void testStateOfActorsAfterDeployment() { // Check the contract has the Avm prefix, and deployment succeeded, and grab the address. assertEquals(NodeEnvironment.CONTRACT_PREFIX, receipt.getTransactionOutput()[0]); assertTrue(receipt.isSuccessful()); - Address contract = AionAddress.wrap(receipt.getTransactionOutput()); + Address contract = Address.wrap(receipt.getTransactionOutput()); BigInteger deployerBalanceAfterDeployment = getBalance(this.deployer); BigInteger deployerNonceAfterDeployment = getNonce(this.deployer); @@ -115,7 +113,7 @@ public void testUsingCallInContract() { // Check the contract has the Avm prefix, and deployment succeeded, and grab the address. assertEquals(NodeEnvironment.CONTRACT_PREFIX, receipt.getTransactionOutput()[0]); assertTrue(receipt.isSuccessful()); - Address contract = AionAddress.wrap(receipt.getTransactionOutput()); + Address contract = Address.wrap(receipt.getTransactionOutput()); BigInteger deployerInitialNonce = getNonce(this.deployer); BigInteger contractInitialNonce = getNonce(contract); @@ -142,7 +140,7 @@ public void testUsingCallInContract() { assertEquals(contractInitialNonce, getNonce(contract)); // Generate a random beneficiary to transfer funds to via the contract. - Address beneficiary = randomAionAddress(); + Address beneficiary = randomAddress(); long valueForContractToSend = fundsToSendToContract.longValue() / 2; // Call the contract to send value using an internal call. @@ -273,9 +271,9 @@ private BigInteger getNonce(Address address) { return this.blockchain.getRepository().getNonce(address); } - private Address randomAionAddress() { + private Address randomAddress() { byte[] bytes = RandomUtils.nextBytes(32); bytes[0] = (byte) 0xa0; - return AionAddress.wrap(bytes); + return Address.wrap(bytes); } } diff --git a/modAionImpl/test/org/aion/zero/impl/vm/TransactionExecutorTest.java b/modAionImpl/test/org/aion/zero/impl/vm/TransactionExecutorTest.java index 43605db7b5..e3219a34ae 100644 --- a/modAionImpl/test/org/aion/zero/impl/vm/TransactionExecutorTest.java +++ b/modAionImpl/test/org/aion/zero/impl/vm/TransactionExecutorTest.java @@ -32,18 +32,18 @@ import java.math.BigInteger; import java.util.Arrays; import java.util.Collections; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; -import org.aion.base.util.Hex; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.aion.mcf.core.ImportResult; -import org.aion.mcf.vm.types.DataWord; +import org.aion.util.conversions.Hex; import org.aion.vm.BulkExecutor; import org.aion.vm.ExecutionBatch; import org.aion.vm.PostExecutionWork; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.impl.BlockContext; import org.aion.zero.impl.StandaloneBlockchain; import org.aion.zero.impl.StandaloneBlockchain.Builder; @@ -74,7 +74,7 @@ public void setup() { .build(); blockchain = bundle.bc; deployerKey = bundle.privateKeys.get(0); - deployer = new AionAddress(deployerKey.getAddress()); + deployer = new Address(deployerKey.getAddress()); } @After @@ -104,7 +104,7 @@ public void testExecutor() throws IOException { blockchain.createNewBlockContext( blockchain.getBestBlock(), Collections.singletonList(tx), false); - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); ExecutionBatch details = new ExecutionBatch(context.block, Collections.singletonList(tx)); BulkExecutor exec = new BulkExecutor( @@ -201,7 +201,7 @@ public void testDeployedCodeFunctionality() throws IOException { BlockContext context = blockchain.createNewBlockContext( blockchain.getBestBlock(), Collections.singletonList(tx), false); - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); ExecutionBatch details = new ExecutionBatch(context.block, Collections.singletonList(tx)); BulkExecutor exec = new BulkExecutor( @@ -259,9 +259,9 @@ public void testDeployedCodeFunctionality() throws IOException { // // I'm guessing: first data word is the number of bytes that follows. Then those // following // // bytes denote the size of the output, which follows these last bytes. - // int len = new DataWord(Arrays.copyOfRange(output, 0, DataWord.BYTES)).intValue(); + // int len = new DataWordImpl(Arrays.copyOfRange(output, 0, DataWordImpl.BYTES)).intValue(); // byte[] outputLen = new byte[len]; - // System.arraycopy(output, DataWord.BYTES, outputLen, 0, len); + // System.arraycopy(output, DataWordImpl.BYTES, outputLen, 0, len); // int outputSize = new BigInteger(outputLen).intValue(); // // byte[] expected = new byte[1024]; @@ -269,7 +269,7 @@ public void testDeployedCodeFunctionality() throws IOException { // expected[1023] = 'b'; // // byte[] out = new byte[outputSize]; - // System.arraycopy(output, DataWord.BYTES + len, out, 0, outputSize); + // System.arraycopy(output, DataWordImpl.BYTES + len, out, 0, outputSize); byte[] expected = new byte[1024]; expected[0] = 'a'; @@ -299,7 +299,7 @@ public void testGfunction() throws IOException { BlockContext context = blockchain.createNewBlockContext( blockchain.getBestBlock(), Collections.singletonList(tx), false); - IRepositoryCache repo = blockchain.getRepository().startTracking(); + RepositoryCache repo = blockchain.getRepository().startTracking(); ExecutionBatch details = new ExecutionBatch(context.block, Collections.singletonList(tx)); BulkExecutor exec = @@ -349,19 +349,19 @@ private Address deployByteArrayContract() throws IOException { } private Address getNewRecipient(boolean isContractCreation) { - return (isContractCreation) ? null : new AionAddress(RandomUtils.nextBytes(Address.SIZE)); + return (isContractCreation) ? null : new Address(RandomUtils.nextBytes(Address.SIZE)); } private byte[] extractActualOutput(byte[] rawOutput) { // I'm guessing: first data word is the number of bytes that follows. Then those following // bytes denote the size of the output, which follows these last bytes. - int len = new DataWord(Arrays.copyOfRange(rawOutput, 0, DataWord.BYTES)).intValue(); + int len = new DataWordImpl(Arrays.copyOfRange(rawOutput, 0, DataWordImpl.BYTES)).intValue(); byte[] outputLen = new byte[len]; - System.arraycopy(rawOutput, DataWord.BYTES, outputLen, 0, len); + System.arraycopy(rawOutput, DataWordImpl.BYTES, outputLen, 0, len); int outputSize = new BigInteger(outputLen).intValue(); byte[] out = new byte[outputSize]; - System.arraycopy(rawOutput, DataWord.BYTES + len, out, 0, outputSize); + System.arraycopy(rawOutput, DataWordImpl.BYTES + len, out, 0, outputSize); return out; } diff --git a/modAionImpl/test/org/aion/zero/impl/vm/contracts/ContractUtils.java b/modAionImpl/test/org/aion/zero/impl/vm/contracts/ContractUtils.java index 98995a4752..42b7e28cac 100644 --- a/modAionImpl/test/org/aion/zero/impl/vm/contracts/ContractUtils.java +++ b/modAionImpl/test/org/aion/zero/impl/vm/contracts/ContractUtils.java @@ -3,10 +3,10 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import org.aion.base.util.Hex; import org.aion.solidity.CompilationResult; import org.aion.solidity.Compiler; import org.aion.solidity.Compiler.Options; +import org.aion.util.conversions.Hex; public class ContractUtils { /** diff --git a/modApiServer/build.gradle b/modApiServer/build.gradle index de73ace917..d281f6910f 100644 --- a/modApiServer/build.gradle +++ b/modApiServer/build.gradle @@ -14,7 +14,9 @@ sourceSets { } dependencies { - compile project(':modAionBase') + compile 'network.aion:vm-api4j:0.4.0' + compile 'network.aion:util4j:0.4.0' + compile project(':modAion') compile project(':modAionImpl') compile project(':aion_fastvm') diff --git a/modApiServer/src/module-info.java b/modApiServer/src/module-info.java index 8a38078f1d..adf8957e4d 100644 --- a/modApiServer/src/module-info.java +++ b/modApiServer/src/module-info.java @@ -1,5 +1,4 @@ module aion.apiserver { - requires aion.base; requires aion.zero.impl; requires aion.log; requires aion.p2p; @@ -20,6 +19,7 @@ requires com.google.common; requires jdk.unsupported; requires aion.vm.api; + requires aion.util; requires libnzmq; exports org.aion.api.server.pb; diff --git a/modApiServer/src/org/aion/api/server/Api.java b/modApiServer/src/org/aion/api/server/Api.java index a7233a8c1f..dcb83410c7 100644 --- a/modApiServer/src/org/aion/api/server/Api.java +++ b/modApiServer/src/org/aion/api/server/Api.java @@ -10,8 +10,7 @@ import java.util.NoSuchElementException; import org.aion.api.server.types.CompiContrInfo; import org.aion.api.server.types.CompiledContr; -import org.aion.base.type.AionAddress; -import org.aion.base.util.TypeConverter; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; @@ -22,7 +21,7 @@ import org.aion.solidity.CompilationResult; import org.aion.solidity.CompilationResult.Contract; import org.aion.solidity.Compiler; -import org.aion.vm.api.interfaces.Address; +import org.aion.util.string.StringUtils; import org.aion.zero.impl.blockchain.AionPendingStateImpl; import org.slf4j.Logger; @@ -60,7 +59,7 @@ public abstract class Api<B extends AbstractBlock<?, ?>> { protected boolean unlockAccount( final String _address, final String _password, final int _duration) { - return this.ACCOUNT_MANAGER.unlockAccount(AionAddress.wrap(_address), _password, _duration); + return this.ACCOUNT_MANAGER.unlockAccount(Address.wrap(_address), _password, _duration); } public boolean unlockAccount( @@ -77,7 +76,7 @@ public List<String> getAccounts() { } protected ECKey getAccountKey(final String _address) { - return ACCOUNT_MANAGER.getKey(AionAddress.wrap(_address)); + return ACCOUNT_MANAGER.getKey(Address.wrap(_address)); } @SuppressWarnings("rawtypes") @@ -136,7 +135,7 @@ private Map<String, CompiledContr> processCompileRsp(Compiler.Result res, String for (Entry<String, Contract> stringContractEntry : result.contracts.entrySet()) { CompiledContr ret = new CompiledContr(); Contract Contract = stringContractEntry.getValue(); - ret.code = TypeConverter.toJsonHex(Contract.bin); + ret.code = StringUtils.toJsonHex(Contract.bin); ret.info = new CompiContrInfo(); ret.info.source = source; ret.info.language = "Solidity"; diff --git a/modApiServer/src/org/aion/api/server/ApiAion.java b/modApiServer/src/org/aion/api/server/ApiAion.java index 1879b486c8..a79b1e6f87 100644 --- a/modApiServer/src/org/aion/api/server/ApiAion.java +++ b/modApiServer/src/org/aion/api/server/ApiAion.java @@ -20,12 +20,10 @@ import org.aion.api.server.types.Fltr; import org.aion.api.server.types.SyncInfo; import org.aion.api.server.types.TxRecpt; -import org.aion.base.type.AionAddress; -import org.aion.base.type.ITransaction; -import org.aion.base.type.ITxReceipt; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.TypeConverter; +import org.aion.interfaces.tx.Transaction; +import org.aion.interfaces.tx.TxReceipt; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.crypto.ECKey; import org.aion.evtmgr.IEvent; import org.aion.evtmgr.IEventMgr; @@ -34,7 +32,8 @@ import org.aion.evtmgr.impl.evt.EventBlock; import org.aion.evtmgr.impl.evt.EventTx; import org.aion.mcf.blockchain.TxResponse; -import org.aion.vm.api.interfaces.Address; +import org.aion.util.bytes.ByteUtil; +import org.aion.util.string.StringUtils; import org.aion.zero.impl.AionGenesis; import org.aion.zero.impl.AionHub; import org.aion.zero.impl.BlockContext; @@ -110,11 +109,11 @@ public void run() { } else if (e.getEventType() == IHandler.TYPE.TX0.getValue()) { if (e.getCallbackType() == EventTx.CALLBACK.PENDINGTXUPDATE0.getValue()) { pendingTxUpdate( - (ITxReceipt) e.getFuncArgs().get(0), + (TxReceipt) e.getFuncArgs().get(0), GETSTATE((int) e.getFuncArgs().get(1))); } else if (e.getCallbackType() == EventTx.CALLBACK.PENDINGTXRECEIVED0.getValue()) { - for (ITransaction tx : (List<ITransaction>) e.getFuncArgs().get(0)) { + for (Transaction tx : (List<Transaction>) e.getFuncArgs().get(0)) { pendingTxReceived(tx); } } @@ -130,9 +129,9 @@ public void run() { protected abstract void onBlock(AionBlockSummary cbs); - protected abstract void pendingTxReceived(ITransaction _tx); + protected abstract void pendingTxReceived(Transaction _tx); - protected abstract void pendingTxUpdate(ITxReceipt _txRcpt, EventTx.STATE _state); + protected abstract void pendingTxUpdate(TxReceipt _txRcpt, EventTx.STATE _state); // General Level public byte getApiVersion() { @@ -145,7 +144,7 @@ protected Map<Long, Fltr> getInstalledFltrs() { public String getCoinbase() { String coinbase = CfgAion.inst().getConsensus().getMinerAddress(); - return TypeConverter.toJsonHex(coinbase); + return StringUtils.toJsonHex(coinbase); } @Override @@ -442,7 +441,7 @@ protected byte[] doCall(ArgTxCall _params) { protected long estimateNrg(ArgTxCall params) { Address fromAddr = - (params.getFrom().isEmptyAddress()) ? AionAddress.ZERO_ADDRESS() : params.getFrom(); + (params.getFrom() == null) ? Address.ZERO_ADDRESS() : params.getFrom(); AionTransaction tx = new AionTransaction( params.getNonce().toByteArray(), @@ -467,7 +466,7 @@ protected ApiTxResponse createContract(ArgTxCall _params) { Address from = _params.getFrom(); - if (from == null || from.isEmptyAddress()) { + if (from == null) { LOG.error("<create-contract msg=invalid-from-address>"); return (new ApiTxResponse(TxResponse.INVALID_FROM)); } @@ -485,7 +484,7 @@ protected ApiTxResponse createContract(ArgTxCall _params) { !(_params.getNonce().equals(BigInteger.ZERO)) ? _params.getNonce().toByteArray() : pendingState - .bestPendingStateNonce(AionAddress.wrap(key.getAddress())) + .bestPendingStateNonce(Address.wrap(key.getAddress())) .toByteArray(); AionTransaction tx = @@ -512,7 +511,7 @@ protected ApiTxResponse createContract(ArgTxCall _params) { // Transaction Level public BigInteger getBalance(String _address) { - return this.ac.getRepository().getBalance(AionAddress.wrap(_address)); + return this.ac.getRepository().getBalance(Address.wrap(_address)); } public BigInteger getBalance(Address _address) { @@ -520,7 +519,7 @@ public BigInteger getBalance(Address _address) { } public BigInteger getNonce(String _address) { - return this.ac.getRepository().getNonce(AionAddress.wrap(_address)); + return this.ac.getRepository().getNonce(Address.wrap(_address)); } public BigInteger getNonce(Address _address) { @@ -535,7 +534,7 @@ protected ApiTxResponse sendTransaction(ArgTxCall _params) { Address from = _params.getFrom(); - if (from == null || from.isEmptyAddress()) { + if (from == null) { LOG.error("<send-transaction msg=invalid-from-address>"); return (new ApiTxResponse(TxResponse.INVALID_FROM)); } @@ -553,7 +552,7 @@ protected ApiTxResponse sendTransaction(ArgTxCall _params) { (!_params.getNonce().equals(BigInteger.ZERO)) ? _params.getNonce().toByteArray() : pendingState - .bestPendingStateNonce(AionAddress.wrap(key.getAddress())) + .bestPendingStateNonce(Address.wrap(key.getAddress())) .toByteArray(); AionTransaction tx = @@ -597,7 +596,7 @@ protected AionTransaction signTransaction(ArgTxCall _params, String _address) { LOG.error("<sign-transaction msg=invalid-signing-address>"); return null; } else { - address = AionAddress.wrap(_address); + address = Address.wrap(_address); } ECKey key = getAccountKey(address.toString()); @@ -612,7 +611,7 @@ protected AionTransaction signTransaction(ArgTxCall _params, String _address) { (!_params.getNonce().equals(BigInteger.ZERO)) ? _params.getNonce().toByteArray() : pendingState - .bestPendingStateNonce(AionAddress.wrap(key.getAddress())) + .bestPendingStateNonce(Address.wrap(key.getAddress())) .toByteArray(); AionTransaction tx = @@ -651,8 +650,8 @@ protected String[] getBootNodes() { // } // private synchronized BigInteger getTxNonce(ECKey key, boolean add) { - // return add ? nm.getNonceAndAdd(AionAddress.wrap(key.getAddress())) : - // nm.getNonce(AionAddress.wrap(key.getAddress())); + // return add ? nm.getNonceAndAdd(Address.wrap(key.getAddress())) : + // nm.getNonce(Address.wrap(key.getAddress())); // } public boolean isMining() { diff --git a/modApiServer/src/org/aion/api/server/ApiTxResponse.java b/modApiServer/src/org/aion/api/server/ApiTxResponse.java index a5ba15092e..1ffa681265 100644 --- a/modApiServer/src/org/aion/api/server/ApiTxResponse.java +++ b/modApiServer/src/org/aion/api/server/ApiTxResponse.java @@ -1,7 +1,7 @@ package org.aion.api.server; import org.aion.mcf.blockchain.TxResponse; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Address; public class ApiTxResponse { diff --git a/modApiServer/src/org/aion/api/server/IApiAion.java b/modApiServer/src/org/aion/api/server/IApiAion.java index b018d9c547..9867c9d380 100644 --- a/modApiServer/src/org/aion/api/server/IApiAion.java +++ b/modApiServer/src/org/aion/api/server/IApiAion.java @@ -5,7 +5,7 @@ import org.aion.api.server.pb.TxWaitingMappingUpdate; import org.aion.api.server.types.Fltr; import org.aion.api.server.types.TxPendingStatus; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; import org.aion.zero.types.AionTxReceipt; public interface IApiAion { diff --git a/modApiServer/src/org/aion/api/server/nrgprice/INrgPriceAdvisor.java b/modApiServer/src/org/aion/api/server/nrgprice/INrgPriceAdvisor.java index 7224e6f509..3b99bbda79 100644 --- a/modApiServer/src/org/aion/api/server/nrgprice/INrgPriceAdvisor.java +++ b/modApiServer/src/org/aion/api/server/nrgprice/INrgPriceAdvisor.java @@ -1,9 +1,9 @@ package org.aion.api.server.nrgprice; -import org.aion.base.type.IBlock; -import org.aion.base.type.ITransaction; +import org.aion.interfaces.block.Block; +import org.aion.interfaces.tx.Transaction; -public interface INrgPriceAdvisor<BLK extends IBlock, TXN extends ITransaction> { +public interface INrgPriceAdvisor<BLK extends Block, TXN extends Transaction> { /* Is the recommendation engine hungry for more data? * Recommendations prescribed by engine while hungry are left up to the engine itself */ diff --git a/modApiServer/src/org/aion/api/server/nrgprice/NrgPriceAdvisor.java b/modApiServer/src/org/aion/api/server/nrgprice/NrgPriceAdvisor.java index 85ec845d97..021fb8088b 100644 --- a/modApiServer/src/org/aion/api/server/nrgprice/NrgPriceAdvisor.java +++ b/modApiServer/src/org/aion/api/server/nrgprice/NrgPriceAdvisor.java @@ -1,9 +1,9 @@ package org.aion.api.server.nrgprice; -import org.aion.base.type.IBlock; -import org.aion.base.type.ITransaction; +import org.aion.interfaces.block.Block; +import org.aion.interfaces.tx.Transaction; -public abstract class NrgPriceAdvisor<BLK extends IBlock, TXN extends ITransaction> +public abstract class NrgPriceAdvisor<BLK extends Block, TXN extends Transaction> implements INrgPriceAdvisor<BLK, TXN> { protected long defaultPrice; diff --git a/modApiServer/src/org/aion/api/server/nrgprice/strategy/NrgBlockPrice.java b/modApiServer/src/org/aion/api/server/nrgprice/strategy/NrgBlockPrice.java index 805580e655..da48275dc4 100644 --- a/modApiServer/src/org/aion/api/server/nrgprice/strategy/NrgBlockPrice.java +++ b/modApiServer/src/org/aion/api/server/nrgprice/strategy/NrgBlockPrice.java @@ -4,10 +4,9 @@ import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import org.aion.api.server.nrgprice.NrgPriceAdvisor; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; -import org.aion.vm.api.interfaces.Address; import org.aion.zero.impl.types.AionBlock; import org.aion.zero.types.AionTransaction; import org.slf4j.Logger; diff --git a/modApiServer/src/org/aion/api/server/nrgprice/strategy/NrgBlockPriceAveraging.java b/modApiServer/src/org/aion/api/server/nrgprice/strategy/NrgBlockPriceAveraging.java index 7c2a986b51..b933aee04a 100644 --- a/modApiServer/src/org/aion/api/server/nrgprice/strategy/NrgBlockPriceAveraging.java +++ b/modApiServer/src/org/aion/api/server/nrgprice/strategy/NrgBlockPriceAveraging.java @@ -5,10 +5,9 @@ import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import org.aion.api.server.nrgprice.NrgPriceAdvisor; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; -import org.aion.vm.api.interfaces.Address; import org.aion.zero.impl.types.AionBlock; import org.aion.zero.types.AionTransaction; import org.slf4j.Logger; diff --git a/modApiServer/src/org/aion/api/server/pb/ApiAion0.java b/modApiServer/src/org/aion/api/server/pb/ApiAion0.java index e37f70880c..bf5d49ecc4 100644 --- a/modApiServer/src/org/aion/api/server/pb/ApiAion0.java +++ b/modApiServer/src/org/aion/api/server/pb/ApiAion0.java @@ -1,6 +1,5 @@ package org.aion.api.server.pb; -import static org.aion.base.util.ByteUtil.EMPTY_BYTE_ARRAY; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; @@ -39,15 +38,12 @@ import org.aion.api.server.types.TxPendingStatus; import org.aion.api.server.types.TxRecpt; import org.aion.api.server.types.TxRecptLg; -import org.aion.base.type.AionAddress; -import org.aion.base.type.Hash256; -import org.aion.base.type.IBlock; -import org.aion.base.type.ITransaction; -import org.aion.base.type.ITxReceipt; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.Hex; -import org.aion.base.util.TypeConverter; +import org.aion.interfaces.block.Block; +import org.aion.interfaces.tx.Transaction; +import org.aion.interfaces.tx.TxReceipt; +import org.aion.types.Address; + +import org.aion.types.ByteArrayWrapper; import org.aion.equihash.EquihashMiner; import org.aion.evtmgr.IEvent; import org.aion.evtmgr.IHandler; @@ -58,7 +54,10 @@ import org.aion.mcf.account.Keystore; import org.aion.p2p.INode; import org.aion.solidity.Abi; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Hash256; +import org.aion.util.bytes.ByteUtil; +import org.aion.util.conversions.Hex; +import org.aion.util.string.StringUtils; import org.aion.vm.api.interfaces.IExecutionLog; import org.aion.zero.impl.AionBlockchainImpl; import org.aion.zero.impl.AionHub; @@ -73,6 +72,8 @@ import org.apache.commons.collections4.map.LRUMap; import org.json.JSONArray; +import static org.aion.util.bytes.ByteUtil.EMPTY_BYTE_ARRAY; + @SuppressWarnings("Duplicates") public class ApiAion0 extends ApiAion implements IApiAion { @@ -128,7 +129,7 @@ protected void onBlock(AionBlockSummary cbs) { contractAddress, ByteUtil.toHexString( lg))) { - IBlock<AionTransaction, ?> + Block<AionTransaction, ?> blk = (cbs) .getBlock(); @@ -177,7 +178,7 @@ protected void onBlock(AionBlockSummary cbs) { } } - protected void pendingTxReceived(ITransaction _tx) { + protected void pendingTxReceived(Transaction _tx) { installedFilters .values() .forEach( @@ -188,7 +189,7 @@ protected void pendingTxReceived(ITransaction _tx) { }); } - protected void pendingTxUpdate(ITxReceipt _txRcpt, EventTx.STATE _state) { + protected void pendingTxUpdate(TxReceipt _txRcpt, EventTx.STATE _state) { ByteArrayWrapper txHashW = ByteArrayWrapper.wrap( ((AionTxReceipt) _txRcpt).getTransaction().getTransactionHash()); @@ -399,7 +400,7 @@ public byte[] process(byte[] request, byte[] socketId) { .newBuilder() .setMinerAddr( ByteString.copyFrom( - TypeConverter.StringHexToByteArray(cb))) + StringUtils.StringHexToByteArray(cb))) .build(); byte[] retHeader = @@ -429,7 +430,7 @@ public byte[] process(byte[] request, byte[] socketId) { ArgTxCall params = new ArgTxCall( - AionAddress.wrap(req.getFrom().toByteArray()), + Address.wrap(req.getFrom().toByteArray()), null, Hex.decode(new String(bytes).substring(2)), BigInteger.ZERO, @@ -493,7 +494,7 @@ public byte[] process(byte[] request, byte[] socketId) { List<String> accounts = this.getAccounts(); ArrayList<ByteString> al = new ArrayList<>(); for (String s : accounts) { - al.add(ByteString.copyFrom(TypeConverter.StringHexToByteArray(s))); + al.add(ByteString.copyFrom(StringUtils.StringHexToByteArray(s))); } Message.rsp_accounts rsp = Message.rsp_accounts.newBuilder().addAllAccout(al).build(); @@ -531,7 +532,7 @@ public byte[] process(byte[] request, byte[] socketId) { Message.req_unlockAccount req = Message.req_unlockAccount.parseFrom(data); result = this.unlockAccount( - AionAddress.wrap(req.getAccount().toByteArray()), + Address.wrap(req.getAccount().toByteArray()), req.getPassword(), req.getDuration()); } catch (InvalidProtocolBufferException e) { @@ -558,7 +559,7 @@ public byte[] process(byte[] request, byte[] socketId) { try { Message.req_getBalance req = Message.req_getBalance.parseFrom(data); - Address addr = AionAddress.wrap(req.getAddress().toByteArray()); + Address addr = Address.wrap(req.getAddress().toByteArray()); balance = this.getBalance(addr); } catch (InvalidProtocolBufferException e) { @@ -589,7 +590,7 @@ public byte[] process(byte[] request, byte[] socketId) { try { Message.req_getNonce req = Message.req_getNonce.parseFrom(data); - Address addr = AionAddress.wrap(req.getAddress().toByteArray()); + Address addr = Address.wrap(req.getAddress().toByteArray()); nonce = this.getNonce(addr); } catch (InvalidProtocolBufferException e) { @@ -796,8 +797,8 @@ public byte[] process(byte[] request, byte[] socketId) { ArgTxCall params = new ArgTxCall( - AionAddress.wrap(req.getFrom().toByteArray()), - AionAddress.wrap(req.getTo().toByteArray()), + Address.wrap(req.getFrom().toByteArray()), + Address.wrap(req.getTo().toByteArray()), req.getData().toByteArray(), new BigInteger(req.getNonce().toByteArray()), new BigInteger(req.getValue().toByteArray()), @@ -825,7 +826,7 @@ public byte[] process(byte[] request, byte[] socketId) { Message.req_getCode req; try { req = Message.req_getCode.parseFrom(data); - Address to = AionAddress.wrap(req.getAddress().toByteArray()); + Address to = Address.wrap(req.getAddress().toByteArray()); byte[] code = this.getCode(to); if (code == null) { @@ -876,7 +877,7 @@ public byte[] process(byte[] request, byte[] socketId) { .newBuilder() .setAddress( ByteString.copyFrom( - AionAddress.wrap(log.address) + Address.wrap(log.address) .toBytes())) .setData( ByteString.copyFrom( @@ -946,8 +947,8 @@ public byte[] process(byte[] request, byte[] socketId) { try { req = Message.req_call.parseFrom(data); - Address from = AionAddress.wrap(req.getFrom().toByteArray()); - Address to = AionAddress.wrap(req.getTo().toByteArray()); + Address from = Address.wrap(req.getFrom().toByteArray()); + Address to = Address.wrap(req.getTo().toByteArray()); BigInteger value = new BigInteger(req.getValue().toByteArray()); byte[] d = req.getData().toByteArray(); @@ -1162,7 +1163,7 @@ public byte[] process(byte[] request, byte[] socketId) { try { req = Message.req_getTransactionCount.parseFrom(data); long blkNr = req.getBlocknumber(); - Address addr = AionAddress.wrap(req.getAddress().toByteArray()); + Address addr = Address.wrap(req.getAddress().toByteArray()); if (blkNr < -1) { return ApiUtil.toReturnHeader( @@ -1449,7 +1450,7 @@ public byte[] process(byte[] request, byte[] socketId) { pKeyList.add(ByteString.copyFrom(pKey)); } - addressList.add(ByteString.copyFrom(AionAddress.wrap(addr).toBytes())); + addressList.add(ByteString.copyFrom(Address.wrap(addr).toBytes())); } Message.rsp_accountCreate rsp = @@ -1489,7 +1490,7 @@ public byte[] process(byte[] request, byte[] socketId) { Message.req_accountlock req = Message.req_accountlock.parseFrom(data); result = this.lockAccount( - AionAddress.wrap(req.getAccount().toByteArray()), + Address.wrap(req.getAccount().toByteArray()), req.getPassword()); } catch (InvalidProtocolBufferException e) { LOG.error("ApiAion0.process.lockAccount exception: [{}]", e.getMessage()); @@ -1542,8 +1543,8 @@ public byte[] process(byte[] request, byte[] socketId) { ArgTxCall params = new ArgTxCall( - AionAddress.wrap(req.getFrom().toByteArray()), - AionAddress.wrap(req.getTo().toByteArray()), + Address.wrap(req.getFrom().toByteArray()), + Address.wrap(req.getTo().toByteArray()), req.getData().toByteArray(), BigInteger.ZERO, new BigInteger(req.getValue().toByteArray()), @@ -1590,7 +1591,7 @@ public byte[] process(byte[] request, byte[] socketId) { i < req.getKeyFileList().size() && i < ACCOUNT_CREATE_LIMIT; i++) { addrMap.put( - AionAddress.wrap(req.getKeyFile(i).getAddress().toByteArray()), + Address.wrap(req.getKeyFile(i).getAddress().toByteArray()), req.getKeyFile(i).getPassword()); } @@ -2446,7 +2447,7 @@ public byte[] process(byte[] request, byte[] socketId) { a -> { BigInteger b = this.getBalance( - AionAddress.wrap( + Address.wrap( a.toByteArray())); Message.t_AccountDetail.Builder builder = @@ -2654,7 +2655,7 @@ private Message.t_TxDetail getTxDetailsObj( log -> { List<String> topics = new ArrayList<>(); for (int i = 0; i < log.getTopics().size(); i++) { - topics.add(TypeConverter.toJsonHex(log.getTopics().get(i))); + topics.add(StringUtils.toJsonHex(log.getTopics().get(i))); } return Message.t_LgEle @@ -2925,7 +2926,7 @@ private List<Message.t_BlockDetail> getRsp_getBlockDetails( .size(); i++) { topics.add( - TypeConverter + StringUtils .toJsonHex( log.getTopics() .get( diff --git a/modApiServer/src/org/aion/api/server/pb/Message.java b/modApiServer/src/org/aion/api/server/pb/Message.java index d859b60d2a..a48531eade 100644 --- a/modApiServer/src/org/aion/api/server/pb/Message.java +++ b/modApiServer/src/org/aion/api/server/pb/Message.java @@ -71622,7 +71622,7 @@ public com.google.protobuf.ExtensionRegistry assignDescriptors( internal_static_org_aion_api_server_pb_t_BlockDetail_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_aion_api_server_pb_t_BlockDetail_descriptor, - new java.lang.String[] { "BlockNumber", "Timestamp", "NrgConsumed", "NrgLimit", "ParentHash", "MinerAddress", "StateRoot", "TxTrieRoot", "ReceiptTrieRoot", "LogsBloom", "Difficulty", "TotalDifficulty", "ExtraData", "Nonce", "Solution", "Hash", "Size", "Tx", "BlockTime", }); + new java.lang.String[] { "BlockNumber", "Timestamp", "NrgConsumed", "NrgLimit", "ParentHash", "MinerAddress", "StateRoot", "TxTrieRoot", "ReceiptTrieRoot", "LogsBloom", "Difficulty", "TotalDifficulty", "ExtraData", "Nonce", "SolutionImpl", "Hash", "Size", "Tx", "BlockTime", }); internal_static_org_aion_api_server_pb_t_TxDetail_descriptor = getDescriptor().getMessageTypes().get(7); internal_static_org_aion_api_server_pb_t_TxDetail_fieldAccessorTable = new @@ -71640,7 +71640,7 @@ public com.google.protobuf.ExtensionRegistry assignDescriptors( internal_static_org_aion_api_server_pb_t_Block_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_aion_api_server_pb_t_Block_descriptor, - new java.lang.String[] { "BlockNumber", "Timestamp", "NrgConsumed", "NrgLimit", "ParentHash", "MinerAddress", "StateRoot", "TxTrieRoot", "ReceiptTrieRoot", "LogsBloom", "Difficulty", "TotalDifficulty", "ExtraData", "Nonce", "Solution", "Hash", "Size", "TxHash", }); + new java.lang.String[] { "BlockNumber", "Timestamp", "NrgConsumed", "NrgLimit", "ParentHash", "MinerAddress", "StateRoot", "TxTrieRoot", "ReceiptTrieRoot", "LogsBloom", "Difficulty", "TotalDifficulty", "ExtraData", "Nonce", "SolutionImpl", "Hash", "Size", "TxHash", }); internal_static_org_aion_api_server_pb_t_BlockSql_descriptor = getDescriptor().getMessageTypes().get(10); internal_static_org_aion_api_server_pb_t_BlockSql_fieldAccessorTable = new @@ -71676,7 +71676,7 @@ public com.google.protobuf.ExtensionRegistry assignDescriptors( internal_static_org_aion_api_server_pb_rsp_getBlock_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_aion_api_server_pb_rsp_getBlock_descriptor, - new java.lang.String[] { "BlockNumber", "Timestamp", "NrgConsumed", "NrgLimit", "ParentHash", "MinerAddress", "StateRoot", "TxTrieRoot", "ReceiptTrieRoot", "LogsBloom", "Difficulty", "TotalDifficulty", "ExtraData", "Nonce", "Solution", "Hash", "Size", "TxHash", }); + new java.lang.String[] { "BlockNumber", "Timestamp", "NrgConsumed", "NrgLimit", "ParentHash", "MinerAddress", "StateRoot", "TxTrieRoot", "ReceiptTrieRoot", "LogsBloom", "Difficulty", "TotalDifficulty", "ExtraData", "Nonce", "SolutionImpl", "Hash", "Size", "TxHash", }); internal_static_org_aion_api_server_pb_req_getBlockHeaderByNumber_descriptor = getDescriptor().getMessageTypes().get(16); internal_static_org_aion_api_server_pb_req_getBlockHeaderByNumber_fieldAccessorTable = new @@ -71688,7 +71688,7 @@ public com.google.protobuf.ExtensionRegistry assignDescriptors( internal_static_org_aion_api_server_pb_rsp_getBlockHeader_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_aion_api_server_pb_rsp_getBlockHeader_descriptor, - new java.lang.String[] { "BlockNumber", "Timestamp", "NrgConsumed", "NrgLimit", "ParentHash", "MinerAddress", "StateRoot", "TxTrieRoot", "ReceiptTrieRoot", "LogsBloom", "Difficulty", "ExtraData", "Nonce", "Solution", "Hash", "Size", }); + new java.lang.String[] { "BlockNumber", "Timestamp", "NrgConsumed", "NrgLimit", "ParentHash", "MinerAddress", "StateRoot", "TxTrieRoot", "ReceiptTrieRoot", "LogsBloom", "Difficulty", "ExtraData", "Nonce", "SolutionImpl", "Hash", "Size", }); internal_static_org_aion_api_server_pb_req_sendTransaction_descriptor = getDescriptor().getMessageTypes().get(18); internal_static_org_aion_api_server_pb_req_sendTransaction_fieldAccessorTable = new @@ -71910,7 +71910,7 @@ public com.google.protobuf.ExtensionRegistry assignDescriptors( internal_static_org_aion_api_server_pb_req_submitWork_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_aion_api_server_pb_req_submitWork_descriptor, - new java.lang.String[] { "Nonce", "Solution", "Digest", }); + new java.lang.String[] { "Nonce", "SolutionImpl", "Digest", }); internal_static_org_aion_api_server_pb_rsp_submitWork_descriptor = getDescriptor().getMessageTypes().get(54); internal_static_org_aion_api_server_pb_rsp_submitWork_fieldAccessorTable = new diff --git a/modApiServer/src/org/aion/api/server/pb/TxWaitingMappingUpdate.java b/modApiServer/src/org/aion/api/server/pb/TxWaitingMappingUpdate.java index 1e614236f3..b24e675917 100644 --- a/modApiServer/src/org/aion/api/server/pb/TxWaitingMappingUpdate.java +++ b/modApiServer/src/org/aion/api/server/pb/TxWaitingMappingUpdate.java @@ -1,6 +1,6 @@ package org.aion.api.server.pb; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; import org.aion.zero.types.AionTxReceipt; public class TxWaitingMappingUpdate { diff --git a/modApiServer/src/org/aion/api/server/rpc/ApiWeb3Aion.java b/modApiServer/src/org/aion/api/server/rpc/ApiWeb3Aion.java index d03d880a03..43c834374a 100644 --- a/modApiServer/src/org/aion/api/server/rpc/ApiWeb3Aion.java +++ b/modApiServer/src/org/aion/api/server/rpc/ApiWeb3Aion.java @@ -1,8 +1,8 @@ package org.aion.api.server.rpc; import static java.util.stream.Collectors.toList; -import static org.aion.base.util.ByteUtil.hexStringToBytes; -import static org.aion.base.util.ByteUtil.toHexString; +import static org.aion.util.HexConvert.hexStringToBytes; +import static org.aion.util.conversions.Hex.toHexString; import com.github.benmanes.caffeine.cache.CacheLoader; import com.github.benmanes.caffeine.cache.Caffeine; @@ -39,15 +39,12 @@ import org.aion.api.server.types.SyncInfo; import org.aion.api.server.types.Tx; import org.aion.api.server.types.TxRecpt; -import org.aion.base.db.IRepository; -import org.aion.base.type.AionAddress; -import org.aion.base.type.Hash256; -import org.aion.base.type.ITransaction; -import org.aion.base.type.ITxReceipt; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.TypeConverter; -import org.aion.base.util.Utils; +import org.aion.interfaces.db.Repository; +import org.aion.interfaces.tx.Transaction; +import org.aion.interfaces.tx.TxReceipt; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.crypto.ECKey; import org.aion.crypto.HashUtil; import org.aion.evtmgr.IEventMgr; @@ -66,9 +63,10 @@ import org.aion.mcf.config.CfgTx; import org.aion.mcf.core.AccountState; import org.aion.mcf.core.ImportResult; -import org.aion.mcf.vm.types.DataWord; import org.aion.p2p.INode; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Hash256; +import org.aion.util.bytes.ByteUtil; +import org.aion.util.string.StringUtils; import org.aion.vm.api.interfaces.IExecutionLog; import org.aion.zero.impl.AionBlockchainImpl; import org.aion.zero.impl.BlockContext; @@ -138,7 +136,7 @@ protected void onBlock(AionBlockSummary cbs) { } } - protected void pendingTxReceived(ITransaction _tx) { + protected void pendingTxReceived(Transaction _tx) { if (isFilterEnabled) { // not absolutely neccessary to do eviction on installedFilters here, since we're doing // it already @@ -156,13 +154,13 @@ protected void pendingTxReceived(ITransaction _tx) { "<filter append, onPendingTransaction fltrSize={} type={} txHash={}>", f.getSize(), f.getType().name(), - TypeConverter.toJsonHex(_tx.getTransactionHash())); + StringUtils.toJsonHex(_tx.getTransactionHash())); } }); } } - protected void pendingTxUpdate(ITxReceipt _txRcpt, EventTx.STATE _state) { + protected void pendingTxUpdate(TxReceipt _txRcpt, EventTx.STATE _state) { // commenting this out because of lack support for old web3 client that we are using // TODO: re-enable this when we upgrade our web3 client /* @@ -248,7 +246,7 @@ public AionBlock load(ByteArrayWrapper blockHash) { .build( new CacheLoader<>() { public MinerStatsView load(String key) { // no checked exception - Address miner = new AionAddress(key); + Address miner = new Address(key); return new MinerStatsView( STRATUM_RECENT_BLK_COUNT, miner.toBytes()) .update(); @@ -309,7 +307,7 @@ public RpcMsg web3_sha3(Object _params) { } return new RpcMsg( - TypeConverter.toJsonHex(HashUtil.keccak256(ByteUtil.hexStringToBytes(_data)))); + StringUtils.toJsonHex(HashUtil.keccak256(ByteUtil.hexStringToBytes(_data)))); } public RpcMsg net_version() { @@ -377,7 +375,7 @@ public RpcMsg eth_submitHashrate(Object _params) { } public RpcMsg eth_gasPrice() { - return new RpcMsg(TypeConverter.toJsonHex(getRecommendedNrgPrice())); + return new RpcMsg(StringUtils.toJsonHex(getRecommendedNrgPrice())); } public RpcMsg eth_accounts() { @@ -402,14 +400,14 @@ public RpcMsg eth_getBalance(Object _params) { return new RpcMsg(null, RpcError.INVALID_PARAMS, "Invalid parameters"); } - Address address = new AionAddress(_address); + Address address = new Address(_address); String bnOrId = "latest"; if (!JSONObject.NULL.equals(_bnOrId)) { bnOrId = _bnOrId + ""; } - IRepository repo = getRepoByJsonBlockId(bnOrId); + Repository repo = getRepoByJsonBlockId(bnOrId); if (repo == null) // invalid bnOrId { return new RpcMsg( @@ -422,7 +420,7 @@ public RpcMsg eth_getBalance(Object _params) { } BigInteger balance = repo.getBalance(address); - return new RpcMsg(TypeConverter.toJsonHex(balance)); + return new RpcMsg(StringUtils.toJsonHex(balance)); } public RpcMsg eth_getStorageAt(Object _params) { @@ -441,17 +439,17 @@ public RpcMsg eth_getStorageAt(Object _params) { return new RpcMsg(null, RpcError.INVALID_PARAMS, "Invalid parameters"); } - Address address = new AionAddress(_address); + Address address = new Address(_address); String bnOrId = "latest"; if (!JSONObject.NULL.equals(_bnOrId)) { bnOrId = _bnOrId + ""; } - DataWord key; + DataWordImpl key; try { - key = new DataWord(ByteUtil.hexStringToBytes(_index)); + key = new DataWordImpl(ByteUtil.hexStringToBytes(_index)); } catch (Exception e) { // invalid key LOG.debug("eth_getStorageAt: invalid storageIndex. Must be <= 16 bytes."); @@ -459,7 +457,7 @@ public RpcMsg eth_getStorageAt(Object _params) { null, RpcError.INVALID_PARAMS, "Invalid storageIndex. Must be <= 16 bytes."); } - IRepository repo = getRepoByJsonBlockId(bnOrId); + Repository repo = getRepoByJsonBlockId(bnOrId); if (repo == null) // invalid bnOrId { return new RpcMsg( @@ -473,7 +471,7 @@ public RpcMsg eth_getStorageAt(Object _params) { ByteArrayWrapper storageValue = repo.getStorageValue(address, key.toWrapper()); if (storageValue != null) { - return new RpcMsg(TypeConverter.toJsonHex(storageValue.getData())); + return new RpcMsg(StringUtils.toJsonHex(storageValue.getData())); } else { return new RpcMsg(null, RpcError.EXECUTION_ERROR, "Storage value not found"); } @@ -492,14 +490,14 @@ public RpcMsg eth_getTransactionCount(Object _params) { return new RpcMsg(null, RpcError.INVALID_PARAMS, "Invalid parameters"); } - Address address = new AionAddress(_address); + Address address = new Address(_address); String bnOrId = "latest"; if (!JSONObject.NULL.equals(_bnOrId)) { bnOrId = _bnOrId + ""; } - IRepository repo = getRepoByJsonBlockId(bnOrId); + Repository repo = getRepoByJsonBlockId(bnOrId); if (repo == null) // invalid bnOrId { return new RpcMsg( @@ -511,7 +509,7 @@ public RpcMsg eth_getTransactionCount(Object _params) { + "State may have been pruned; please check your db pruning settings in the configuration file."); } - return new RpcMsg(TypeConverter.toJsonHex(repo.getNonce(address))); + return new RpcMsg(StringUtils.toJsonHex(repo.getNonce(address))); } public RpcMsg eth_getBlockTransactionCountByHash(Object _params) { @@ -531,7 +529,7 @@ public RpcMsg eth_getBlockTransactionCountByHash(Object _params) { } long n = b.getTransactionsList().size(); - return new RpcMsg(TypeConverter.toJsonHex(n)); + return new RpcMsg(StringUtils.toJsonHex(n)); } public RpcMsg eth_getBlockTransactionCountByNumber(Object _params) { @@ -552,7 +550,7 @@ public RpcMsg eth_getBlockTransactionCountByNumber(Object _params) { // pending transactions if (bn == BEST_PENDING_BLOCK) { long pendingTxCount = this.ac.getAionHub().getPendingState().getPendingTxSize(); - return new RpcMsg(TypeConverter.toJsonHex(pendingTxCount)); + return new RpcMsg(StringUtils.toJsonHex(pendingTxCount)); } AionBlock b = this.ac.getBlockchain().getBlockByNumber(bn); @@ -563,7 +561,7 @@ public RpcMsg eth_getBlockTransactionCountByNumber(Object _params) { List<AionTransaction> list = b.getTransactionsList(); long n = list.size(); - return new RpcMsg(TypeConverter.toJsonHex(n)); + return new RpcMsg(StringUtils.toJsonHex(n)); } public RpcMsg eth_getCode(Object _params) { @@ -579,14 +577,14 @@ public RpcMsg eth_getCode(Object _params) { return new RpcMsg(null, RpcError.INVALID_PARAMS, "Invalid parameters"); } - Address address = new AionAddress(_address); + Address address = new Address(_address); String bnOrId = "latest"; if (!JSONObject.NULL.equals(_bnOrId)) { bnOrId = _bnOrId + ""; } - IRepository repo = getRepoByJsonBlockId(bnOrId); + Repository repo = getRepoByJsonBlockId(bnOrId); if (repo == null) // invalid bnOrId { return new RpcMsg( @@ -599,7 +597,7 @@ public RpcMsg eth_getCode(Object _params) { } byte[] code = repo.getCode(address); - return new RpcMsg(TypeConverter.toJsonHex(code)); + return new RpcMsg(StringUtils.toJsonHex(code)); } public RpcMsg eth_sign(Object _params) { @@ -615,7 +613,7 @@ public RpcMsg eth_sign(Object _params) { return new RpcMsg(null, RpcError.INVALID_PARAMS, "Invalid parameters"); } - Address address = AionAddress.wrap(_address); + Address address = Address.wrap(_address); ECKey key = getAccountKey(address.toString()); if (key == null) { return new RpcMsg(null, RpcError.NOT_ALLOWED, "Account not unlocked."); @@ -625,7 +623,7 @@ public RpcMsg eth_sign(Object _params) { String message = "\u0019Aion Signed Message:\n" + _message.length() + _message; byte[] messageHash = HashUtil.keccak256(message.getBytes()); - return new RpcMsg(TypeConverter.toJsonHex(key.sign(messageHash).getSignature())); + return new RpcMsg(StringUtils.toJsonHex(key.sign(messageHash).getSignature())); } /** @@ -657,18 +655,18 @@ public RpcMsg eth_signTransaction(Object _params) { AionTransaction tx = signTransaction(txParams, _address); if (tx != null) { JSONObject obj = new JSONObject(); - obj.put("raw", TypeConverter.toJsonHex(tx.getEncoded())); + obj.put("raw", StringUtils.toJsonHex(tx.getEncoded())); JSONObject txObj = new JSONObject(); - txObj.put("nonce", TypeConverter.toJsonHex(tx.getNonce())); - txObj.put("gasPrice", TypeConverter.toJsonHex(tx.getEnergyPrice())); - txObj.put("nrgPrice", TypeConverter.toJsonHex(tx.getEnergyPrice())); - txObj.put("gas", TypeConverter.toJsonHex(tx.getEnergyLimit())); - txObj.put("nrg", TypeConverter.toJsonHex(tx.getEnergyLimit())); - txObj.put("to", TypeConverter.toJsonHex(tx.getDestinationAddress().toString())); - txObj.put("value", TypeConverter.toJsonHex(tx.getValue())); - txObj.put("input", TypeConverter.toJsonHex(tx.getData())); - txObj.put("hash", TypeConverter.toJsonHex(tx.getTransactionHash())); + txObj.put("nonce", StringUtils.toJsonHex(tx.getNonce())); + txObj.put("gasPrice", StringUtils.toJsonHex(tx.getEnergyPrice())); + txObj.put("nrgPrice", StringUtils.toJsonHex(tx.getEnergyPrice())); + txObj.put("gas", StringUtils.toJsonHex(tx.getEnergyLimit())); + txObj.put("nrg", StringUtils.toJsonHex(tx.getEnergyLimit())); + txObj.put("to", StringUtils.toJsonHex(tx.getDestinationAddress().toString())); + txObj.put("value", StringUtils.toJsonHex(tx.getValue())); + txObj.put("input", StringUtils.toJsonHex(tx.getData())); + txObj.put("hash", StringUtils.toJsonHex(tx.getTransactionHash())); obj.put("tx", txObj); return new RpcMsg(obj); @@ -686,7 +684,7 @@ private RpcMsg processTxResponse(ApiTxResponse rsp) { case CACHED_NONCE: case ALREADY_SEALED: case REPAID: - return new RpcMsg(TypeConverter.toJsonHex(rsp.getTxHash())); + return new RpcMsg(StringUtils.toJsonHex(rsp.getTxHash())); case INVALID_TX: case INVALID_TX_NRG_PRICE: case INVALID_FROM: @@ -784,7 +782,7 @@ public RpcMsg eth_call(Object _params) { AionTxReceipt receipt = this.ac.callConstant(tx, b); - return new RpcMsg(TypeConverter.toJsonHex(receipt.getTransactionOutput())); + return new RpcMsg(StringUtils.toJsonHex(receipt.getTransactionOutput())); } public RpcMsg eth_estimateGas(Object _params) { @@ -1000,7 +998,7 @@ public RpcMsg eth_getTransactionReceipt(Object _params) { return new RpcMsg(null, RpcError.INVALID_PARAMS, "Invalid parameters"); } - byte[] txHash = TypeConverter.StringHexToByteArray(_hash); + byte[] txHash = StringUtils.StringHexToByteArray(_hash); TxRecpt r = getTransactionReceipt(txHash); // commenting this out because of lack support for old web3 client that we are using @@ -1168,7 +1166,7 @@ public RpcMsg eth_newFilter(Object _params) { long id = fltrIndex.getAndIncrement(); installedFilters.put(id, filter); - return new RpcMsg(TypeConverter.toJsonHex(id)); + return new RpcMsg(StringUtils.toJsonHex(id)); } public RpcMsg eth_newBlockFilter() { @@ -1178,7 +1176,7 @@ public RpcMsg eth_newBlockFilter() { long id = fltrIndex.getAndIncrement(); installedFilters.put(id, new FltrBlk()); - return new RpcMsg(TypeConverter.toJsonHex(id)); + return new RpcMsg(StringUtils.toJsonHex(id)); } public RpcMsg eth_newPendingTransactionFilter() { @@ -1188,7 +1186,7 @@ public RpcMsg eth_newPendingTransactionFilter() { long id = fltrIndex.getAndIncrement(); installedFilters.put(id, new FltrTx()); - return new RpcMsg(TypeConverter.toJsonHex(id)); + return new RpcMsg(StringUtils.toJsonHex(id)); } public RpcMsg eth_uninstallFilter(Object _params) { @@ -1206,7 +1204,7 @@ public RpcMsg eth_uninstallFilter(Object _params) { } return new RpcMsg( - installedFilters.remove(TypeConverter.StringHexToBigInteger(_id).longValue()) + installedFilters.remove(StringUtils.StringHexToBigInteger(_id).longValue()) != null); } @@ -1236,7 +1234,7 @@ public RpcMsg eth_getFilterChanges(Object _params) { return new RpcMsg(null, RpcError.INVALID_PARAMS, "Invalid parameters"); } - long id = TypeConverter.StringHexToBigInteger(_id).longValue(); + long id = StringUtils.StringHexToBigInteger(_id).longValue(); Fltr filter = installedFilters.get(id); if (filter == null) { @@ -1310,7 +1308,7 @@ public RpcMsg personal_lockAccount(Object _params) { return new RpcMsg(null, RpcError.INVALID_PARAMS, "Invalid parameters"); } - return new RpcMsg(lockAccount(AionAddress.wrap(_account), _password)); + return new RpcMsg(lockAccount(Address.wrap(_account), _password)); } public RpcMsg personal_newAccount(Object _params) { @@ -1325,7 +1323,7 @@ public RpcMsg personal_newAccount(Object _params) { String address = Keystore.create(_password); - return new RpcMsg(TypeConverter.toJsonHex(address)); + return new RpcMsg(StringUtils.toJsonHex(address)); } /* ------------------------------------------------------------------------- @@ -1814,7 +1812,7 @@ public RpcMsg ops_getAccountState(Object _params) { Address address; try { - address = new AionAddress(_address); + address = new Address(_address); } catch (Exception e) { return new RpcMsg(null, RpcError.INVALID_PARAMS, "Invalid address provided."); } @@ -1834,8 +1832,8 @@ public RpcMsg ops_getAccountState(Object _params) { JSONObject response = new JSONObject(); response.put("address", address.toString()); response.put("blockNumber", latestBlkNum); - response.put("balance", TypeConverter.toJsonHex(balance)); - response.put("nonce", TypeConverter.toJsonHex(nonce)); + response.put("balance", StringUtils.toJsonHex(balance)); + response.put("nonce", StringUtils.toJsonHex(nonce)); return new RpcMsg(response); } @@ -2021,14 +2019,14 @@ ChainHeadView update() { /* if (hashQueue.peekFirst() != null) { - System.out.println("[" + 0 + "]: " + TypeConverter.toJsonHex(hashQueue.peekFirst()) + " - " + blocks.get(hashQueue.peekFirst()).getNumber()); + System.out.println("[" + 0 + "]: " + StringUtils.toJsonHex(hashQueue.peekFirst()) + " - " + blocks.get(hashQueue.peekFirst()).getNumber()); System.out.println("----------------------------------------------------------"); System.out.println("isParentHashMatch? " + FastByteComparisons.equal(hashQueue.peekFirst(), blk.getParentHash())); System.out.println("blk.getNumber() " + blk.getNumber()); } System.out.println("blkNum: " + blk.getNumber() + - " parentHash: " + TypeConverter.toJsonHex(blk.getParentHash()) + - " blkHash: " + TypeConverter.toJsonHex(blk.getHash())); + " parentHash: " + StringUtils.toJsonHex(blk.getParentHash()) + + " blkHash: " + StringUtils.toJsonHex(blk.getHash())); */ while (!Arrays.equals(hashQueue.peekFirst(), blk.getParentHash()) @@ -2040,8 +2038,8 @@ ChainHeadView update() { itr++; /* System.out.println("blkNum: " + blk.getNumber() + - " parentHash: " + TypeConverter.toJsonHex(blk.getParentHash()) + - " blkHash: " + TypeConverter.toJsonHex(blk.getHash())); + " parentHash: " + StringUtils.toJsonHex(blk.getParentHash()) + + " blkHash: " + StringUtils.toJsonHex(blk.getHash())); */ } @@ -2071,10 +2069,10 @@ ChainHeadView update() { } /* - System.out.println("[" + 0 + "]: " + TypeConverter.toJsonHex(hashQueue.peekFirst()) + " - " + blocks.get(hashQueue.peekFirst()).getNumber()); + System.out.println("[" + 0 + "]: " + StringUtils.toJsonHex(hashQueue.peekFirst()) + " - " + blocks.get(hashQueue.peekFirst()).getNumber()); System.out.println("----------------------------------------------------------"); for (int i = hashQueue.size() - 1; i >= 0; i--) { - System.out.println("[" + i + "]: " + TypeConverter.toJsonHex(hashQueue.get(i)) + " - " + blocks.get(hashQueue.get(i)).getNumber()); + System.out.println("[" + i + "]: " + StringUtils.toJsonHex(hashQueue.get(i)) + " - " + blocks.get(hashQueue.get(i)).getNumber()); } */ this.response = buildResponse(); @@ -2129,7 +2127,7 @@ public RpcMsg ops_getTransaction(Object _params) { return new RpcMsg(null, RpcError.INVALID_PARAMS, "Invalid parameters"); } - byte[] txHash = TypeConverter.StringHexToByteArray(_hash); + byte[] txHash = StringUtils.StringHexToByteArray(_hash); if (txHash == null) { return new RpcMsg(null, RpcError.INVALID_PARAMS, "Invalid parameters"); @@ -2156,26 +2154,26 @@ public RpcMsg ops_getTransaction(Object _params) { JSONObject result = new JSONObject(); result.put("timestampVal", block.getTimestamp()); - result.put("transactionHash", TypeConverter.toJsonHex(tx.getTransactionHash())); + result.put("transactionHash", StringUtils.toJsonHex(tx.getTransactionHash())); result.put("blockNumber", block.getNumber()); - result.put("blockHash", TypeConverter.toJsonHex(block.getHash())); - result.put("nonce", TypeConverter.toJsonHex(tx.getNonce())); - result.put("fromAddr", TypeConverter.toJsonHex(tx.getSenderAddress().toBytes())); - result.put("toAddr", TypeConverter.toJsonHex(tx.getDestinationAddress().toBytes())); - result.put("value", TypeConverter.toJsonHex(tx.getValue())); + result.put("blockHash", StringUtils.toJsonHex(block.getHash())); + result.put("nonce", StringUtils.toJsonHex(tx.getNonce())); + result.put("fromAddr", StringUtils.toJsonHex(tx.getSenderAddress().toBytes())); + result.put("toAddr", StringUtils.toJsonHex(tx.getDestinationAddress().toBytes())); + result.put("value", StringUtils.toJsonHex(tx.getValue())); result.put("nrgPrice", tx.getEnergyPrice()); result.put("nrgConsumed", txInfo.getReceipt().getEnergyUsed()); - result.put("data", TypeConverter.toJsonHex(tx.getData())); + result.put("data", StringUtils.toJsonHex(tx.getData())); result.put("transactionIndex", txInfo.getIndex()); JSONArray logs = new JSONArray(); for (IExecutionLog l : txInfo.getReceipt().getLogInfoList()) { JSONObject log = new JSONObject(); log.put("address", l.getSourceAddress().toString()); - log.put("data", TypeConverter.toJsonHex(l.getData())); + log.put("data", StringUtils.toJsonHex(l.getData())); JSONArray topics = new JSONArray(); for (byte[] topic : l.getTopics()) { - topics.put(TypeConverter.toJsonHex(topic)); + topics.put(StringUtils.toJsonHex(topic)); } log.put("topics", topics); logs.put(log); @@ -2246,26 +2244,26 @@ public RpcMsg ops_getBlock(Object _params) { blk.put("blockNumber", block.getNumber()); blk.put("numTransactions", block.getTransactionsList().size()); - blk.put("blockHash", TypeConverter.toJsonHex(block.getHash())); - blk.put("parentHash", TypeConverter.toJsonHex(block.getParentHash())); - blk.put("minerAddress", TypeConverter.toJsonHex(block.getCoinbase().toBytes())); + blk.put("blockHash", StringUtils.toJsonHex(block.getHash())); + blk.put("parentHash", StringUtils.toJsonHex(block.getParentHash())); + blk.put("minerAddress", StringUtils.toJsonHex(block.getCoinbase().toBytes())); - blk.put("receiptTxRoot", TypeConverter.toJsonHex(block.getReceiptsRoot())); - blk.put("txTrieRoot", TypeConverter.toJsonHex(block.getTxTrieRoot())); - blk.put("stateRoot", TypeConverter.toJsonHex(block.getStateRoot())); + blk.put("receiptTxRoot", StringUtils.toJsonHex(block.getReceiptsRoot())); + blk.put("txTrieRoot", StringUtils.toJsonHex(block.getTxTrieRoot())); + blk.put("stateRoot", StringUtils.toJsonHex(block.getStateRoot())); - blk.put("difficulty", TypeConverter.toJsonHex(block.getDifficulty())); + blk.put("difficulty", StringUtils.toJsonHex(block.getDifficulty())); blk.put("totalDifficulty", totalDiff.toString(16)); - blk.put("nonce", TypeConverter.toJsonHex(block.getNonce())); + blk.put("nonce", StringUtils.toJsonHex(block.getNonce())); blk.put("blockReward", blkReward); blk.put("nrgConsumed", block.getNrgConsumed()); blk.put("nrgLimit", block.getNrgLimit()); blk.put("size", block.size()); - blk.put("bloom", TypeConverter.toJsonHex(block.getLogBloom())); - blk.put("extraData", TypeConverter.toJsonHex(block.getExtraData())); - blk.put("solution", TypeConverter.toJsonHex(block.getHeader().getSolution())); + blk.put("bloom", StringUtils.toJsonHex(block.getLogBloom())); + blk.put("extraData", StringUtils.toJsonHex(block.getExtraData())); + blk.put("solution", StringUtils.toJsonHex(block.getHeader().getSolution())); JSONObject result = new JSONObject(); result.put("blk", blk); @@ -2275,10 +2273,10 @@ public RpcMsg ops_getBlock(Object _params) { for (AionTransaction tx : block.getTransactionsList()) { // transactionHash, fromAddr, toAddr, value, timestampVal, blockNumber, blockHash JSONArray t = new JSONArray(); - t.put(TypeConverter.toJsonHex(tx.getTransactionHash())); - t.put(TypeConverter.toJsonHex(tx.getSenderAddress().toBytes())); - t.put(TypeConverter.toJsonHex(tx.getDestinationAddress().toBytes())); - t.put(TypeConverter.toJsonHex(tx.getValue())); + t.put(StringUtils.toJsonHex(tx.getTransactionHash())); + t.put(StringUtils.toJsonHex(tx.getSenderAddress().toBytes())); + t.put(StringUtils.toJsonHex(tx.getDestinationAddress().toBytes())); + t.put(StringUtils.toJsonHex(tx.getValue())); t.put(block.getTimestamp()); t.put(block.getNumber()); @@ -2304,7 +2302,7 @@ public RpcMsg ops_getTransactionReceiptByTransactionHash(Object _params) { return new RpcMsg(null, RpcError.INVALID_PARAMS, "Invalid parameters"); } - byte[] transactionHash = TypeConverter.StringHexToByteArray(_transactionHash); + byte[] transactionHash = StringUtils.StringHexToByteArray(_transactionHash); AionTxInfo info = this.ac.getAionHub().getBlockchain().getTransactionInfo(transactionHash); if (info == null) { @@ -2333,8 +2331,8 @@ public RpcMsg ops_getTransactionReceiptByTransactionAndBlockHash(Object _params) return new RpcMsg(null, RpcError.INVALID_PARAMS, "Invalid parameters"); } - byte[] transactionHash = TypeConverter.StringHexToByteArray(_transactionHash); - byte[] blockHash = TypeConverter.StringHexToByteArray(_blockHash); + byte[] transactionHash = StringUtils.StringHexToByteArray(_transactionHash); + byte[] blockHash = StringUtils.StringHexToByteArray(_blockHash); // cast will cause issues after the PoW refactor goes in AionBlockchainImpl chain = (AionBlockchainImpl) this.ac.getAionHub().getBlockchain(); @@ -2366,7 +2364,7 @@ public RpcMsg ops_getTransactionReceiptListByBlockHash(Object _params) { return new RpcMsg(null, RpcError.INVALID_PARAMS, "Invalid parameters"); } - byte[] blockHash = TypeConverter.StringHexToByteArray(_blockHash); + byte[] blockHash = StringUtils.StringHexToByteArray(_blockHash); if (blockHash.length != 32) { return new RpcMsg(null, RpcError.INVALID_PARAMS, "Invalid parameters"); @@ -2503,7 +2501,7 @@ public RpcMsg stratum_validateaddress(Object _params) { JSONObject obj = new JSONObject(); - obj.put("isvalid", Utils.isValidAddress(_address)); + obj.put("isvalid", StringUtils.isValidAddress(_address)); obj.put("address", _address + ""); obj.put("ismine", Keystore.exist(_address)); return new RpcMsg(obj); @@ -2747,14 +2745,14 @@ MinerStatsView update() { /* if (hashQueue.peekFirst() != null) { - System.out.println("[" + 0 + "]: " + TypeConverter.toJsonHex(hashQueue.peekFirst()) + " - " + blocks.get(hashQueue.peekFirst()).getNumber()); + System.out.println("[" + 0 + "]: " + StringUtils.toJsonHex(hashQueue.peekFirst()) + " - " + blocks.get(hashQueue.peekFirst()).getNumber()); System.out.println("----------------------------------------------------------"); System.out.println("isParentHashMatch? " + FastByteComparisons.equal(hashQueue.peekFirst(), blk.getParentHash())); System.out.println("blk.getNumber() " + blk.getNumber()); } System.out.println("blkNum: " + blk.getNumber() + - " parentHash: " + TypeConverter.toJsonHex(blk.getParentHash()) + - " blkHash: " + TypeConverter.toJsonHex(blk.getHash())); + " parentHash: " + StringUtils.toJsonHex(blk.getParentHash()) + + " blkHash: " + StringUtils.toJsonHex(blk.getHash())); */ while (!Arrays.equals(hashQueue.peekFirst(), blk.getParentHash()) @@ -2766,8 +2764,8 @@ MinerStatsView update() { itr++; /* System.out.println("blkNum: " + blk.getNumber() + - " parentHash: " + TypeConverter.toJsonHex(blk.getParentHash()) + - " blkHash: " + TypeConverter.toJsonHex(blk.getHash())); + " parentHash: " + StringUtils.toJsonHex(blk.getParentHash()) + + " blkHash: " + StringUtils.toJsonHex(blk.getHash())); */ } @@ -2791,10 +2789,10 @@ MinerStatsView update() { } /* - System.out.println("[" + 0 + "]: " + TypeConverter.toJsonHex(hashQueue.peekFirst()) + " - " + blocks.get(hashQueue.peekFirst()).getNumber()); + System.out.println("[" + 0 + "]: " + StringUtils.toJsonHex(hashQueue.peekFirst()) + " - " + blocks.get(hashQueue.peekFirst()).getNumber()); System.out.println("----------------------------------------------------------"); for (int i = hashQueue.size() - 1; i >= 0; i--) { - System.out.println("[" + i + "]: " + TypeConverter.toJsonHex(hashQueue.get(i)) + " - " + blocks.get(hashQueue.get(i)).getNumber()); + System.out.println("[" + i + "]: " + StringUtils.toJsonHex(hashQueue.get(i)) + " - " + blocks.get(hashQueue.get(i)).getNumber()); } */ this.response = buildResponse(); @@ -2832,7 +2830,7 @@ public RpcMsg stratum_getMinerStats(Object _params) { // potential bug introduced by .getSnapshotTo() // comment out until resolved - private IRepository getRepoByJsonBlockId(String _bnOrId) { + private Repository getRepoByJsonBlockId(String _bnOrId) { Long bn = parseBnOrId(_bnOrId); if (bn == null) { @@ -2876,7 +2874,7 @@ private Long parseBnOrId(String _bnOrId) { Long ret; if (_bnOrId.startsWith("0x")) { - ret = TypeConverter.StringHexToBigInteger(_bnOrId).longValue(); + ret = StringUtils.StringHexToBigInteger(_bnOrId).longValue(); } else { ret = Long.parseLong(_bnOrId); } diff --git a/modApiServer/src/org/aion/api/server/types/ArgFltr.java b/modApiServer/src/org/aion/api/server/types/ArgFltr.java index 5c3b2ddf90..85e674516a 100644 --- a/modApiServer/src/org/aion/api/server/types/ArgFltr.java +++ b/modApiServer/src/org/aion/api/server/types/ArgFltr.java @@ -2,8 +2,8 @@ import java.util.ArrayList; import java.util.List; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteUtil; +import org.aion.types.Address; +import org.aion.util.bytes.ByteUtil; import org.json.JSONArray; import org.json.JSONObject; @@ -39,10 +39,10 @@ public static ArgFltr fromJSON(final JSONObject json) { if (addressList == null) { // let the address constructor do the validation on the string // it will throw if user passes invalid address - address.add((new AionAddress(json.optString("address", null))).toBytes()); + address.add((new Address(json.optString("address", null))).toBytes()); } else { for (int i = 0; i < addressList.length(); i++) { - address.add((new AionAddress(addressList.optString(i, null))).toBytes()); + address.add((new Address(addressList.optString(i, null))).toBytes()); } } } diff --git a/modApiServer/src/org/aion/api/server/types/ArgTxCall.java b/modApiServer/src/org/aion/api/server/types/ArgTxCall.java index d50d8f8374..991939d0e2 100644 --- a/modApiServer/src/org/aion/api/server/types/ArgTxCall.java +++ b/modApiServer/src/org/aion/api/server/types/ArgTxCall.java @@ -4,13 +4,13 @@ import static org.aion.mcf.vm.Constants.NRG_TRANSACTION_DEFAULT; import java.math.BigInteger; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.TypeConverter; -import org.aion.base.vm.VirtualMachineSpecs; + +import org.aion.interfaces.vm.VirtualMachineSpecs; +import org.aion.types.Address; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; -import org.aion.vm.api.interfaces.Address; +import org.aion.util.bytes.ByteUtil; +import org.aion.util.string.StringUtils; import org.json.JSONObject; import org.slf4j.Logger; @@ -71,9 +71,13 @@ public ArgTxCall( public static ArgTxCall fromJSON(final JSONObject _jsonObj, long defaultNrgPrice) { try { - Address from = - AionAddress.wrap(ByteUtil.hexStringToBytes(_jsonObj.optString("from", ""))); - Address to = AionAddress.wrap(ByteUtil.hexStringToBytes(_jsonObj.optString("to", ""))); + + String fromStr = _jsonObj.optString("from", ""); + Address from = fromStr.equals("") ? null : Address.wrap(ByteUtil.hexStringToBytes(fromStr)); + + String toStr = _jsonObj.optString("to", ""); + Address to = toStr.equals("") ? null : Address.wrap(ByteUtil.hexStringToBytes(toStr)); + byte[] data = ByteUtil.hexStringToBytes(_jsonObj.optString("data", "")); byte type = ByteUtil.hexStringToBytes(_jsonObj.optString("type", "0x1"))[0]; @@ -81,29 +85,29 @@ public static ArgTxCall fromJSON(final JSONObject _jsonObj, long defaultNrgPrice String valueStr = _jsonObj.optString("value", "0x0"); BigInteger nonce = nonceStr.contains("0x") - ? TypeConverter.StringHexToBigInteger(nonceStr) - : TypeConverter.StringNumberAsBigInt(nonceStr); + ? StringUtils.StringHexToBigInteger(nonceStr) + : StringUtils.StringNumberAsBigInt(nonceStr); BigInteger value = valueStr.contains("0x") - ? TypeConverter.StringHexToBigInteger(valueStr) - : TypeConverter.StringNumberAsBigInt(valueStr); + ? StringUtils.StringHexToBigInteger(valueStr) + : StringUtils.StringNumberAsBigInt(valueStr); String nrgStr = _jsonObj.optString("gas", null); String nrgPriceStr = _jsonObj.optString("gasPrice", null); - long nrg = to.isEmptyAddress() ? NRG_CREATE_CONTRACT_DEFAULT : NRG_TRANSACTION_DEFAULT; + long nrg = to == null ? NRG_CREATE_CONTRACT_DEFAULT : NRG_TRANSACTION_DEFAULT; if (nrgStr != null) nrg = nrgStr.contains("0x") - ? TypeConverter.StringHexToBigInteger(nrgStr).longValue() - : TypeConverter.StringNumberAsBigInt(nrgStr).longValue(); + ? StringUtils.StringHexToBigInteger(nrgStr).longValue() + : StringUtils.StringNumberAsBigInt(nrgStr).longValue(); long nrgPrice = defaultNrgPrice; if (nrgPriceStr != null) nrgPrice = nrgPriceStr.contains("0x") - ? TypeConverter.StringHexToBigInteger(nrgPriceStr).longValue() - : TypeConverter.StringNumberAsBigInt(nrgPriceStr).longValue(); + ? StringUtils.StringHexToBigInteger(nrgPriceStr).longValue() + : StringUtils.StringNumberAsBigInt(nrgPriceStr).longValue(); return new ArgTxCall(from, to, data, nonce, value, nrg, nrgPrice, type); } catch (Exception e) { diff --git a/modApiServer/src/org/aion/api/server/types/Blk.java b/modApiServer/src/org/aion/api/server/types/Blk.java index e40fdeb634..5133db89b7 100644 --- a/modApiServer/src/org/aion/api/server/types/Blk.java +++ b/modApiServer/src/org/aion/api/server/types/Blk.java @@ -2,8 +2,9 @@ import java.math.BigInteger; import java.util.List; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.TypeConverter; + +import org.aion.util.bytes.ByteUtil; +import org.aion.util.string.StringUtils; import org.aion.zero.impl.types.AionBlock; import org.aion.zero.types.AionTransaction; import org.json.JSONArray; @@ -23,29 +24,29 @@ public static Object AionBlockToJson( JSONObject obj = new JSONObject(); obj.put("number", block.getNumber()); - obj.put("hash", TypeConverter.toJsonHex(block.getHash())); - obj.put("parentHash", TypeConverter.toJsonHex(block.getParentHash())); - obj.put("logsBloom", TypeConverter.toJsonHex(block.getLogBloom())); - obj.put("transactionsRoot", TypeConverter.toJsonHex(block.getTxTrieRoot())); - obj.put("stateRoot", TypeConverter.toJsonHex(block.getStateRoot())); + obj.put("hash", StringUtils.toJsonHex(block.getHash())); + obj.put("parentHash", StringUtils.toJsonHex(block.getParentHash())); + obj.put("logsBloom", StringUtils.toJsonHex(block.getLogBloom())); + obj.put("transactionsRoot", StringUtils.toJsonHex(block.getTxTrieRoot())); + obj.put("stateRoot", StringUtils.toJsonHex(block.getStateRoot())); obj.put( "receiptsRoot", - TypeConverter.toJsonHex( + StringUtils.toJsonHex( block.getReceiptsRoot() == null ? new byte[0] : block.getReceiptsRoot())); - obj.put("difficulty", TypeConverter.toJsonHex(block.getDifficulty())); - obj.put("totalDifficulty", TypeConverter.toJsonHex(totalDifficulty)); + obj.put("difficulty", StringUtils.toJsonHex(block.getDifficulty())); + obj.put("totalDifficulty", StringUtils.toJsonHex(totalDifficulty)); // TODO: this is coinbase, miner, or minerAddress? - obj.put("miner", TypeConverter.toJsonHex(block.getCoinbase().toString())); - obj.put("timestamp", TypeConverter.toJsonHex(block.getTimestamp())); - obj.put("nonce", TypeConverter.toJsonHex(block.getNonce())); - obj.put("solution", TypeConverter.toJsonHex(block.getHeader().getSolution())); - obj.put("gasUsed", TypeConverter.toJsonHex(block.getHeader().getEnergyConsumed())); - obj.put("gasLimit", TypeConverter.toJsonHex(block.getHeader().getEnergyLimit())); - obj.put("nrgUsed", TypeConverter.toJsonHex(block.getHeader().getEnergyConsumed())); - obj.put("nrgLimit", TypeConverter.toJsonHex(block.getHeader().getEnergyLimit())); + obj.put("miner", StringUtils.toJsonHex(block.getCoinbase().toString())); + obj.put("timestamp", StringUtils.toJsonHex(block.getTimestamp())); + obj.put("nonce", StringUtils.toJsonHex(block.getNonce())); + obj.put("solution", StringUtils.toJsonHex(block.getHeader().getSolution())); + obj.put("gasUsed", StringUtils.toJsonHex(block.getHeader().getEnergyConsumed())); + obj.put("gasLimit", StringUtils.toJsonHex(block.getHeader().getEnergyLimit())); + obj.put("nrgUsed", StringUtils.toJsonHex(block.getHeader().getEnergyConsumed())); + obj.put("nrgLimit", StringUtils.toJsonHex(block.getHeader().getEnergyLimit())); // - obj.put("extraData", TypeConverter.toJsonHex(block.getExtraData())); + obj.put("extraData", StringUtils.toJsonHex(block.getExtraData())); obj.put("size", new NumericalValue(block.size()).toHexString()); JSONArray jsonTxs = new JSONArray(); @@ -57,24 +58,24 @@ public static Object AionBlockToJson( jsonTx.put( "contractAddress", (tx.getContractAddress() != null) - ? TypeConverter.toJsonHex(tx.getContractAddress().toString()) + ? StringUtils.toJsonHex(tx.getContractAddress().toString()) : null); - jsonTx.put("hash", TypeConverter.toJsonHex(tx.getTransactionHash())); + jsonTx.put("hash", StringUtils.toJsonHex(tx.getTransactionHash())); jsonTx.put("transactionIndex", i); - jsonTx.put("value", TypeConverter.toJsonHex(tx.getValue())); + jsonTx.put("value", StringUtils.toJsonHex(tx.getValue())); jsonTx.put("nrg", tx.getEnergyLimit()); - jsonTx.put("nrgPrice", TypeConverter.toJsonHex(tx.getEnergyPrice())); + jsonTx.put("nrgPrice", StringUtils.toJsonHex(tx.getEnergyPrice())); jsonTx.put("gas", tx.getEnergyLimit()); - jsonTx.put("gasPrice", TypeConverter.toJsonHex(tx.getEnergyPrice())); + jsonTx.put("gasPrice", StringUtils.toJsonHex(tx.getEnergyPrice())); jsonTx.put("nonce", ByteUtil.byteArrayToLong(tx.getNonce())); - jsonTx.put("from", TypeConverter.toJsonHex(tx.getSenderAddress().toString())); - jsonTx.put("to", TypeConverter.toJsonHex(tx.getDestinationAddress().toString())); + jsonTx.put("from", StringUtils.toJsonHex(tx.getSenderAddress().toString())); + jsonTx.put("to", StringUtils.toJsonHex(tx.getDestinationAddress().toString())); jsonTx.put("timestamp", block.getTimestamp()); - jsonTx.put("input", TypeConverter.toJsonHex(tx.getData())); + jsonTx.put("input", StringUtils.toJsonHex(tx.getData())); jsonTx.put("blockNumber", block.getNumber()); jsonTxs.put(jsonTx); } else { - jsonTxs.put(TypeConverter.toJsonHex(tx.getTransactionHash())); + jsonTxs.put(StringUtils.toJsonHex(tx.getTransactionHash())); } } obj.put("transactions", jsonTxs); @@ -87,28 +88,28 @@ public static JSONObject AionBlockOnlyToJson(AionBlock block, BigInteger totalDi JSONObject obj = new JSONObject(); obj.put("number", block.getNumber()); - obj.put("hash", TypeConverter.toJsonHex(block.getHash())); - obj.put("parentHash", TypeConverter.toJsonHex(block.getParentHash())); - obj.put("logsBloom", TypeConverter.toJsonHex(block.getLogBloom())); - obj.put("transactionsRoot", TypeConverter.toJsonHex(block.getTxTrieRoot())); - obj.put("stateRoot", TypeConverter.toJsonHex(block.getStateRoot())); + obj.put("hash", StringUtils.toJsonHex(block.getHash())); + obj.put("parentHash", StringUtils.toJsonHex(block.getParentHash())); + obj.put("logsBloom", StringUtils.toJsonHex(block.getLogBloom())); + obj.put("transactionsRoot", StringUtils.toJsonHex(block.getTxTrieRoot())); + obj.put("stateRoot", StringUtils.toJsonHex(block.getStateRoot())); obj.put( "receiptsRoot", - TypeConverter.toJsonHex( + StringUtils.toJsonHex( block.getReceiptsRoot() == null ? new byte[0] : block.getReceiptsRoot())); - obj.put("difficulty", TypeConverter.toJsonHex(block.getDifficulty())); - obj.put("totalDifficulty", TypeConverter.toJsonHex(totalDifficulty)); + obj.put("difficulty", StringUtils.toJsonHex(block.getDifficulty())); + obj.put("totalDifficulty", StringUtils.toJsonHex(totalDifficulty)); - obj.put("miner", TypeConverter.toJsonHex(block.getCoinbase().toString())); - obj.put("timestamp", TypeConverter.toJsonHex(block.getTimestamp())); - obj.put("nonce", TypeConverter.toJsonHex(block.getNonce())); - obj.put("solution", TypeConverter.toJsonHex(block.getHeader().getSolution())); - obj.put("gasUsed", TypeConverter.toJsonHex(block.getHeader().getEnergyConsumed())); - obj.put("gasLimit", TypeConverter.toJsonHex(block.getHeader().getEnergyLimit())); - obj.put("nrgUsed", TypeConverter.toJsonHex(block.getHeader().getEnergyConsumed())); - obj.put("nrgLimit", TypeConverter.toJsonHex(block.getHeader().getEnergyLimit())); + obj.put("miner", StringUtils.toJsonHex(block.getCoinbase().toString())); + obj.put("timestamp", StringUtils.toJsonHex(block.getTimestamp())); + obj.put("nonce", StringUtils.toJsonHex(block.getNonce())); + obj.put("solution", StringUtils.toJsonHex(block.getHeader().getSolution())); + obj.put("gasUsed", StringUtils.toJsonHex(block.getHeader().getEnergyConsumed())); + obj.put("gasLimit", StringUtils.toJsonHex(block.getHeader().getEnergyLimit())); + obj.put("nrgUsed", StringUtils.toJsonHex(block.getHeader().getEnergyConsumed())); + obj.put("nrgLimit", StringUtils.toJsonHex(block.getHeader().getEnergyLimit())); - obj.put("extraData", TypeConverter.toJsonHex(block.getExtraData())); + obj.put("extraData", StringUtils.toJsonHex(block.getExtraData())); obj.put("size", block.size()); obj.put("numTransactions", block.getTransactionsList().size()); diff --git a/modApiServer/src/org/aion/api/server/types/EvtBlk.java b/modApiServer/src/org/aion/api/server/types/EvtBlk.java index 74ec3577ae..9accb99671 100644 --- a/modApiServer/src/org/aion/api/server/types/EvtBlk.java +++ b/modApiServer/src/org/aion/api/server/types/EvtBlk.java @@ -2,15 +2,15 @@ import static org.aion.api.server.types.Fltr.Type; -import org.aion.base.type.IBlock; -import org.aion.base.util.TypeConverter; +import org.aion.interfaces.block.Block; +import org.aion.util.string.StringUtils; @SuppressWarnings("rawtypes") public class EvtBlk extends Evt { - public final IBlock b; + public final Block b; - public EvtBlk(IBlock b) { + public EvtBlk(Block b) { this.b = b; } @@ -21,6 +21,6 @@ public Type getType() { @Override public String toJSON() { - return TypeConverter.toJsonHex(b.getHash()); + return StringUtils.toJsonHex(b.getHash()); } } diff --git a/modApiServer/src/org/aion/api/server/types/EvtTx.java b/modApiServer/src/org/aion/api/server/types/EvtTx.java index 902b18947c..e63b9225e8 100644 --- a/modApiServer/src/org/aion/api/server/types/EvtTx.java +++ b/modApiServer/src/org/aion/api/server/types/EvtTx.java @@ -1,14 +1,14 @@ package org.aion.api.server.types; import org.aion.api.server.types.Fltr.Type; -import org.aion.base.type.ITransaction; -import org.aion.base.util.TypeConverter; +import org.aion.interfaces.tx.Transaction; +import org.aion.util.string.StringUtils; public class EvtTx extends Evt { - private final ITransaction tx; + private final Transaction tx; - public EvtTx(ITransaction tx) { + public EvtTx(Transaction tx) { this.tx = tx; } @@ -19,6 +19,6 @@ public Type getType() { @Override public String toJSON() { - return TypeConverter.toJsonHex(tx.getTransactionHash()); + return StringUtils.toJsonHex(tx.getTransactionHash()); } } diff --git a/modApiServer/src/org/aion/api/server/types/Fltr.java b/modApiServer/src/org/aion/api/server/types/Fltr.java index a97eff6e9e..cd821a7035 100644 --- a/modApiServer/src/org/aion/api/server/types/Fltr.java +++ b/modApiServer/src/org/aion/api/server/types/Fltr.java @@ -2,8 +2,8 @@ import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.atomic.AtomicLong; -import org.aion.base.type.IBlockSummary; -import org.aion.base.type.ITransaction; +import org.aion.interfaces.block.BlockSummary; +import org.aion.interfaces.tx.Transaction; public abstract class Fltr { @@ -70,11 +70,11 @@ public synchronized void add(Evt evt) { if (events.size() < EVTS_MAX) events.add(evt); } - public boolean onBlock(IBlockSummary b) { + public boolean onBlock(BlockSummary b) { return false; } - public boolean onTransaction(ITransaction tx) { + public boolean onTransaction(Transaction tx) { return false; } } diff --git a/modApiServer/src/org/aion/api/server/types/FltrBlk.java b/modApiServer/src/org/aion/api/server/types/FltrBlk.java index 0dd2ba82de..27d27098ba 100644 --- a/modApiServer/src/org/aion/api/server/types/FltrBlk.java +++ b/modApiServer/src/org/aion/api/server/types/FltrBlk.java @@ -1,6 +1,6 @@ package org.aion.api.server.types; -import org.aion.base.type.IBlockSummary; +import org.aion.interfaces.block.BlockSummary; public class FltrBlk extends Fltr { @@ -9,7 +9,7 @@ public FltrBlk() { } @Override - public boolean onBlock(IBlockSummary b) { + public boolean onBlock(BlockSummary b) { add(new EvtBlk(b.getBlock())); return true; } diff --git a/modApiServer/src/org/aion/api/server/types/FltrCt.java b/modApiServer/src/org/aion/api/server/types/FltrCt.java index e3be169faf..ff22afed51 100644 --- a/modApiServer/src/org/aion/api/server/types/FltrCt.java +++ b/modApiServer/src/org/aion/api/server/types/FltrCt.java @@ -2,8 +2,7 @@ import java.util.Arrays; import java.util.List; -import org.aion.base.type.AionAddress; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Address; /** @author chris */ // NOTE: only used by java api diff --git a/modApiServer/src/org/aion/api/server/types/FltrLg.java b/modApiServer/src/org/aion/api/server/types/FltrLg.java index 2c598cb7bd..407f379912 100644 --- a/modApiServer/src/org/aion/api/server/types/FltrLg.java +++ b/modApiServer/src/org/aion/api/server/types/FltrLg.java @@ -3,11 +3,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.aion.base.type.IBlock; -import org.aion.base.type.IBlockSummary; -import org.aion.base.type.ITransaction; +import org.aion.interfaces.block.Block; +import org.aion.interfaces.block.BlockSummary; +import org.aion.interfaces.tx.Transaction; import org.aion.mcf.vm.types.Bloom; -import org.aion.mcf.vm.types.Log; import org.aion.vm.api.interfaces.IExecutionLog; import org.aion.zero.impl.core.BloomFilter; import org.aion.zero.impl.core.IAionBlockchain; @@ -45,14 +44,14 @@ public void setTopics(List<byte[][]> topics) { // ------------------------------------------------------------------------------- @Override - public boolean onBlock(IBlockSummary bs) { + public boolean onBlock(BlockSummary bs) { List<AionTxReceipt> receipts = ((AionBlockSummary) bs).getReceipts(); - IBlock blk = bs.getBlock(); + Block blk = bs.getBlock(); if (matchBloom(new Bloom(((IAionBlock) blk).getLogBloom()))) { int txIndex = 0; for (AionTxReceipt receipt : receipts) { - ITransaction tx = receipt.getTransaction(); + Transaction tx = receipt.getTransaction(); if (matchesContractAddress(tx.getDestinationAddress().toBytes())) { if (matchBloom(receipt.getBloomFilter())) { int logIndex = 0; @@ -85,7 +84,7 @@ public boolean onBlock(IBlockSummary bs) { public boolean onBlock(IAionBlock blk, IAionBlockchain chain) { if (matchBloom(new Bloom(blk.getLogBloom()))) { int txIndex = 0; - for (ITransaction txn : blk.getTransactionsList()) { + for (Transaction txn : blk.getTransactionsList()) { if (matchesContractAddress(txn.getDestinationAddress().toBytes())) { // now that we know that our filter might match with some logs in this // transaction, go ahead diff --git a/modApiServer/src/org/aion/api/server/types/FltrTx.java b/modApiServer/src/org/aion/api/server/types/FltrTx.java index 9c191ad687..7dc99da63b 100644 --- a/modApiServer/src/org/aion/api/server/types/FltrTx.java +++ b/modApiServer/src/org/aion/api/server/types/FltrTx.java @@ -1,6 +1,6 @@ package org.aion.api.server.types; -import org.aion.base.type.ITransaction; +import org.aion.interfaces.tx.Transaction; public class FltrTx extends Fltr { @@ -9,7 +9,7 @@ public FltrTx() { } @Override - public boolean onTransaction(ITransaction tx) { + public boolean onTransaction(Transaction tx) { add(new EvtTx(tx)); return true; } diff --git a/modApiServer/src/org/aion/api/server/types/NumericalValue.java b/modApiServer/src/org/aion/api/server/types/NumericalValue.java index 54e2f91bcb..ee3e344dc5 100644 --- a/modApiServer/src/org/aion/api/server/types/NumericalValue.java +++ b/modApiServer/src/org/aion/api/server/types/NumericalValue.java @@ -2,7 +2,7 @@ import java.math.BigInteger; import java.util.regex.Pattern; -import org.aion.base.util.ByteUtil; +import org.aion.util.bytes.ByteUtil; /** Base type for a numerical value derived from some JSON string, or vice versa */ public class NumericalValue { diff --git a/modApiServer/src/org/aion/api/server/types/Tx.java b/modApiServer/src/org/aion/api/server/types/Tx.java index 2540728fdb..49e5ecb327 100644 --- a/modApiServer/src/org/aion/api/server/types/Tx.java +++ b/modApiServer/src/org/aion/api/server/types/Tx.java @@ -1,7 +1,7 @@ package org.aion.api.server.types; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.TypeConverter; +import org.aion.util.bytes.ByteUtil; +import org.aion.util.string.StringUtils; import org.aion.zero.impl.types.AionBlock; import org.aion.zero.impl.types.AionTxInfo; import org.aion.zero.types.AionTransaction; @@ -35,22 +35,22 @@ public static JSONObject AionTransactionToJSON(AionTransaction tx, AionBlock b, json.put( "contractAddress", (tx.getContractAddress() != null) - ? TypeConverter.toJsonHex(tx.getContractAddress().toString()) + ? StringUtils.toJsonHex(tx.getContractAddress().toString()) : null); - json.put("hash", TypeConverter.toJsonHex(tx.getTransactionHash())); + json.put("hash", StringUtils.toJsonHex(tx.getTransactionHash())); json.put("transactionIndex", index); - json.put("value", TypeConverter.toJsonHex(tx.getValue())); + json.put("value", StringUtils.toJsonHex(tx.getValue())); json.put("nrg", tx.getEnergyLimit()); - json.put("nrgPrice", TypeConverter.toJsonHex(tx.getEnergyPrice())); + json.put("nrgPrice", StringUtils.toJsonHex(tx.getEnergyPrice())); json.put("gas", tx.getEnergyLimit()); - json.put("gasPrice", TypeConverter.toJsonHex(tx.getEnergyPrice())); + json.put("gasPrice", StringUtils.toJsonHex(tx.getEnergyPrice())); json.put("nonce", ByteUtil.byteArrayToLong(tx.getNonce())); - json.put("from", TypeConverter.toJsonHex(tx.getSenderAddress().toString())); - json.put("to", TypeConverter.toJsonHex(tx.getDestinationAddress().toString())); + json.put("from", StringUtils.toJsonHex(tx.getSenderAddress().toString())); + json.put("to", StringUtils.toJsonHex(tx.getDestinationAddress().toString())); json.put("timestamp", b.getTimestamp()); - json.put("input", TypeConverter.toJsonHex(tx.getData())); - json.put("blockNumber", TypeConverter.toJsonHex(b.getNumber())); - json.put("blockHash", TypeConverter.toJsonHex(b.getHash())); + json.put("input", StringUtils.toJsonHex(tx.getData())); + json.put("blockNumber", StringUtils.toJsonHex(b.getNumber())); + json.put("blockHash", StringUtils.toJsonHex(b.getHash())); return json; } diff --git a/modApiServer/src/org/aion/api/server/types/TxPendingStatus.java b/modApiServer/src/org/aion/api/server/types/TxPendingStatus.java index 7747c777f9..4f8939e036 100644 --- a/modApiServer/src/org/aion/api/server/types/TxPendingStatus.java +++ b/modApiServer/src/org/aion/api/server/types/TxPendingStatus.java @@ -1,6 +1,6 @@ package org.aion.api.server.types; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; import org.aion.mcf.evt.IListenerBase; public class TxPendingStatus { diff --git a/modApiServer/src/org/aion/api/server/types/TxRecpt.java b/modApiServer/src/org/aion/api/server/types/TxRecpt.java index 688efe2a5b..5ea5bb1516 100644 --- a/modApiServer/src/org/aion/api/server/types/TxRecpt.java +++ b/modApiServer/src/org/aion/api/server/types/TxRecpt.java @@ -1,17 +1,15 @@ package org.aion.api.server.types; -import static org.aion.base.util.TypeConverter.toJsonHex; +import static org.aion.util.string.StringUtils.toJsonHex; -import org.aion.base.type.AionAddress; -import org.aion.base.type.IBlock; -import org.aion.base.type.IBlockHeader; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.TypeConverter; +import org.aion.interfaces.block.Block; +import org.aion.interfaces.block.BlockHeader; +import org.aion.types.Address; import org.aion.mcf.core.AbstractTxInfo; import org.aion.mcf.types.AbstractTransaction; import org.aion.mcf.types.AbstractTxReceipt; -import org.aion.mcf.vm.types.Log; -import org.aion.vm.api.interfaces.Address; +import org.aion.util.bytes.ByteUtil; +import org.aion.util.string.StringUtils; import org.aion.vm.api.interfaces.IExecutionLog; import org.aion.zero.impl.types.AionBlock; import org.aion.zero.types.AionTransaction; @@ -72,10 +70,10 @@ public final class TxRecpt { public < TX extends AbstractTransaction, - BH extends IBlockHeader, + BH extends BlockHeader, TXR extends AbstractTxReceipt<TX>> TxRecpt( - IBlock<TX, BH> block, + Block<TX, BH> block, AbstractTxInfo<TXR, TX> txInfo, long cumulativeNrgUsed, boolean isMainchain) { @@ -120,7 +118,7 @@ public final class TxRecpt { this.to = this.toAddr == null ? null : toJsonHex(this.toAddr.toBytes()); this.txTimeStamp = ByteUtil.byteArrayToLong(receipt.getTransaction().getTimeStamp()); - this.txValue = TypeConverter.toJsonHex(txInfo.getReceipt().getTransaction().getValue()); + this.txValue = StringUtils.toJsonHex(txInfo.getReceipt().getTransaction().getValue()); this.txNonce = ByteUtil.byteArrayToLong(txInfo.getReceipt().getTransaction().getNonce()); byte[] _txData = txInfo.getReceipt().getTransaction().getData(); this.txData = _txData == null ? "" : toJsonHex(_txData); diff --git a/modApiServer/src/org/aion/api/server/types/TxRecptLg.java b/modApiServer/src/org/aion/api/server/types/TxRecptLg.java index aa27d92ae2..7c08f379d5 100644 --- a/modApiServer/src/org/aion/api/server/types/TxRecptLg.java +++ b/modApiServer/src/org/aion/api/server/types/TxRecptLg.java @@ -1,9 +1,8 @@ package org.aion.api.server.types; -import org.aion.base.type.IBlock; -import org.aion.base.type.ITransaction; -import org.aion.base.util.TypeConverter; -import org.aion.mcf.vm.types.Log; +import org.aion.interfaces.block.Block; +import org.aion.interfaces.tx.Transaction; +import org.aion.util.string.StringUtils; import org.aion.vm.api.interfaces.IExecutionLog; public class TxRecptLg { @@ -27,21 +26,21 @@ public class TxRecptLg { // true when the log was removed, due to a chain reorganization. false if its a valid log. public boolean removed; - public <TX extends ITransaction> TxRecptLg( - IExecutionLog logInfo, IBlock b, Integer txIndex, TX tx, int logIdx, boolean isMainchain) { - this.logIndex = TypeConverter.toJsonHex(logIdx); - this.blockNumber = b == null ? null : TypeConverter.toJsonHex(b.getNumber()); - this.blockHash = b == null ? null : TypeConverter.toJsonHex(b.getHash()); + public <TX extends Transaction> TxRecptLg( + IExecutionLog logInfo, Block b, Integer txIndex, TX tx, int logIdx, boolean isMainchain) { + this.logIndex = StringUtils.toJsonHex(logIdx); + this.blockNumber = b == null ? null : StringUtils.toJsonHex(b.getNumber()); + this.blockHash = b == null ? null : StringUtils.toJsonHex(b.getHash()); this.transactionIndex = - (b == null || txIndex == null) ? null : TypeConverter.toJsonHex(txIndex); - this.transactionHash = TypeConverter.toJsonHex(tx.getTransactionHash()); - this.address = TypeConverter.toJsonHex(logInfo.getSourceAddress().toString()); - this.data = TypeConverter.toJsonHex(logInfo.getData()); + (b == null || txIndex == null) ? null : StringUtils.toJsonHex(txIndex); + this.transactionHash = StringUtils.toJsonHex(tx.getTransactionHash()); + this.address = StringUtils.toJsonHex(logInfo.getSourceAddress().toString()); + this.data = StringUtils.toJsonHex(logInfo.getData()); this.removed = !isMainchain; this.topics = new String[logInfo.getTopics().size()]; for (int i = 0, m = this.topics.length; i < m; i++) { - this.topics[i] = TypeConverter.toJsonHex(logInfo.getTopics().get(i)); + this.topics[i] = StringUtils.toJsonHex(logInfo.getTopics().get(i)); } } } diff --git a/modApiServer/src/org/aion/api/server/zmq/HdlrZmq.java b/modApiServer/src/org/aion/api/server/zmq/HdlrZmq.java index 27824e3177..a8ef01b4b3 100644 --- a/modApiServer/src/org/aion/api/server/zmq/HdlrZmq.java +++ b/modApiServer/src/org/aion/api/server/zmq/HdlrZmq.java @@ -9,8 +9,7 @@ import org.aion.api.server.pb.TxWaitingMappingUpdate; import org.aion.api.server.types.Fltr; import org.aion.api.server.types.TxPendingStatus; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.NativeLoader; +import org.aion.types.ByteArrayWrapper; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.slf4j.Logger; diff --git a/modApiServer/src/org/aion/api/server/zmq/ProtocolProcessor.java b/modApiServer/src/org/aion/api/server/zmq/ProtocolProcessor.java index 89fb52200a..af60e7e90d 100644 --- a/modApiServer/src/org/aion/api/server/zmq/ProtocolProcessor.java +++ b/modApiServer/src/org/aion/api/server/zmq/ProtocolProcessor.java @@ -22,11 +22,11 @@ import org.aion.api.server.types.EvtContract; import org.aion.api.server.types.Fltr; import org.aion.api.server.types.TxPendingStatus; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.Hex; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.aion.mcf.config.CfgApiZmq; +import org.aion.util.bytes.ByteUtil; +import org.aion.util.conversions.Hex; import org.slf4j.Logger; import org.zeromq.ZMQ; import org.zeromq.ZMQ.Context; @@ -155,7 +155,7 @@ public void run() { } private void loadCurveKeyPair() { - List<File> files = org.aion.base.io.File.getFiles(PATH); + List<File> files = org.aion.util.file.File.getFiles(PATH); String nextLoad = ""; for (File f : files) { if (f.getName().contains("zmqCurvePubkey")) { diff --git a/modApiServer/test/org/aion/api/server/ApiAionTest.java b/modApiServer/test/org/aion/api/server/ApiAionTest.java index b4b3cd0737..a0015e31f8 100644 --- a/modApiServer/test/org/aion/api/server/ApiAionTest.java +++ b/modApiServer/test/org/aion/api/server/ApiAionTest.java @@ -16,9 +16,9 @@ import java.util.Map; import org.aion.api.server.types.ArgTxCall; import org.aion.api.server.types.SyncInfo; -import org.aion.base.type.AionAddress; -import org.aion.base.type.ITransaction; -import org.aion.base.type.ITxReceipt; +import org.aion.interfaces.tx.Transaction; +import org.aion.interfaces.tx.TxReceipt; +import org.aion.types.Address; import org.aion.crypto.ed25519.ECKeyEd25519; import org.aion.evtmgr.impl.evt.EventBlock; import org.aion.evtmgr.impl.evt.EventDummy; @@ -26,7 +26,7 @@ import org.aion.mcf.account.AccountManager; import org.aion.mcf.account.Keystore; import org.aion.mcf.blockchain.TxResponse; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.impl.blockchain.AionImpl; import org.aion.zero.impl.config.CfgAion; import org.aion.zero.impl.db.AionBlockStore; @@ -54,12 +54,12 @@ protected void onBlock(AionBlockSummary cbs) { } @Override - protected void pendingTxReceived(ITransaction _tx) { + protected void pendingTxReceived(Transaction _tx) { pendingRcvdFlag = true; } @Override - protected void pendingTxUpdate(ITxReceipt _txRcpt, EventTx.STATE _state) { + protected void pendingTxUpdate(TxReceipt _txRcpt, EventTx.STATE _state) { pendingUpdateFlag = true; } @@ -77,7 +77,7 @@ private ApiAionImpl(AionImpl impl) { private void addEvents() { EventTx pendingRcvd = new EventTx(EventTx.CALLBACK.PENDINGTXRECEIVED0); AionTransaction tx = new AionTransaction(null); - List l1 = new ArrayList<ITransaction>(); + List l1 = new ArrayList<Transaction>(); l1.add(tx); l1.add(tx); l1.add(tx); @@ -230,9 +230,9 @@ public void testGetTransactions() { byte[] msg = "test message".getBytes(); AionTransaction tx = new AionTransaction( - repo.getNonce(AionAddress.ZERO_ADDRESS()).toByteArray(), - AionAddress.ZERO_ADDRESS(), - AionAddress.ZERO_ADDRESS(), + repo.getNonce(Address.ZERO_ADDRESS()).toByteArray(), + Address.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), BigInteger.ONE.toByteArray(), msg, 100000, @@ -261,7 +261,7 @@ public void testGetTransactions() { 1, api.getTransactionCount( blk.getTransactionsList().get(0).getSenderAddress(), blk.getNumber())); - assertEquals(0, api.getTransactionCount(AionAddress.EMPTY_ADDRESS(), blk.getNumber())); + assertEquals(0, api.getTransactionCount(null, blk.getNumber())); assertEquals(tx, api.getTransactionByHash(tx.getTransactionHash())); } @@ -270,14 +270,14 @@ public void testGetTransactions() { public void testDoCall() { byte[] msg = "test message".getBytes(); - Address addr = new AionAddress(Keystore.create("testPwd")); + Address addr = new Address(Keystore.create("testPwd")); AccountManager.inst().unlockAccount(addr, "testPwd", 50000); AionTransaction tx = new AionTransaction( - repo.getNonce(AionAddress.ZERO_ADDRESS()).toByteArray(), + repo.getNonce(Address.ZERO_ADDRESS()).toByteArray(), addr, - AionAddress.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), BigInteger.ONE.toByteArray(), msg, 100000, @@ -287,7 +287,7 @@ public void testDoCall() { ArgTxCall txcall = new ArgTxCall( addr, - AionAddress.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), msg, repo.getNonce(addr), BigInteger.ONE, @@ -302,15 +302,15 @@ public void testDoCall() { public void testEstimates() { byte[] msg = "test message".getBytes(); - Address addr = new AionAddress(Keystore.create("testPwd")); + Address addr = new Address(Keystore.create("testPwd")); AccountManager.inst().unlockAccount(addr, "testPwd", 50000); AionTransaction tx = new AionTransaction( - repo.getNonce(AionAddress.ZERO_ADDRESS()).toByteArray(), + repo.getNonce(Address.ZERO_ADDRESS()).toByteArray(), addr, - AionAddress.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), BigInteger.ONE.toByteArray(), msg, 100000, @@ -320,7 +320,7 @@ public void testEstimates() { ArgTxCall txcall = new ArgTxCall( addr, - AionAddress.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), msg, repo.getNonce(addr), BigInteger.ONE, @@ -346,7 +346,7 @@ public void testCreateContract() { txcall = new ArgTxCall( null, - AionAddress.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), msg, BigInteger.ONE, BigInteger.ONE, @@ -357,8 +357,8 @@ public void testCreateContract() { txcall = new ArgTxCall( - AionAddress.EMPTY_ADDRESS(), - AionAddress.ZERO_ADDRESS(), + null, + Address.ZERO_ADDRESS(), msg, BigInteger.ONE, BigInteger.ONE, @@ -369,12 +369,12 @@ public void testCreateContract() { // locked account should throw INVALID_ACCOUNT - Address addr = new AionAddress(Keystore.create("testPwd")); + Address addr = new Address(Keystore.create("testPwd")); txcall = new ArgTxCall( addr, - AionAddress.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), msg, repo.getNonce(addr), BigInteger.ONE, @@ -387,17 +387,17 @@ public void testCreateContract() { @Test public void testAccountGetters() { assertEquals( - repo.getBalance(AionAddress.ZERO_ADDRESS()), - api.getBalance(AionAddress.ZERO_ADDRESS())); + repo.getBalance(Address.ZERO_ADDRESS()), + api.getBalance(Address.ZERO_ADDRESS())); assertEquals( - repo.getNonce(AionAddress.ZERO_ADDRESS()), - api.getNonce(AionAddress.ZERO_ADDRESS())); + repo.getNonce(Address.ZERO_ADDRESS()), + api.getNonce(Address.ZERO_ADDRESS())); assertEquals( - repo.getBalance(AionAddress.ZERO_ADDRESS()), - api.getBalance(AionAddress.ZERO_ADDRESS().toString())); + repo.getBalance(Address.ZERO_ADDRESS()), + api.getBalance(Address.ZERO_ADDRESS().toString())); assertEquals( - repo.getNonce(AionAddress.ZERO_ADDRESS()), - api.getNonce(AionAddress.ZERO_ADDRESS().toString())); + repo.getNonce(Address.ZERO_ADDRESS()), + api.getNonce(Address.ZERO_ADDRESS().toString())); } @Test @@ -416,7 +416,7 @@ public void testSendTransaction() { txcall = new ArgTxCall( null, - AionAddress.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), msg, BigInteger.ONE, BigInteger.ONE, @@ -427,8 +427,8 @@ public void testSendTransaction() { txcall = new ArgTxCall( - AionAddress.EMPTY_ADDRESS(), - AionAddress.ZERO_ADDRESS(), + null, + Address.ZERO_ADDRESS(), msg, BigInteger.ONE, BigInteger.ONE, @@ -439,12 +439,12 @@ public void testSendTransaction() { // locked account should throw INVALID_ACCOUNT - Address addr = new AionAddress(Keystore.create("testPwd")); + Address addr = new Address(Keystore.create("testPwd")); txcall = new ArgTxCall( addr, - AionAddress.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), msg, repo.getNonce(addr), BigInteger.ONE, @@ -463,7 +463,7 @@ public void testSimpleGetters() { assertNotNull(api.getCoinbase()); assertEquals( - repo.getCode(AionAddress.ZERO_ADDRESS()), api.getCode(AionAddress.ZERO_ADDRESS())); + repo.getCode(Address.ZERO_ADDRESS()), api.getCode(Address.ZERO_ADDRESS())); assertEquals(impl.getBlockMiner().isMining(), api.isMining()); assertArrayEquals(CfgAion.inst().getNodes(), api.getBootNodes()); assertEquals(impl.getAionHub().getP2pMgr().getActiveNodes().size(), api.peerCount()); diff --git a/modApiServer/test/org/aion/api/server/ApiTest.java b/modApiServer/test/org/aion/api/server/ApiTest.java index b84cdeac9b..83d9476aa5 100644 --- a/modApiServer/test/org/aion/api/server/ApiTest.java +++ b/modApiServer/test/org/aion/api/server/ApiTest.java @@ -12,7 +12,7 @@ import java.math.BigInteger; import java.util.Map; import org.aion.api.server.types.CompiledContr; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.mcf.account.AccountManager; import org.aion.mcf.account.Keystore; import org.aion.mcf.types.AbstractBlock; @@ -110,9 +110,9 @@ public void tearDown() { @Test public void testLockAndUnlock() { - assertFalse(api.unlockAccount(AionAddress.ZERO_ADDRESS(), "testPassword", 0)); - assertFalse(api.unlockAccount(AionAddress.ZERO_ADDRESS().toString(), "testPassword", 0)); - assertFalse(api.lockAccount(AionAddress.ZERO_ADDRESS(), "testPassword")); + assertFalse(api.unlockAccount(Address.ZERO_ADDRESS(), "testPassword", 0)); + assertFalse(api.unlockAccount(Address.ZERO_ADDRESS().toString(), "testPassword", 0)); + assertFalse(api.lockAccount(Address.ZERO_ADDRESS(), "testPassword")); addr = Keystore.create("testPwd"); assertTrue(api.unlockAccount(addr, "testPwd", 50000)); @@ -121,10 +121,10 @@ public void testLockAndUnlock() { @Test public void testAccountRetrieval() { - assertNull(api.getAccountKey(AionAddress.ZERO_ADDRESS().toString())); + assertNull(api.getAccountKey(Address.ZERO_ADDRESS().toString())); addr = Keystore.create("testPwd"); - assertEquals(AccountManager.inst().getKey(AionAddress.wrap(addr)), api.getAccountKey(addr)); + assertEquals(AccountManager.inst().getKey(Address.wrap(addr)), api.getAccountKey(addr)); assertTrue(api.getAccounts().contains(addr)); tearDown(); diff --git a/modApiServer/test/org/aion/api/server/ApiUtilTest.java b/modApiServer/test/org/aion/api/server/ApiUtilTest.java index 459816e77c..042f2c0005 100644 --- a/modApiServer/test/org/aion/api/server/ApiUtilTest.java +++ b/modApiServer/test/org/aion/api/server/ApiUtilTest.java @@ -5,7 +5,7 @@ import static org.junit.Assert.assertNull; import java.util.Arrays; -import org.aion.base.util.ByteUtil; +import org.aion.util.bytes.ByteUtil; import org.apache.commons.lang3.RandomUtils; import org.junit.Test; diff --git a/modApiServer/test/org/aion/api/server/TxRecptLgTest.java b/modApiServer/test/org/aion/api/server/TxRecptLgTest.java index 481c5b1bda..80a1970dca 100644 --- a/modApiServer/test/org/aion/api/server/TxRecptLgTest.java +++ b/modApiServer/test/org/aion/api/server/TxRecptLgTest.java @@ -8,13 +8,14 @@ import java.math.BigInteger; import java.util.List; import org.aion.api.server.types.TxRecptLg; -import org.aion.base.util.ByteUtil; import org.aion.crypto.ECKey; import org.aion.crypto.HashUtil; import org.aion.mcf.core.ImportResult; import org.aion.solidity.CompilationResult; import org.aion.solidity.Compiler; -import org.aion.vm.api.interfaces.Address; + +import org.aion.types.Address; +import org.aion.util.bytes.ByteUtil; import org.aion.vm.api.interfaces.IExecutionLog; import org.aion.zero.impl.BlockContext; import org.aion.zero.impl.StandaloneBlockchain; diff --git a/modApiServer/test/org/aion/api/server/pb/ApiAion0Test.java b/modApiServer/test/org/aion/api/server/pb/ApiAion0Test.java index 02cc4c0ca3..e9c3955621 100644 --- a/modApiServer/test/org/aion/api/server/pb/ApiAion0Test.java +++ b/modApiServer/test/org/aion/api/server/pb/ApiAion0Test.java @@ -15,14 +15,14 @@ import java.util.Arrays; import java.util.Collections; import org.aion.api.server.ApiUtil; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteUtil; -import org.aion.base.util.TypeConverter; +import org.aion.types.Address; import org.aion.crypto.ed25519.ECKeyEd25519; import org.aion.equihash.EquihashMiner; import org.aion.mcf.account.AccountManager; import org.aion.mcf.account.Keystore; -import org.aion.vm.api.interfaces.Address; + +import org.aion.util.bytes.ByteUtil; +import org.aion.util.string.StringUtils; import org.aion.zero.impl.Version; import org.aion.zero.impl.blockchain.AionImpl; import org.aion.zero.impl.config.CfgAion; @@ -181,7 +181,7 @@ public void testProcessMinerAddress() throws Exception { Message.rsp_minerAddress ma = Message.rsp_minerAddress.parseFrom(stripHeader(rsp)); assertEquals( - ByteString.copyFrom(TypeConverter.StringHexToByteArray(api.getCoinbase())), + ByteString.copyFrom(StringUtils.StringHexToByteArray(api.getCoinbase())), ma.getMinerAddr()); rsp = sendRequest(Message.Servs.s_hb_VALUE, Message.Funcs.f_minerAddress_VALUE); @@ -191,7 +191,7 @@ public void testProcessMinerAddress() throws Exception { @Test public void testProcessAccountsValue() throws Exception { - Address addr = new AionAddress(Keystore.create("testPwd")); + Address addr = new Address(Keystore.create("testPwd")); rsp = sendRequest(Message.Servs.s_wallet_VALUE, Message.Funcs.f_accounts_VALUE); @@ -202,7 +202,7 @@ public void testProcessAccountsValue() throws Exception { assertEquals( ByteString.copyFrom( - TypeConverter.StringHexToByteArray((String) api.getAccounts().get(0))), + StringUtils.StringHexToByteArray((String) api.getAccounts().get(0))), accts.getAccout(0)); rsp = sendRequest(Message.Servs.s_hb_VALUE, Message.Funcs.f_accounts_VALUE); @@ -226,7 +226,7 @@ public void testProcessBlockNumber() throws Exception { @Test public void testProcessUnlockAccount() { - Address addr = new AionAddress(Keystore.create("testPwd")); + Address addr = new Address(Keystore.create("testPwd")); AccountManager.inst().unlockAccount(addr, "testPwd", 50000); Message.req_unlockAccount reqBody = @@ -256,7 +256,7 @@ public void testProcessUnlockAccount() { @Test public void testProcessGetBalance() throws Exception { - Address addr = new AionAddress(Keystore.create("testPwd")); + Address addr = new Address(Keystore.create("testPwd")); AccountManager.inst().unlockAccount(addr, "testPwd", 50000); @@ -288,7 +288,7 @@ public void testProcessGetBalance() throws Exception { @Test public void testProcessGetNonce() throws Exception { - Address addr = new AionAddress(Keystore.create("testPwd")); + Address addr = new Address(Keystore.create("testPwd")); AccountManager.inst().unlockAccount(addr, "testPwd", 50000); @@ -397,7 +397,7 @@ public void testProcessCompileFail() { @Test public void testProcessGetCode() throws Exception { - Address addr = new AionAddress(Keystore.create("testPwd")); + Address addr = new Address(Keystore.create("testPwd")); AccountManager.inst().unlockAccount(addr, "testPwd", 50000); @@ -433,9 +433,9 @@ public void testProcessGetTR() throws Exception { AionTransaction tx = new AionTransaction( - repo.getNonce(AionAddress.ZERO_ADDRESS()).toByteArray(), - AionAddress.ZERO_ADDRESS(), - AionAddress.ZERO_ADDRESS(), + repo.getNonce(Address.ZERO_ADDRESS()).toByteArray(), + Address.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), BigInteger.ONE.toByteArray(), msg, 100000, @@ -465,7 +465,7 @@ public void testProcessGetTR() throws Exception { Message.rsp_getTransactionReceipt rslt = Message.rsp_getTransactionReceipt.parseFrom(stripHeader(rsp)); - assertEquals(ByteString.copyFrom(AionAddress.ZERO_ADDRESS().toBytes()), rslt.getTo()); + assertEquals(ByteString.copyFrom(Address.ZERO_ADDRESS().toBytes()), rslt.getTo()); rsp = sendRequest(Message.Servs.s_hb_VALUE, Message.Funcs.f_getTransactionReceipt_VALUE); @@ -474,7 +474,7 @@ public void testProcessGetTR() throws Exception { @Test public void testProcessCall() throws Exception { - Address addr = new AionAddress(Keystore.create("testPwd")); + Address addr = new Address(Keystore.create("testPwd")); AccountManager.inst().unlockAccount(addr, "testPwd", 50000); @@ -484,7 +484,7 @@ public void testProcessCall() throws Exception { .setData(ByteString.copyFrom(msg)) .setFrom(ByteString.copyFrom(addr.toBytes())) .setValue(ByteString.copyFrom("1234".getBytes())) - .setTo(ByteString.copyFrom(AionAddress.ZERO_ADDRESS().toBytes())) + .setTo(ByteString.copyFrom(Address.ZERO_ADDRESS().toBytes())) .build(); rsp = @@ -563,9 +563,9 @@ public void testProcessGetTxByBlockHashAndIndex() throws Exception { AionTransaction tx = new AionTransaction( - repo.getNonce(AionAddress.ZERO_ADDRESS()).toByteArray(), - AionAddress.ZERO_ADDRESS(), - AionAddress.ZERO_ADDRESS(), + repo.getNonce(Address.ZERO_ADDRESS()).toByteArray(), + Address.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), BigInteger.ONE.toByteArray(), msg, 100000, @@ -617,9 +617,9 @@ public void testProcessGetTxByBlockNumberAndIndex() throws Exception { AionTransaction tx = new AionTransaction( - repo.getNonce(AionAddress.ZERO_ADDRESS()).toByteArray(), - AionAddress.ZERO_ADDRESS(), - AionAddress.ZERO_ADDRESS(), + repo.getNonce(Address.ZERO_ADDRESS()).toByteArray(), + Address.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), BigInteger.ONE.toByteArray(), msg, 100000, @@ -671,9 +671,9 @@ public void testProcessGetBlockTxCountByNumber() throws Exception { AionTransaction tx = new AionTransaction( - repo.getNonce(AionAddress.ZERO_ADDRESS()).toByteArray(), - AionAddress.ZERO_ADDRESS(), - AionAddress.ZERO_ADDRESS(), + repo.getNonce(Address.ZERO_ADDRESS()).toByteArray(), + Address.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), BigInteger.ONE.toByteArray(), msg, 100000, @@ -723,9 +723,9 @@ public void testProcessGetBlockTxCountByHash() throws Exception { AionTransaction tx = new AionTransaction( - repo.getNonce(AionAddress.ZERO_ADDRESS()).toByteArray(), - AionAddress.ZERO_ADDRESS(), - AionAddress.ZERO_ADDRESS(), + repo.getNonce(Address.ZERO_ADDRESS()).toByteArray(), + Address.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), BigInteger.ONE.toByteArray(), msg, 100000, @@ -775,9 +775,9 @@ public void testProcessGetTxByHash() throws Exception { AionTransaction tx = new AionTransaction( - repo.getNonce(AionAddress.ZERO_ADDRESS()).toByteArray(), - AionAddress.ZERO_ADDRESS(), - AionAddress.ZERO_ADDRESS(), + repo.getNonce(Address.ZERO_ADDRESS()).toByteArray(), + Address.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), BigInteger.ONE.toByteArray(), msg, 100000, @@ -827,9 +827,9 @@ public void testProcessGetTxCount() throws Exception { AionTransaction tx = new AionTransaction( - repo.getNonce(AionAddress.ZERO_ADDRESS()).toByteArray(), - AionAddress.ZERO_ADDRESS(), - AionAddress.ZERO_ADDRESS(), + repo.getNonce(Address.ZERO_ADDRESS()).toByteArray(), + Address.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), BigInteger.ONE.toByteArray(), msg, 100000, @@ -978,13 +978,13 @@ public void testProcessAccountCreateAndLock() throws Exception { ByteString addr = rslt.getAddress(0); assertTrue( api.unlockAccount( - AionAddress.wrap(rslt.getAddress(0).toByteArray()), "passwd0", 500)); + Address.wrap(rslt.getAddress(0).toByteArray()), "passwd0", 500)); assertTrue( api.unlockAccount( - AionAddress.wrap(rslt.getAddress(1).toByteArray()), "passwd1", 500)); + Address.wrap(rslt.getAddress(1).toByteArray()), "passwd1", 500)); assertTrue( api.unlockAccount( - AionAddress.wrap(rslt.getAddress(2).toByteArray()), "passwd2", 500)); + Address.wrap(rslt.getAddress(2).toByteArray()), "passwd2", 500)); rsp = sendRequest(Message.Servs.s_hb_VALUE, Message.Funcs.f_accountCreate_VALUE); @@ -1043,8 +1043,8 @@ public void testProcessEstimateNrg() throws Exception { Message.req_estimateNrg reqBody = Message.req_estimateNrg .newBuilder() - .setFrom(ByteString.copyFrom(AionAddress.ZERO_ADDRESS().toBytes())) - .setTo(ByteString.copyFrom(AionAddress.ZERO_ADDRESS().toBytes())) + .setFrom(ByteString.copyFrom(Address.ZERO_ADDRESS().toBytes())) + .setTo(ByteString.copyFrom(Address.ZERO_ADDRESS().toBytes())) .setNrg(1000) .setNrgPrice(5000) .setData(ByteString.copyFrom(msg)) @@ -1064,10 +1064,10 @@ public void testProcessEstimateNrg() throws Exception { AionTransaction tx = new AionTransaction( AionRepositoryImpl.inst() - .getNonce(AionAddress.ZERO_ADDRESS()) + .getNonce(Address.ZERO_ADDRESS()) .toByteArray(), - AionAddress.ZERO_ADDRESS(), - AionAddress.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), val, msg, 1000, @@ -1083,10 +1083,10 @@ public void testProcessEstimateNrg() throws Exception { @Test public void testProcessExportAccounts() throws Exception { - Address addr1 = new AionAddress(Keystore.create("testPwd1")); + Address addr1 = new Address(Keystore.create("testPwd1")); AccountManager.inst().unlockAccount(addr1, "testPwd1", 50000); - Address addr2 = new AionAddress(Keystore.create("testPwd2")); + Address addr2 = new Address(Keystore.create("testPwd2")); AccountManager.inst().unlockAccount(addr2, "testPwd12", 50000); Message.t_Key tkey1 = @@ -1163,10 +1163,10 @@ public void testProcessImportAccounts() throws Exception { @Test public void testProcessEventRegister() throws Exception { - Address addr1 = new AionAddress(Keystore.create("testPwd1")); + Address addr1 = new Address(Keystore.create("testPwd1")); AccountManager.inst().unlockAccount(addr1, "testPwd1", 50000); - Address addr2 = new AionAddress(Keystore.create("testPwd2")); + Address addr2 = new Address(Keystore.create("testPwd2")); AccountManager.inst().unlockAccount(addr2, "testPwd12", 50000); Message.t_FilterCt fil1 = @@ -1368,14 +1368,14 @@ public void testProcessBlocksLatest() throws Exception { @Test public void testProcessAccountDetails() throws Exception { - Address addr = new AionAddress(Keystore.create("testPwd")); + Address addr = new Address(Keystore.create("testPwd")); AccountManager.inst().unlockAccount(addr, "testPwd", 50000); Message.req_getAccountDetailsByAddressList reqBody = Message.req_getAccountDetailsByAddressList .newBuilder() .addAddresses(ByteString.copyFrom(addr.toBytes())) - .addAddresses(ByteString.copyFrom(AionAddress.ZERO_ADDRESS().toBytes())) + .addAddresses(ByteString.copyFrom(Address.ZERO_ADDRESS().toBytes())) .build(); rsp = diff --git a/modApiServer/test/org/aion/api/server/rpc/ApiWeb3AionTest.java b/modApiServer/test/org/aion/api/server/rpc/ApiWeb3AionTest.java index fbd3832847..605ba5db03 100644 --- a/modApiServer/test/org/aion/api/server/rpc/ApiWeb3AionTest.java +++ b/modApiServer/test/org/aion/api/server/rpc/ApiWeb3AionTest.java @@ -1,14 +1,14 @@ package org.aion.api.server.rpc; -import static org.aion.base.util.TypeConverter.StringHexToBigInteger; +import static org.aion.util.string.StringUtils.StringHexToBigInteger; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.mcf.account.AccountManager; import org.aion.mcf.account.Keystore; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.impl.blockchain.AionImpl; import org.aion.zero.impl.blockchain.AionPendingStateImpl; import org.json.JSONArray; @@ -29,11 +29,11 @@ public void setup() { @Test public void testEthSignTransaction() { - Address addr = new AionAddress(Keystore.create("testPwd")); + Address addr = new Address(Keystore.create("testPwd")); AccountManager.inst().unlockAccount(addr, "testPwd", 50000); - Address toAddr = new AionAddress(Keystore.create("testPwd")); + Address toAddr = new Address(Keystore.create("testPwd")); JSONObject tx = new JSONObject(); tx.put("from", "0x" + addr.toString()); @@ -78,11 +78,11 @@ public void testEthSignTransaction() { @Test public void testEthSignTransactionAddressParamIsNull() { - Address addr = new AionAddress(Keystore.create("testPwd")); + Address addr = new Address(Keystore.create("testPwd")); AccountManager.inst().unlockAccount(addr, "testPwd", 50000); - Address toAddr = new AionAddress(Keystore.create("testPwd")); + Address toAddr = new Address(Keystore.create("testPwd")); JSONObject tx = new JSONObject(); tx.put("from", addr.toString()); @@ -106,9 +106,9 @@ public void testEthSignTransactionAddressParamIsNull() { @Test public void testEthSignTransactionAccountNotUnlocked() { - Address addr = new AionAddress(Keystore.create("testPwd")); + Address addr = new Address(Keystore.create("testPwd")); - Address toAddr = new AionAddress(Keystore.create("testPwd")); + Address toAddr = new Address(Keystore.create("testPwd")); JSONObject tx = new JSONObject(); tx.put("from", addr.toString()); @@ -132,9 +132,9 @@ public void testEthSignTransactionAccountNotUnlocked() { @Test public void testEthSendTransactionAccountNotUnlocked() { - Address addr = new AionAddress(Keystore.create("testPwd")); + Address addr = new Address(Keystore.create("testPwd")); - Address toAddr = new AionAddress(Keystore.create("testPwd")); + Address toAddr = new Address(Keystore.create("testPwd")); JSONObject tx = new JSONObject(); tx.put("from", addr.toString()); @@ -159,28 +159,28 @@ public void testEthSendTransactionAccountNotUnlocked() { @Test public void testEthGetTransactionCountPending() { JSONObject req = new JSONObject(); - req.put("address", AionAddress.ZERO_ADDRESS().toString()); + req.put("address", Address.ZERO_ADDRESS().toString()); req.put("block", "pending"); RpcMsg rsp = web3Api.eth_getTransactionCount(req); assertNull(rsp.getError()); assertEquals( - impl.getPendingState().getNonce(AionAddress.ZERO_ADDRESS()), + impl.getPendingState().getNonce(Address.ZERO_ADDRESS()), StringHexToBigInteger(rsp.getResult().toString())); } @Test public void testEthGetBalancePending() { JSONObject req = new JSONObject(); - req.put("address", AionAddress.ZERO_ADDRESS().toString()); + req.put("address", Address.ZERO_ADDRESS().toString()); req.put("block", "pending"); RpcMsg rsp = web3Api.eth_getBalance(req); assertNull(rsp.getError()); assertEquals( - impl.getPendingState().getBalance(AionAddress.ZERO_ADDRESS()), + impl.getPendingState().getBalance(Address.ZERO_ADDRESS()), StringHexToBigInteger(rsp.getResult().toString())); } diff --git a/modApiServer/test/org/aion/api/server/types/ArgTxCallTest.java b/modApiServer/test/org/aion/api/server/types/ArgTxCallTest.java index bf10b425ae..e7ce1e8447 100644 --- a/modApiServer/test/org/aion/api/server/types/ArgTxCallTest.java +++ b/modApiServer/test/org/aion/api/server/types/ArgTxCallTest.java @@ -1,11 +1,12 @@ package org.aion.api.server.types; import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertNull; import static org.aion.mcf.vm.Constants.NRG_TRANSACTION_DEFAULT; import static org.aion.mcf.vm.Constants.NRG_CREATE_CONTRACT_DEFAULT; import java.math.BigInteger; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.json.JSONObject; import org.junit.Test; @@ -18,8 +19,8 @@ public void testFromJsonContractCreateDefaults() { JSONObject tx = new JSONObject(); ArgTxCall txCall = ArgTxCall.fromJSON(tx, nrgPrice); - assertEquals(AionAddress.EMPTY_ADDRESS(), txCall.getFrom()); - assertEquals(AionAddress.EMPTY_ADDRESS(), txCall.getTo()); + assertNull(txCall.getFrom()); + assertNull(txCall.getTo()); assertEquals(0, txCall.getData().length); assertEquals(BigInteger.ZERO, txCall.getNonce()); assertEquals(BigInteger.ZERO, txCall.getValue()); @@ -38,8 +39,8 @@ public void testFromJsonTxDefaults() { tx.put("to", toAddr); ArgTxCall txCall = ArgTxCall.fromJSON(tx, nrgPrice); - assertEquals(AionAddress.EMPTY_ADDRESS(), txCall.getFrom()); - assertEquals(new AionAddress(toAddr), txCall.getTo()); + assertNull(txCall.getFrom()); + assertEquals(new Address(toAddr), txCall.getTo()); assertEquals(0, txCall.getData().length); assertEquals(BigInteger.ZERO, txCall.getNonce()); assertEquals(BigInteger.ZERO, txCall.getValue()); diff --git a/modBoot/build.gradle b/modBoot/build.gradle index c702eb23ca..e23efdc52d 100644 --- a/modBoot/build.gradle +++ b/modBoot/build.gradle @@ -1,7 +1,9 @@ ext.moduleName = 'aion.boot' dependencies { - compile project(':modAionBase') + compile 'network.aion:vm-api4j:0.4.0' + compile 'network.aion:util4j:0.4.0' + compile project(':modCrypto') compile project(':modApiServer') compile project(':modAionImpl') @@ -11,8 +13,6 @@ dependencies { compile project(':modMcf') compile project(':modTxPoolImpl') compile project(':3rdParty.libnzmq') - compile 'com.madgag.spongycastle:prov:1.58.0.0' - compile 'com.madgag.spongycastle:core:1.58.0.0' compile 'org.slf4j:slf4j-api:1.7.25' testCompile 'junit:junit:4.12' diff --git a/modBoot/src/module-info.java b/modBoot/src/module-info.java index d197e37923..b98965e90e 100644 --- a/modBoot/src/module-info.java +++ b/modBoot/src/module-info.java @@ -14,8 +14,8 @@ uses org.aion.evtmgr.EventMgrModule; uses org.aion.log.AionLoggerFactory; - requires aion.base; requires aion.vm; + requires aion.util; exports org.aion; } diff --git a/modBoot/src/org/aion/Aion.java b/modBoot/src/org/aion/Aion.java index da4b3bd892..e5d1267c3f 100644 --- a/modBoot/src/org/aion/Aion.java +++ b/modBoot/src/org/aion/Aion.java @@ -414,7 +414,7 @@ private static void checkZmqKeyPair() throws IOException { } private static boolean existZmqSecKeyFile(final Path path) { - List<File> files = org.aion.base.io.File.getFiles(path); + List<File> files = org.aion.util.file.File.getFiles(path); for (File file : files) { if (file.getName().contains("zmqCurveSeckey")) { diff --git a/modCrypto/build.gradle b/modCrypto/build.gradle index db167c0a1b..a0bd4217c8 100644 --- a/modCrypto/build.gradle +++ b/modCrypto/build.gradle @@ -1,7 +1,8 @@ ext.moduleName = 'aion.crypto' dependencies { - compile project(':modUtil') + compile 'network.aion:util4j:0.4.0' + compile project(':modRlp') compile 'com.madgag.spongycastle:prov:1.58.0.0' compile 'com.madgag.spongycastle:core:1.58.0.0' diff --git a/modCrypto/src/org/aion/crypto/HashUtil.java b/modCrypto/src/org/aion/crypto/HashUtil.java index 0f6467e228..114cfd5d43 100644 --- a/modCrypto/src/org/aion/crypto/HashUtil.java +++ b/modCrypto/src/org/aion/crypto/HashUtil.java @@ -12,7 +12,7 @@ import org.aion.crypto.hash.Blake2b; import org.aion.crypto.hash.Blake2bNative; import org.aion.rlp.RLP; -import org.aion.util.NativeLoader; +import org.aion.util.file.NativeLoader; import org.spongycastle.crypto.Digest; import org.spongycastle.crypto.digests.KeccakDigest; import org.spongycastle.crypto.digests.RIPEMD160Digest; diff --git a/modCrypto/src/org/aion/crypto/ed25519/ECKeyEd25519.java b/modCrypto/src/org/aion/crypto/ed25519/ECKeyEd25519.java index 9de63422f5..e6040a6135 100644 --- a/modCrypto/src/org/aion/crypto/ed25519/ECKeyEd25519.java +++ b/modCrypto/src/org/aion/crypto/ed25519/ECKeyEd25519.java @@ -4,8 +4,8 @@ import org.aion.crypto.AddressSpecs; import org.aion.crypto.ECKey; import org.aion.crypto.ISignature; -import org.aion.util.NativeLoader; import org.aion.util.bytes.ByteUtil; +import org.aion.util.file.NativeLoader; import org.libsodium.jni.NaCl; import org.libsodium.jni.Sodium; import org.spongycastle.util.encoders.Hex; diff --git a/modDbImpl/build.gradle b/modDbImpl/build.gradle index 3053406d5b..8bd024afee 100644 --- a/modDbImpl/build.gradle +++ b/modDbImpl/build.gradle @@ -14,7 +14,9 @@ sourceSets { } dependencies { - compile project(':modAionBase') + compile 'network.aion:vm-api4j:0.4.0' + compile 'network.aion:util4j:0.4.0' + compile project(':modLogger') compile 'com.google.guava:guava:25.1-jre' compile 'org.slf4j:slf4j-api:1.7.25' diff --git a/modDbImpl/src/module-info.java b/modDbImpl/src/module-info.java index 4569a0f7ad..6b57ada028 100644 --- a/modDbImpl/src/module-info.java +++ b/modDbImpl/src/module-info.java @@ -1,8 +1,8 @@ module aion.db.impl { requires slf4j.api; requires aion.log; - requires aion.base; - requires leveldbjni.all; + requires aion.util; + requires aion.vm.api; requires rocksdbjni; requires h2.mvstore; requires com.google.common; diff --git a/modDbImpl/src/org/aion/db/generic/CacheIteratorWrapper.java b/modDbImpl/src/org/aion/db/generic/CacheIteratorWrapper.java index d95bb3c67c..60d36cf01f 100644 --- a/modDbImpl/src/org/aion/db/generic/CacheIteratorWrapper.java +++ b/modDbImpl/src/org/aion/db/generic/CacheIteratorWrapper.java @@ -4,7 +4,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; /** * A wrapper for the iterator needed by {@link DatabaseWithCache} conforming to the {@link Iterator} diff --git a/modDbImpl/src/org/aion/db/generic/DatabaseWithCache.java b/modDbImpl/src/org/aion/db/generic/DatabaseWithCache.java index 02de10abf7..1659aaf5b9 100644 --- a/modDbImpl/src/org/aion/db/generic/DatabaseWithCache.java +++ b/modDbImpl/src/org/aion/db/generic/DatabaseWithCache.java @@ -10,12 +10,12 @@ import java.util.Iterator; import java.util.Map; import java.util.Optional; -import org.aion.base.db.IByteArrayKeyValueDatabase; -import org.aion.base.db.PersistenceMethod; -import org.aion.base.util.ByteArrayWrapper; import org.aion.db.impl.AbstractDB; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; +import org.aion.interfaces.db.PersistenceMethod; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; +import org.aion.types.ByteArrayWrapper; import org.slf4j.Logger; /** @@ -25,7 +25,7 @@ * @author Alexandra Roatis * @implNote Assumes persistent database. Overwrite method if this is not the case. */ -public class DatabaseWithCache implements IByteArrayKeyValueDatabase { +public class DatabaseWithCache implements ByteArrayKeyValueDatabase { private static final Logger LOG = AionLoggerFactory.getLogger(LogEnum.DB.name()); diff --git a/modDbImpl/src/org/aion/db/generic/LockedDatabase.java b/modDbImpl/src/org/aion/db/generic/LockedDatabase.java index 2d33ff1501..6b1448672b 100644 --- a/modDbImpl/src/org/aion/db/generic/LockedDatabase.java +++ b/modDbImpl/src/org/aion/db/generic/LockedDatabase.java @@ -6,8 +6,8 @@ import java.util.Optional; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import org.aion.base.db.IByteArrayKeyValueDatabase; -import org.aion.base.db.PersistenceMethod; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; +import org.aion.interfaces.db.PersistenceMethod; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.slf4j.Logger; @@ -20,17 +20,17 @@ * * @author Alexandra Roatis */ -public class LockedDatabase implements IByteArrayKeyValueDatabase { +public class LockedDatabase implements ByteArrayKeyValueDatabase { /** Unlocked database. */ - protected final IByteArrayKeyValueDatabase database; + protected final ByteArrayKeyValueDatabase database; /** Read-write lock allowing concurrent reads and single write operations. */ protected final ReadWriteLock lock = new ReentrantReadWriteLock(); protected static final Logger LOG = AionLoggerFactory.getLogger(LogEnum.DB.name()); - public LockedDatabase(IByteArrayKeyValueDatabase _unlockedDatabase) { + public LockedDatabase(ByteArrayKeyValueDatabase _unlockedDatabase) { this.database = _unlockedDatabase; } diff --git a/modDbImpl/src/org/aion/db/generic/SpecialLockedDatabase.java b/modDbImpl/src/org/aion/db/generic/SpecialLockedDatabase.java index 942d5701ff..4b6b514701 100644 --- a/modDbImpl/src/org/aion/db/generic/SpecialLockedDatabase.java +++ b/modDbImpl/src/org/aion/db/generic/SpecialLockedDatabase.java @@ -2,7 +2,7 @@ import java.util.Collection; import java.util.Map; -import org.aion.base.db.IByteArrayKeyValueDatabase; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; /** * Implements locking functionality for a database that is mostly thread-safe except for open and @@ -10,9 +10,9 @@ * * @author Alexandra Roatis */ -public class SpecialLockedDatabase extends LockedDatabase implements IByteArrayKeyValueDatabase { +public class SpecialLockedDatabase extends LockedDatabase implements ByteArrayKeyValueDatabase { - public SpecialLockedDatabase(IByteArrayKeyValueDatabase _unlockedDatabase) { + public SpecialLockedDatabase(ByteArrayKeyValueDatabase _unlockedDatabase) { super(_unlockedDatabase); } diff --git a/modDbImpl/src/org/aion/db/generic/TimedDatabase.java b/modDbImpl/src/org/aion/db/generic/TimedDatabase.java index c47db63659..fee344e29d 100644 --- a/modDbImpl/src/org/aion/db/generic/TimedDatabase.java +++ b/modDbImpl/src/org/aion/db/generic/TimedDatabase.java @@ -4,11 +4,11 @@ import java.util.Iterator; import java.util.Map; import java.util.Optional; -import org.aion.base.db.IByteArrayKeyValueDatabase; -import org.aion.base.db.PersistenceMethod; -import org.aion.base.util.Hex; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; +import org.aion.interfaces.db.PersistenceMethod; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; +import org.aion.util.conversions.Hex; import org.slf4j.Logger; /** @@ -16,14 +16,14 @@ * * @author Alexandra Roatis */ -public class TimedDatabase implements IByteArrayKeyValueDatabase { +public class TimedDatabase implements ByteArrayKeyValueDatabase { /** Unlocked database. */ - protected final IByteArrayKeyValueDatabase database; + protected final ByteArrayKeyValueDatabase database; protected static final Logger LOG = AionLoggerFactory.getLogger(LogEnum.DB.name()); - public TimedDatabase(IByteArrayKeyValueDatabase _database) { + public TimedDatabase(ByteArrayKeyValueDatabase _database) { this.database = _database; } diff --git a/modDbImpl/src/org/aion/db/impl/AbstractDB.java b/modDbImpl/src/org/aion/db/impl/AbstractDB.java index 447f53f11c..813f387548 100644 --- a/modDbImpl/src/org/aion/db/impl/AbstractDB.java +++ b/modDbImpl/src/org/aion/db/impl/AbstractDB.java @@ -8,11 +8,11 @@ import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; -import org.aion.base.db.IByteArrayKeyValueDatabase; -import org.aion.base.db.PersistenceMethod; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; +import org.aion.interfaces.db.PersistenceMethod; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; +import org.aion.types.ByteArrayWrapper; import org.slf4j.Logger; /** @@ -21,7 +21,7 @@ * @author Alexandra Roatis * @implNote Assumes persistent database. Overwrite method if this is not the case. */ -public abstract class AbstractDB implements IByteArrayKeyValueDatabase { +public abstract class AbstractDB implements ByteArrayKeyValueDatabase { protected static final Logger LOG = AionLoggerFactory.getLogger(LogEnum.DB.name()); diff --git a/modDbImpl/src/org/aion/db/impl/DBVendor.java b/modDbImpl/src/org/aion/db/impl/DBVendor.java index 37209f6da7..0bc45bb16b 100644 --- a/modDbImpl/src/org/aion/db/impl/DBVendor.java +++ b/modDbImpl/src/org/aion/db/impl/DBVendor.java @@ -3,8 +3,8 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import org.aion.base.db.PersistenceMethod; import org.aion.db.impl.rocksdb.RocksDBWrapper; +import org.aion.interfaces.db.PersistenceMethod; // @ThreadSafe public enum DBVendor { diff --git a/modDbImpl/src/org/aion/db/impl/DatabaseFactory.java b/modDbImpl/src/org/aion/db/impl/DatabaseFactory.java index ad3363e5ba..367f38d7d4 100644 --- a/modDbImpl/src/org/aion/db/impl/DatabaseFactory.java +++ b/modDbImpl/src/org/aion/db/impl/DatabaseFactory.java @@ -1,7 +1,6 @@ package org.aion.db.impl; import java.util.Properties; -import org.aion.base.db.IByteArrayKeyValueDatabase; import org.aion.db.generic.DatabaseWithCache; import org.aion.db.generic.LockedDatabase; import org.aion.db.generic.SpecialLockedDatabase; @@ -14,12 +13,13 @@ import org.aion.db.impl.mongodb.MongoDB; import org.aion.db.impl.rocksdb.RocksDBConstants; import org.aion.db.impl.rocksdb.RocksDBWrapper; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.slf4j.Logger; /** - * Returns an instance of {@link IByteArrayKeyValueDatabase} based on the given properties. + * Returns an instance of {@link ByteArrayKeyValueDatabase} based on the given properties. * * @author Alexandra Roatis */ @@ -54,14 +54,14 @@ public static class Props { public static final String READ_BUFFER_SIZE = "read_buffer_size"; } - public static IByteArrayKeyValueDatabase connect(Properties info) { + public static ByteArrayKeyValueDatabase connect(Properties info) { return connect(info, false); } - public static IByteArrayKeyValueDatabase connect(Properties info, boolean debug) { + public static ByteArrayKeyValueDatabase connect(Properties info, boolean debug) { DBVendor dbType = DBVendor.fromString(info.getProperty(Props.DB_TYPE)); - IByteArrayKeyValueDatabase db; + ByteArrayKeyValueDatabase db; if (dbType == DBVendor.UNKNOWN) { // the driver, if correct should check path and name @@ -96,7 +96,7 @@ public static IByteArrayKeyValueDatabase connect(Properties info, boolean debug) * * @return A database implementation with read-write locks. */ - private static IByteArrayKeyValueDatabase connectWithLocks(Properties info) { + private static ByteArrayKeyValueDatabase connectWithLocks(Properties info) { boolean enableHeapCache = getBoolean(info, Props.ENABLE_HEAP_CACHE); if (enableHeapCache) { return new LockedDatabase(connectWithCache(info)); @@ -111,7 +111,7 @@ private static IByteArrayKeyValueDatabase connectWithLocks(Properties info) { } /** @return A database implementation with a caching layer. */ - private static IByteArrayKeyValueDatabase connectWithCache(Properties info) { + private static ByteArrayKeyValueDatabase connectWithCache(Properties info) { boolean enableAutoCommit = getBoolean(info, Props.ENABLE_AUTO_COMMIT); return new DatabaseWithCache( connectBasic(info), @@ -208,7 +208,7 @@ private static AbstractDB connectBasic(Properties info) { * @return A database implementation based on a driver implementing the {@link IDriver} * interface. */ - public static IByteArrayKeyValueDatabase connect(String driverName, Properties info) { + public static ByteArrayKeyValueDatabase connect(String driverName, Properties info) { try { // see if the given name is a valid driver IDriver driver = @@ -226,7 +226,7 @@ public static IByteArrayKeyValueDatabase connect(String driverName, Properties i } /** @return A mock database. */ - public static IByteArrayKeyValueDatabase connect(String _dbName) { + public static ByteArrayKeyValueDatabase connect(String _dbName) { return new MockDB(_dbName); } diff --git a/modDbImpl/src/org/aion/db/impl/IDriver.java b/modDbImpl/src/org/aion/db/impl/IDriver.java index aa9affd809..ad9d3d3268 100644 --- a/modDbImpl/src/org/aion/db/impl/IDriver.java +++ b/modDbImpl/src/org/aion/db/impl/IDriver.java @@ -1,6 +1,6 @@ package org.aion.db.impl; -import org.aion.base.db.IByteArrayKeyValueDatabase; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; public interface IDriver { @@ -10,7 +10,7 @@ public interface IDriver { * @param info the parameters for this database, all represented in String. * @return HashMapDB, or null. */ - IByteArrayKeyValueDatabase connect(java.util.Properties info); + ByteArrayKeyValueDatabase connect(java.util.Properties info); /** * Retrieves the driver's major version number. Initially this should be 1. diff --git a/modDbImpl/src/org/aion/db/impl/h2/H2MVMap.java b/modDbImpl/src/org/aion/db/impl/h2/H2MVMap.java index ee8f92699f..9611699da0 100644 --- a/modDbImpl/src/org/aion/db/impl/h2/H2MVMap.java +++ b/modDbImpl/src/org/aion/db/impl/h2/H2MVMap.java @@ -5,7 +5,7 @@ import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; import org.aion.db.impl.AbstractDB; import org.h2.mvstore.FileStore; import org.h2.mvstore.MVMap; diff --git a/modDbImpl/src/org/aion/db/impl/leveldb/LevelDB.java b/modDbImpl/src/org/aion/db/impl/leveldb/LevelDB.java index f1ed18cc24..ad26279b55 100644 --- a/modDbImpl/src/org/aion/db/impl/leveldb/LevelDB.java +++ b/modDbImpl/src/org/aion/db/impl/leveldb/LevelDB.java @@ -6,7 +6,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.Map; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; import org.aion.db.impl.AbstractDB; import org.fusesource.leveldbjni.JniDBFactory; import org.iq80.leveldb.CompressionType; diff --git a/modDbImpl/src/org/aion/db/impl/mockdb/MockDB.java b/modDbImpl/src/org/aion/db/impl/mockdb/MockDB.java index 8af7ad47e3..bfe7de3fb8 100644 --- a/modDbImpl/src/org/aion/db/impl/mockdb/MockDB.java +++ b/modDbImpl/src/org/aion/db/impl/mockdb/MockDB.java @@ -6,8 +6,8 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; -import org.aion.base.db.PersistenceMethod; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.interfaces.db.PersistenceMethod; +import org.aion.types.ByteArrayWrapper; import org.aion.db.impl.AbstractDB; public class MockDB extends AbstractDB { diff --git a/modDbImpl/src/org/aion/db/impl/mockdb/MockDBDriver.java b/modDbImpl/src/org/aion/db/impl/mockdb/MockDBDriver.java index f30ee35321..b195eb943d 100644 --- a/modDbImpl/src/org/aion/db/impl/mockdb/MockDBDriver.java +++ b/modDbImpl/src/org/aion/db/impl/mockdb/MockDBDriver.java @@ -3,8 +3,8 @@ import static org.aion.db.impl.DatabaseFactory.Props; import java.util.Properties; -import org.aion.base.db.IByteArrayKeyValueDatabase; import org.aion.db.impl.IDriver; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.slf4j.Logger; @@ -25,7 +25,7 @@ public class MockDBDriver implements IDriver { /** @inheritDoc */ @Override - public IByteArrayKeyValueDatabase connect(Properties info) { + public ByteArrayKeyValueDatabase connect(Properties info) { String dbType = info.getProperty(Props.DB_TYPE); String dbName = info.getProperty(Props.DB_NAME); diff --git a/modDbImpl/src/org/aion/db/impl/mockdb/PersistentMockDB.java b/modDbImpl/src/org/aion/db/impl/mockdb/PersistentMockDB.java index b4d3df8dd3..dac067ae89 100644 --- a/modDbImpl/src/org/aion/db/impl/mockdb/PersistentMockDB.java +++ b/modDbImpl/src/org/aion/db/impl/mockdb/PersistentMockDB.java @@ -10,8 +10,8 @@ import java.util.HashMap; import java.util.Map; import java.util.Objects; -import org.aion.base.db.PersistenceMethod; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.interfaces.db.PersistenceMethod; +import org.aion.types.ByteArrayWrapper; /** * Provides the same behavior as {@link MockDB} with the addition that data is read from a file on diff --git a/modDbImpl/src/org/aion/db/impl/mongodb/MongoDB.java b/modDbImpl/src/org/aion/db/impl/mongodb/MongoDB.java index bdf70b8f31..5e7dba4f64 100644 --- a/modDbImpl/src/org/aion/db/impl/mongodb/MongoDB.java +++ b/modDbImpl/src/org/aion/db/impl/mongodb/MongoDB.java @@ -27,8 +27,9 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import org.aion.base.db.PersistenceMethod; -import org.aion.base.util.ByteArrayWrapper; + +import org.aion.interfaces.db.PersistenceMethod; +import org.aion.types.ByteArrayWrapper; import org.aion.db.impl.AbstractDB; import org.bson.BsonBinary; import org.bson.BsonDocument; diff --git a/modDbImpl/src/org/aion/db/impl/rocksdb/RocksDBWrapper.java b/modDbImpl/src/org/aion/db/impl/rocksdb/RocksDBWrapper.java index c0eec3c27e..522bccf948 100644 --- a/modDbImpl/src/org/aion/db/impl/rocksdb/RocksDBWrapper.java +++ b/modDbImpl/src/org/aion/db/impl/rocksdb/RocksDBWrapper.java @@ -6,7 +6,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.Map; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; import org.aion.db.impl.AbstractDB; import org.rocksdb.BlockBasedTableConfig; import org.rocksdb.CompressionType; diff --git a/modDbImpl/test/org/aion/db/impl/AccessWithExceptionTest.java b/modDbImpl/test/org/aion/db/impl/AccessWithExceptionTest.java index f5e25e5096..6abb028ff2 100644 --- a/modDbImpl/test/org/aion/db/impl/AccessWithExceptionTest.java +++ b/modDbImpl/test/org/aion/db/impl/AccessWithExceptionTest.java @@ -11,8 +11,8 @@ import java.util.Properties; import junitparams.JUnitParamsRunner; import junitparams.Parameters; -import org.aion.base.db.IByteArrayKeyValueDatabase; import org.aion.db.utils.FileUtils; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; import org.aion.log.AionLoggerFactory; import org.junit.AfterClass; import org.junit.Before; @@ -58,7 +58,7 @@ private static Object databaseInstanceDefinitions() { public void testIsEmptyWithClosedDatabase(Properties dbDef) { // create database dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + DatabaseTestUtils.getNext()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.isOpen()).isFalse(); if (VERBOSE) { @@ -74,7 +74,7 @@ public void testIsEmptyWithClosedDatabase(Properties dbDef) { public void testKeysWithClosedDatabase(Properties dbDef) { // create database dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + DatabaseTestUtils.getNext()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.isOpen()).isFalse(); if (VERBOSE) { @@ -90,7 +90,7 @@ public void testKeysWithClosedDatabase(Properties dbDef) { public void testGetWithClosedDatabase(Properties dbDef) { // create database dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + DatabaseTestUtils.getNext()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.isOpen()).isFalse(); if (VERBOSE) { @@ -106,7 +106,7 @@ public void testGetWithClosedDatabase(Properties dbDef) { public void testPutWithClosedDatabase(Properties dbDef) { // create database dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + DatabaseTestUtils.getNext()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.isOpen()).isFalse(); if (VERBOSE) { @@ -122,7 +122,7 @@ public void testPutWithClosedDatabase(Properties dbDef) { public void testDeleteWithClosedDatabase(Properties dbDef) { // create database dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + DatabaseTestUtils.getNext()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.isOpen()).isFalse(); if (VERBOSE) { @@ -138,7 +138,7 @@ public void testDeleteWithClosedDatabase(Properties dbDef) { public void testPutToBatchWithClosedDatabase(Properties dbDef) { // create database dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + DatabaseTestUtils.getNext()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.isOpen()).isFalse(); if (VERBOSE) { @@ -154,7 +154,7 @@ public void testPutToBatchWithClosedDatabase(Properties dbDef) { public void testDeleteInBatchWithClosedDatabase(Properties dbDef) { // create database dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + DatabaseTestUtils.getNext()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.isOpen()).isFalse(); if (VERBOSE) { @@ -170,7 +170,7 @@ public void testDeleteInBatchWithClosedDatabase(Properties dbDef) { public void testPutBatchWithClosedDatabase(Properties dbDef) { // create database dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + DatabaseTestUtils.getNext()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.isOpen()).isFalse(); Map<byte[], byte[]> map = new HashMap<>(); @@ -191,7 +191,7 @@ public void testPutBatchWithClosedDatabase(Properties dbDef) { public void testDeleteBatchWithClosedDatabase(Properties dbDef) { // create database dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + DatabaseTestUtils.getNext()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.isOpen()).isFalse(); List<byte[]> list = new ArrayList<>(); @@ -212,7 +212,7 @@ public void testDeleteBatchWithClosedDatabase(Properties dbDef) { public void testCommitWithClosedDatabase(Properties dbDef) { // create database dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + DatabaseTestUtils.getNext()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.isOpen()).isFalse(); if (VERBOSE) { @@ -229,7 +229,7 @@ public void testCommitWithClosedDatabase(Properties dbDef) { public void testSizeWithClosedDatabase(Properties dbDef) { // create database dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + DatabaseTestUtils.getNext()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.isOpen()).isFalse(); if (VERBOSE) { @@ -245,7 +245,7 @@ public void testSizeWithClosedDatabase(Properties dbDef) { public void testGetWithNullKey(Properties dbDef) { // create database dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + DatabaseTestUtils.getNext()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.open()).isTrue(); if (VERBOSE) { @@ -261,7 +261,7 @@ public void testGetWithNullKey(Properties dbDef) { public void testPutWithNullKey(Properties dbDef) { // create database dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + DatabaseTestUtils.getNext()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.open()).isTrue(); if (VERBOSE) { @@ -277,7 +277,7 @@ public void testPutWithNullKey(Properties dbDef) { public void testPutWithNullValue(Properties dbDef) { // create database dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + DatabaseTestUtils.getNext()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.open()).isTrue(); if (VERBOSE) { @@ -293,7 +293,7 @@ public void testPutWithNullValue(Properties dbDef) { public void testPutToBatchWithNullKey(Properties dbDef) { // create database dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + DatabaseTestUtils.getNext()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.open()).isTrue(); if (VERBOSE) { @@ -309,7 +309,7 @@ public void testPutToBatchWithNullKey(Properties dbDef) { public void testPutToBatchWithNullValue(Properties dbDef) { // create database dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + DatabaseTestUtils.getNext()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.open()).isTrue(); if (VERBOSE) { @@ -325,7 +325,7 @@ public void testPutToBatchWithNullValue(Properties dbDef) { public void testDeleteWithNullKey(Properties dbDef) { // create database dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + DatabaseTestUtils.getNext()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.open()).isTrue(); if (VERBOSE) { @@ -341,7 +341,7 @@ public void testDeleteWithNullKey(Properties dbDef) { public void testDeleteInBatchWithNullKey(Properties dbDef) { // create database dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + DatabaseTestUtils.getNext()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.open()).isTrue(); if (VERBOSE) { @@ -357,7 +357,7 @@ public void testDeleteInBatchWithNullKey(Properties dbDef) { public void testPutBatchWithNullKey(Properties dbDef) { // create database dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + DatabaseTestUtils.getNext()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.open()).isTrue(); Map<byte[], byte[]> map = new HashMap<>(); @@ -378,7 +378,7 @@ public void testPutBatchWithNullKey(Properties dbDef) { public void testPutBatchWithNullValue(Properties dbDef) { // create database dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + DatabaseTestUtils.getNext()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.open()).isTrue(); Map<byte[], byte[]> map = new HashMap<>(); @@ -399,7 +399,7 @@ public void testPutBatchWithNullValue(Properties dbDef) { public void testDeleteBatchWithNullKey(Properties dbDef) { // create database dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + DatabaseTestUtils.getNext()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.open()).isTrue(); List<byte[]> list = new ArrayList<>(); diff --git a/modDbImpl/test/org/aion/db/impl/ConcurrencyTest.java b/modDbImpl/test/org/aion/db/impl/ConcurrencyTest.java index 9cab4bcfdf..66f01738cf 100644 --- a/modDbImpl/test/org/aion/db/impl/ConcurrencyTest.java +++ b/modDbImpl/test/org/aion/db/impl/ConcurrencyTest.java @@ -14,8 +14,8 @@ import java.util.Properties; import junitparams.JUnitParamsRunner; import junitparams.Parameters; -import org.aion.base.db.IByteArrayKeyValueDatabase; import org.aion.db.utils.FileUtils; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; import org.aion.log.AionLoggerFactory; import org.junit.AfterClass; import org.junit.Before; @@ -76,7 +76,7 @@ private static int count(Iterator<byte[]> keys) { return size; } - private void addThread4IsEmpty(List<Runnable> threads, IByteArrayKeyValueDatabase db) { + private void addThread4IsEmpty(List<Runnable> threads, ByteArrayKeyValueDatabase db) { threads.add( () -> { boolean check = db.isEmpty(); @@ -89,7 +89,7 @@ private void addThread4IsEmpty(List<Runnable> threads, IByteArrayKeyValueDatabas }); } - private void addThread4Keys(List<Runnable> threads, IByteArrayKeyValueDatabase db) { + private void addThread4Keys(List<Runnable> threads, ByteArrayKeyValueDatabase db) { threads.add( () -> { Iterator<byte[]> keys = db.keys(); @@ -100,7 +100,7 @@ private void addThread4Keys(List<Runnable> threads, IByteArrayKeyValueDatabase d }); } - private void addThread4Get(List<Runnable> threads, IByteArrayKeyValueDatabase db, String key) { + private void addThread4Get(List<Runnable> threads, ByteArrayKeyValueDatabase db, String key) { threads.add( () -> { boolean hasValue = db.get(key.getBytes()).isPresent(); @@ -115,7 +115,7 @@ private void addThread4Get(List<Runnable> threads, IByteArrayKeyValueDatabase db }); } - private void addThread4Put(List<Runnable> threads, IByteArrayKeyValueDatabase db, String key) { + private void addThread4Put(List<Runnable> threads, ByteArrayKeyValueDatabase db, String key) { threads.add( () -> { db.put(key.getBytes(), DatabaseTestUtils.randomBytes(32)); @@ -127,7 +127,7 @@ private void addThread4Put(List<Runnable> threads, IByteArrayKeyValueDatabase db } private void addThread4Delete( - List<Runnable> threads, IByteArrayKeyValueDatabase db, String key) { + List<Runnable> threads, ByteArrayKeyValueDatabase db, String key) { threads.add( () -> { db.delete(key.getBytes()); @@ -139,7 +139,7 @@ private void addThread4Delete( } private void addThread4PutBatch( - List<Runnable> threads, IByteArrayKeyValueDatabase db, String key) { + List<Runnable> threads, ByteArrayKeyValueDatabase db, String key) { threads.add( () -> { Map<byte[], byte[]> map = new HashMap<>(); @@ -162,7 +162,7 @@ private void addThread4PutBatch( } private void addThread4DeleteBatch( - List<Runnable> threads, IByteArrayKeyValueDatabase db, String key) { + List<Runnable> threads, ByteArrayKeyValueDatabase db, String key) { threads.add( () -> { List<byte[]> list = new ArrayList<>(); @@ -184,7 +184,7 @@ private void addThread4DeleteBatch( }); } - private void addThread4Open(List<Runnable> threads, IByteArrayKeyValueDatabase db) { + private void addThread4Open(List<Runnable> threads, ByteArrayKeyValueDatabase db) { threads.add( () -> { db.open(); @@ -194,7 +194,7 @@ private void addThread4Open(List<Runnable> threads, IByteArrayKeyValueDatabase d }); } - private void addThread4Close(List<Runnable> threads, IByteArrayKeyValueDatabase db) { + private void addThread4Close(List<Runnable> threads, ByteArrayKeyValueDatabase db) { threads.add( () -> { db.close(); @@ -204,7 +204,7 @@ private void addThread4Close(List<Runnable> threads, IByteArrayKeyValueDatabase }); } - private void addThread4Size(List<Runnable> threads, IByteArrayKeyValueDatabase db) { + private void addThread4Size(List<Runnable> threads, ByteArrayKeyValueDatabase db) { threads.add( () -> { long size = db.approximateSize(); @@ -221,7 +221,7 @@ public void testConcurrentAccessOnOpenDatabase(Properties dbDef) throws Interrup dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + getNext()); dbDef.setProperty(ENABLE_LOCKING, "true"); // open database - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.open()).isTrue(); // create distinct threads with @@ -276,7 +276,7 @@ public void testConcurrentAccessOnOpenDatabase(Properties dbDef) throws Interrup public void testConcurrentPut(Properties dbDef) throws InterruptedException { dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + getNext()); dbDef.setProperty(ENABLE_LOCKING, "true"); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.open()).isTrue(); // create distinct threads with @@ -302,7 +302,7 @@ public void testConcurrentPut(Properties dbDef) throws InterruptedException { public void testConcurrentPutBatch(Properties dbDef) throws InterruptedException { dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + getNext()); dbDef.setProperty(ENABLE_LOCKING, "true"); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.open()).isTrue(); // create distinct threads with @@ -329,7 +329,7 @@ public void testConcurrentUpdate(Properties dbDef) throws InterruptedException { dbDef.setProperty(DB_NAME, DatabaseTestUtils.dbName + getNext()); dbDef.setProperty(ENABLE_LOCKING, "true"); // open database - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(dbDef); assertThat(db.open()).isTrue(); // create distinct threads with diff --git a/modDbImpl/test/org/aion/db/impl/DatabaseFactoryTest.java b/modDbImpl/test/org/aion/db/impl/DatabaseFactoryTest.java index 3148b54778..69dcc8bf48 100644 --- a/modDbImpl/test/org/aion/db/impl/DatabaseFactoryTest.java +++ b/modDbImpl/test/org/aion/db/impl/DatabaseFactoryTest.java @@ -6,7 +6,6 @@ import java.io.File; import java.util.Properties; -import org.aion.base.db.IByteArrayKeyValueDatabase; import org.aion.db.generic.DatabaseWithCache; import org.aion.db.generic.LockedDatabase; import org.aion.db.generic.SpecialLockedDatabase; @@ -18,6 +17,7 @@ import org.aion.db.impl.mockdb.PersistentMockDB; import org.aion.db.impl.rocksdb.RocksDBConstants; import org.aion.db.impl.rocksdb.RocksDBWrapper; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; import org.junit.Test; public class DatabaseFactoryTest { @@ -36,7 +36,7 @@ public void testReturnBasicDatabase() { // MOCKDB props.setProperty(Props.DB_TYPE, DBVendor.MOCKDB.toValue()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); assertThat(db).isNotNull(); assertThat(db.getClass().getSimpleName()).isEqualTo(MockDB.class.getSimpleName()); @@ -91,7 +91,7 @@ public void testReturnLockedDatabase() { // MOCKDB props.setProperty(Props.DB_TYPE, DBVendor.MOCKDB.toValue()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); assertThat(db).isNotNull(); assertThat(db.getClass().getSimpleName()).isEqualTo(LockedDatabase.class.getSimpleName()); assertThat(db.toString()).contains(MockDB.class.getSimpleName()); @@ -154,7 +154,7 @@ public void testReturnDatabaseWithCacheParameterSet1() { // MOCKDB props.setProperty(Props.DB_TYPE, DBVendor.MOCKDB.toValue()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); assertThat(db).isNotNull(); assertThat(db.getClass().getSimpleName()) .isEqualTo(DatabaseWithCache.class.getSimpleName()); @@ -223,7 +223,7 @@ public void testReturnDatabaseWithCacheParameterSet2() { // MOCKDB props.setProperty(Props.DB_TYPE, DBVendor.MOCKDB.toValue()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); assertThat(db).isNotNull(); assertThat(db.getClass().getSimpleName()) .isEqualTo(DatabaseWithCache.class.getSimpleName()); @@ -283,7 +283,7 @@ public void testDriverRandomClassReturnNull() { // random class that is not an IDriver props.setProperty(Props.DB_TYPE, MockDB.class.getName()); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); // System.out.println(db); assertNull(db); } @@ -296,7 +296,7 @@ public void testDriverRandomStringReturnNull() { // random string props.setProperty(Props.DB_TYPE, "not a class"); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); // System.out.println(db); assertNull(db); } diff --git a/modDbImpl/test/org/aion/db/impl/DriverBaseTest.java b/modDbImpl/test/org/aion/db/impl/DriverBaseTest.java index a71eff7d54..d98e8d105a 100644 --- a/modDbImpl/test/org/aion/db/impl/DriverBaseTest.java +++ b/modDbImpl/test/org/aion/db/impl/DriverBaseTest.java @@ -13,8 +13,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import org.aion.base.db.IByteArrayKeyValueDatabase; -import org.aion.base.db.PersistenceMethod; import org.aion.db.generic.DatabaseWithCache; import org.aion.db.generic.LockedDatabase; import org.aion.db.impl.h2.H2MVMap; @@ -22,6 +20,8 @@ import org.aion.db.impl.mockdb.MockDB; import org.aion.db.impl.mockdb.PersistentMockDB; import org.aion.db.utils.FileUtils; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; +import org.aion.interfaces.db.PersistenceMethod; import org.aion.log.AionLoggerFactory; import org.junit.After; import org.junit.AfterClass; @@ -314,9 +314,9 @@ public static Iterable<Object[]> data() throws NoSuchMethodException, SecurityEx }); } - private IByteArrayKeyValueDatabase db; + private ByteArrayKeyValueDatabase db; - private final Constructor<IByteArrayKeyValueDatabase> constructor; + private final Constructor<ByteArrayKeyValueDatabase> constructor; private final Object[] args; private final String dbName; @@ -333,7 +333,7 @@ public static Iterable<Object[]> data() throws NoSuchMethodException, SecurityEx public DriverBaseTest( String testName, boolean[] props, - Constructor<IByteArrayKeyValueDatabase> constructor, + Constructor<ByteArrayKeyValueDatabase> constructor, Object[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { @@ -457,7 +457,7 @@ public void testOpenSecondInstance() if (db.getPersistenceMethod() == PersistenceMethod.FILE_BASED && !(db instanceof PersistentMockDB)) { // another connection to same DB should fail on open for all persistent KVDBs - IByteArrayKeyValueDatabase otherDatabase = this.constructor.newInstance(this.args); + ByteArrayKeyValueDatabase otherDatabase = this.constructor.newInstance(this.args); assertThat(otherDatabase.open()).isFalse(); // ensuring that new connection did not somehow close old one diff --git a/modDbImpl/test/org/aion/db/impl/DriverBenchmarkTest.java b/modDbImpl/test/org/aion/db/impl/DriverBenchmarkTest.java index 1e1dc3790d..9933f9fa0e 100644 --- a/modDbImpl/test/org/aion/db/impl/DriverBenchmarkTest.java +++ b/modDbImpl/test/org/aion/db/impl/DriverBenchmarkTest.java @@ -20,8 +20,6 @@ import java.util.Map; import java.util.Random; import java.util.concurrent.TimeUnit; -import org.aion.base.db.IByteArrayKeyValueDatabase; -import org.aion.base.db.PersistenceMethod; import org.aion.db.impl.h2.H2MVMap; import org.aion.db.impl.leveldb.LevelDB; import org.aion.db.impl.rocksdb.RocksDBConstants; @@ -32,6 +30,8 @@ import org.aion.db.utils.slices.Slice; import org.aion.db.utils.slices.SliceOutput; import org.aion.db.utils.slices.Slices; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; +import org.aion.interfaces.db.PersistenceMethod; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -82,14 +82,14 @@ public static Iterable<Object[]> data() { }); } - public IByteArrayKeyValueDatabase db; + public ByteArrayKeyValueDatabase db; public String testName; private final RandomGenerator generator; private final Random random; // Every test invocation instantiates a new IByteArrayKeyValueDB - public DriverBenchmarkTest(String testName, IByteArrayKeyValueDatabase db) { + public DriverBenchmarkTest(String testName, ByteArrayKeyValueDatabase db) { this.db = db; this.testName = testName; diff --git a/modDbImpl/test/org/aion/db/impl/h2/H2MVMapDriverTest.java b/modDbImpl/test/org/aion/db/impl/h2/H2MVMapDriverTest.java index e22236ef51..27e02e95fd 100644 --- a/modDbImpl/test/org/aion/db/impl/h2/H2MVMapDriverTest.java +++ b/modDbImpl/test/org/aion/db/impl/h2/H2MVMapDriverTest.java @@ -6,9 +6,9 @@ import java.io.File; import java.util.Properties; -import org.aion.base.db.IByteArrayKeyValueDatabase; import org.aion.db.impl.DBVendor; import org.aion.db.impl.DatabaseFactory; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; import org.junit.Test; public class H2MVMapDriverTest { @@ -28,7 +28,7 @@ public void testDriverReturnDatabase() { props.setProperty(Props.DB_NAME, dbName); props.setProperty(Props.DB_PATH, dbPath); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); assertNotNull(db); } @@ -41,7 +41,7 @@ public void testDriverReturnNull() { props.setProperty(Props.DB_NAME, dbName); props.setProperty(Props.DB_PATH, dbPath); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); assertNull(db); } diff --git a/modDbImpl/test/org/aion/db/impl/leveldb/LevelDBDriverTest.java b/modDbImpl/test/org/aion/db/impl/leveldb/LevelDBDriverTest.java index 4f42a12ec1..3c162c9910 100644 --- a/modDbImpl/test/org/aion/db/impl/leveldb/LevelDBDriverTest.java +++ b/modDbImpl/test/org/aion/db/impl/leveldb/LevelDBDriverTest.java @@ -6,9 +6,9 @@ import java.io.File; import java.util.Properties; -import org.aion.base.db.IByteArrayKeyValueDatabase; import org.aion.db.impl.DBVendor; import org.aion.db.impl.DatabaseFactory; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; import org.junit.Test; public class LevelDBDriverTest { @@ -33,7 +33,7 @@ public void testDriverReturnDatabase() { Props.WRITE_BUFFER_SIZE, String.valueOf(LevelDBConstants.WRITE_BUFFER_SIZE)); props.setProperty(Props.DB_CACHE_SIZE, String.valueOf(LevelDBConstants.CACHE_SIZE)); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); assertNotNull(db); } @@ -46,7 +46,7 @@ public void testDriverReturnNull() { props.setProperty(Props.DB_NAME, dbName); props.setProperty(Props.DB_PATH, dbPath); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); assertNull(db); } diff --git a/modDbImpl/test/org/aion/db/impl/mockdb/MockDBDriverTest.java b/modDbImpl/test/org/aion/db/impl/mockdb/MockDBDriverTest.java index 2919dd9a88..9ef0a2d034 100644 --- a/modDbImpl/test/org/aion/db/impl/mockdb/MockDBDriverTest.java +++ b/modDbImpl/test/org/aion/db/impl/mockdb/MockDBDriverTest.java @@ -7,15 +7,15 @@ import static org.junit.Assert.assertNull; import java.util.Properties; -import org.aion.base.db.IByteArrayKeyValueDatabase; import org.aion.db.impl.DBVendor; import org.aion.db.impl.DatabaseFactory; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; import org.junit.Before; import org.junit.Test; public class MockDBDriverTest { - private static IByteArrayKeyValueDatabase db; + private static ByteArrayKeyValueDatabase db; private static DBVendor vendor; private static MockDBDriver driver; private static Properties props; diff --git a/modDbImpl/test/org/aion/db/impl/rocksdb/RocksDBDriverTest.java b/modDbImpl/test/org/aion/db/impl/rocksdb/RocksDBDriverTest.java index d045056f21..c6adc02abc 100644 --- a/modDbImpl/test/org/aion/db/impl/rocksdb/RocksDBDriverTest.java +++ b/modDbImpl/test/org/aion/db/impl/rocksdb/RocksDBDriverTest.java @@ -6,9 +6,9 @@ import java.io.File; import java.util.Properties; -import org.aion.base.db.IByteArrayKeyValueDatabase; import org.aion.db.impl.DBVendor; import org.aion.db.impl.DatabaseFactory; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; import org.junit.Test; public class RocksDBDriverTest { @@ -32,7 +32,7 @@ public void testDriverReturnDatabase() { props.setProperty( Props.WRITE_BUFFER_SIZE, String.valueOf(RocksDBConstants.WRITE_BUFFER_SIZE)); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); assertNotNull(db); } @@ -45,7 +45,7 @@ public void testDriverReturnNull() { props.setProperty(Props.DB_NAME, dbName); props.setProperty(Props.DB_PATH, dbPath); - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(props); assertNull(db); } diff --git a/modMcf/build.gradle b/modMcf/build.gradle index a28a43d476..5d87bb733a 100644 --- a/modMcf/build.gradle +++ b/modMcf/build.gradle @@ -4,8 +4,9 @@ test.dependsOn copyNativeLibsForModuleTests clean.dependsOn deleteNativeLibs dependencies { - compile project(':modUtil') - compile project(':modAionBase') + compile 'network.aion:vm-api4j:0.4.0' + compile 'network.aion:util4j:0.4.0' + compile project(':modLogger') compile project(':modP2p') compile project(':modCrypto') @@ -13,7 +14,6 @@ dependencies { compile project(':modDbImpl') compile 'com.madgag.spongycastle:prov:1.58.0.0' compile 'com.madgag.spongycastle:core:1.58.0.0' - compile project(':aion_vm_api') compile files('../lib/libnsc.jar') compile 'org.slf4j:slf4j-api:1.7.25' compile 'com.google.guava:guava:25.1-jre' diff --git a/modMcf/src/module-info.java b/modMcf/src/module-info.java index 17ea2c75ca..8dcef75ee6 100644 --- a/modMcf/src/module-info.java +++ b/modMcf/src/module-info.java @@ -1,7 +1,6 @@ module aion.mcf { requires aion.util; requires aion.crypto; - requires aion.base; requires aion.log; requires java.xml; requires aion.rlp; @@ -9,7 +8,6 @@ requires slf4j.api; requires aion.p2p; requires com.google.common; - // requires libnsc; requires commons.collections4; requires aion.vm.api; requires core; diff --git a/modMcf/src/org/aion/mcf/account/AccountManager.java b/modMcf/src/org/aion/mcf/account/AccountManager.java index ab22648bee..4467b621ae 100644 --- a/modMcf/src/org/aion/mcf/account/AccountManager.java +++ b/modMcf/src/org/aion/mcf/account/AccountManager.java @@ -9,7 +9,7 @@ import org.aion.crypto.ECKey; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Address; import org.slf4j.Logger; /** Account Manger Class */ diff --git a/modMcf/src/org/aion/mcf/account/Keystore.java b/modMcf/src/org/aion/mcf/account/Keystore.java index 107971ea65..693a12f34a 100644 --- a/modMcf/src/org/aion/mcf/account/Keystore.java +++ b/modMcf/src/org/aion/mcf/account/Keystore.java @@ -22,16 +22,16 @@ import java.util.TimeZone; import java.util.regex.Pattern; import java.util.stream.Collectors; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.TypeConverter; + +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.aion.util.bytes.ByteUtil; import org.aion.util.conversions.Hex; -import org.aion.vm.api.interfaces.Address; +import org.aion.util.string.StringUtils; import org.slf4j.Logger; /** key store class. */ @@ -89,7 +89,7 @@ public static String create(String password, ECKey key) { FileOutputStream fos = new FileOutputStream(path); fos.write(content); fos.close(); - return TypeConverter.toJsonHex(address); + return StringUtils.toJsonHex(address); } catch (IOException e) { LOG.error("fail to create keystore"); return ADDR_PREFIX; @@ -118,7 +118,7 @@ public static Map<Address, ByteArrayWrapper> backupAccount(Map<Address, String> throw new NullPointerException(); } - List<File> files = org.aion.base.io.File.getFiles(PATH); + List<File> files = org.aion.util.file.File.getFiles(PATH); if (files == null) { if (LOG.isWarnEnabled()) { LOG.warn("No key file been stored in the kernel."); @@ -146,7 +146,7 @@ public static Map<Address, ByteArrayWrapper> backupAccount(Map<Address, String> String[] frags = file.getName().split("--"); if (frags.length == 3) { if (frags[2].startsWith(AION_PREFIX)) { - Address addr = AionAddress.wrap(frags[2]); + Address addr = Address.wrap(frags[2]); byte[] content = Files.readAllBytes(file.toPath()); String pw = account.get(addr); @@ -171,7 +171,7 @@ public static Map<Address, ByteArrayWrapper> backupAccount(Map<Address, String> } public static String[] list() { - return addAddrs(org.aion.base.io.File.getFiles(PATH)).toArray(new String[0]); + return addAddrs(org.aion.util.file.File.getFiles(PATH)).toArray(new String[0]); } private static List<String> addAddrs(List<File> files) { @@ -181,7 +181,7 @@ private static List<String> addAddrs(List<File> files) { String[] frags = file.getName().split("--"); if (frags.length == 3) { if (frags[2].startsWith(AION_PREFIX)) { - addresses.add(TypeConverter.toJsonHex(frags[2])); + addresses.add(StringUtils.toJsonHex(frags[2])); } else { if (LOG.isDebugEnabled()) { LOG.debug("Wrong address format: {}", frags[2]); @@ -198,7 +198,7 @@ private static List<String> addAddrs(List<File> files) { * @return address represent by String as a List */ public static List<String> accountsSorted() { - List<File> files = org.aion.base.io.File.getFiles(PATH); + List<File> files = org.aion.util.file.File.getFiles(PATH); files.sort(COMPARE); return addAddrs(files); } @@ -210,7 +210,7 @@ public static ECKey getKey(String _address, String _password) { ECKey key = null; if (_address.startsWith(AION_PREFIX)) { - List<File> files = org.aion.base.io.File.getFiles(PATH); + List<File> files = org.aion.util.file.File.getFiles(PATH); for (File file : files) { if (HEX_64.matcher(_address).find() && file.getName().contains(_address)) { try { @@ -240,7 +240,7 @@ public static boolean exist(String _address) { boolean flag = false; if (_address.startsWith(AION_PREFIX)) { - List<File> files = org.aion.base.io.File.getFiles(PATH); + List<File> files = org.aion.util.file.File.getFiles(PATH); for (File file : files) { if (HEX_64.matcher(_address).find() && file.getName().contains(_address)) { flag = true; @@ -296,7 +296,7 @@ public static Set<String> importAccount(Map<String, String> importKey) { * Test method. Don't use it for the code dev. */ static File getAccountFile(String address, String password) { - List<File> files = org.aion.base.io.File.getFiles(PATH); + List<File> files = org.aion.util.file.File.getFiles(PATH); if (files == null) { if (LOG.isWarnEnabled()) { LOG.warn("No key file been stored in the kernel."); diff --git a/modMcf/src/org/aion/mcf/blockchain/AbstractPendingTx.java b/modMcf/src/org/aion/mcf/blockchain/AbstractPendingTx.java index adb7ac8f2b..6557deef7d 100644 --- a/modMcf/src/org/aion/mcf/blockchain/AbstractPendingTx.java +++ b/modMcf/src/org/aion/mcf/blockchain/AbstractPendingTx.java @@ -1,16 +1,16 @@ package org.aion.mcf.blockchain; import java.math.BigInteger; -import org.aion.base.type.ITransaction; +import org.aion.interfaces.tx.Transaction; +import org.aion.types.Address; import org.aion.util.bytes.ByteUtil; -import org.aion.vm.api.interfaces.Address; /** * Abstract Pending Transaction Class. * * @param <TX> */ -public abstract class AbstractPendingTx<TX extends ITransaction> { +public abstract class AbstractPendingTx<TX extends Transaction> { protected TX transaction; diff --git a/modMcf/src/org/aion/mcf/blockchain/AbstractSyncQueue.java b/modMcf/src/org/aion/mcf/blockchain/AbstractSyncQueue.java index c480c284db..fadb08bbe8 100644 --- a/modMcf/src/org/aion/mcf/blockchain/AbstractSyncQueue.java +++ b/modMcf/src/org/aion/mcf/blockchain/AbstractSyncQueue.java @@ -1,19 +1,14 @@ package org.aion.mcf.blockchain; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.aion.base.type.IBlock; -import org.aion.base.util.ByteArrayWrapper; +import java.util.*; + +import org.aion.interfaces.block.Block; +import org.aion.types.ByteArrayWrapper; import org.aion.mcf.types.AbstractBlockHeaderWrapper; /** Abstract SyncQueue Class */ public abstract class AbstractSyncQueue< - BLK extends IBlock<?, ?>, BHW extends AbstractBlockHeaderWrapper<?>> + BLK extends Block<?, ?>, BHW extends AbstractBlockHeaderWrapper<?>> implements ISyncQueue<BLK, BHW> { protected static int MAX_CHAIN_LEN = 192; @@ -58,7 +53,7 @@ public boolean isReverse() { } } - public class HeaderElement<BK extends IBlock<?, ?>, BW extends AbstractBlockHeaderWrapper<?>> { + public class HeaderElement<BK extends Block<?, ?>, BW extends AbstractBlockHeaderWrapper<?>> { public BW header; public BK block; diff --git a/modMcf/src/org/aion/mcf/blockchain/IChainCfg.java b/modMcf/src/org/aion/mcf/blockchain/IChainCfg.java index 3980ba2c6e..ee2aae4f7f 100644 --- a/modMcf/src/org/aion/mcf/blockchain/IChainCfg.java +++ b/modMcf/src/org/aion/mcf/blockchain/IChainCfg.java @@ -1,14 +1,14 @@ package org.aion.mcf.blockchain; -import org.aion.base.type.IBlock; -import org.aion.base.type.ITransaction; +import org.aion.interfaces.block.Block; +import org.aion.interfaces.tx.Transaction; import org.aion.mcf.core.IDifficultyCalculator; import org.aion.mcf.core.IRewardsCalculator; import org.aion.mcf.valid.BlockHeaderValidator; import org.aion.mcf.valid.ParentBlockHeaderValidator; /** Chain configuration interface. */ -public interface IChainCfg<Blk extends IBlock<?, ?>, Tx extends ITransaction> { +public interface IChainCfg<Blk extends Block<?, ?>, Tx extends Transaction> { boolean acceptTransactionSignature(Tx tx); diff --git a/modMcf/src/org/aion/mcf/blockchain/IGenericChain.java b/modMcf/src/org/aion/mcf/blockchain/IGenericChain.java index 80ab76dd03..59140008ad 100644 --- a/modMcf/src/org/aion/mcf/blockchain/IGenericChain.java +++ b/modMcf/src/org/aion/mcf/blockchain/IGenericChain.java @@ -1,12 +1,12 @@ package org.aion.mcf.blockchain; -import org.aion.base.type.IBlock; +import org.aion.interfaces.block.Block; import org.aion.mcf.core.AbstractTxInfo; import org.aion.mcf.db.IBlockStoreBase; import org.aion.mcf.types.AbstractBlockHeader; /** Generic Chain interface. */ -public interface IGenericChain<BLK extends IBlock, BH extends AbstractBlockHeader> { +public interface IGenericChain<BLK extends Block, BH extends AbstractBlockHeader> { BLK getBlockByNumber(long number); diff --git a/modMcf/src/org/aion/mcf/blockchain/IPendingState.java b/modMcf/src/org/aion/mcf/blockchain/IPendingState.java index 47bd618c4c..6765de76ca 100644 --- a/modMcf/src/org/aion/mcf/blockchain/IPendingState.java +++ b/modMcf/src/org/aion/mcf/blockchain/IPendingState.java @@ -2,11 +2,11 @@ import java.math.BigInteger; import java.util.List; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.ITransaction; -import org.aion.vm.api.interfaces.Address; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.interfaces.tx.Transaction; +import org.aion.types.Address; -public interface IPendingState<TX extends ITransaction> { +public interface IPendingState<TX extends Transaction> { List<TxResponse> addPendingTransactions(List<TX> transactions); @@ -14,7 +14,7 @@ public interface IPendingState<TX extends ITransaction> { boolean isValid(TX tx); - IRepositoryCache<?, ?> getRepository(); + RepositoryCache<?, ?> getRepository(); List<TX> getPendingTransactions(); diff --git a/modMcf/src/org/aion/mcf/blockchain/IPendingStateInternal.java b/modMcf/src/org/aion/mcf/blockchain/IPendingStateInternal.java index a6f2706944..b741ea521e 100644 --- a/modMcf/src/org/aion/mcf/blockchain/IPendingStateInternal.java +++ b/modMcf/src/org/aion/mcf/blockchain/IPendingStateInternal.java @@ -1,8 +1,8 @@ package org.aion.mcf.blockchain; import java.util.List; -import org.aion.base.type.IBlock; -import org.aion.base.type.ITransaction; +import org.aion.interfaces.block.Block; +import org.aion.interfaces.tx.Transaction; import org.aion.mcf.types.AbstractTxReceipt; /** @@ -11,7 +11,7 @@ * @param <BLK> * @param <Tx> */ -public interface IPendingStateInternal<BLK extends IBlock<?, ?>, Tx extends ITransaction> +public interface IPendingStateInternal<BLK extends Block<?, ?>, Tx extends Transaction> extends IPendingState<Tx> { // called by onBest diff --git a/modMcf/src/org/aion/mcf/blockchain/IPowChain.java b/modMcf/src/org/aion/mcf/blockchain/IPowChain.java index 1823772308..1e611276df 100644 --- a/modMcf/src/org/aion/mcf/blockchain/IPowChain.java +++ b/modMcf/src/org/aion/mcf/blockchain/IPowChain.java @@ -1,9 +1,9 @@ package org.aion.mcf.blockchain; import java.math.BigInteger; -import org.aion.base.type.Hash256; -import org.aion.base.type.IBlock; +import org.aion.interfaces.block.Block; import org.aion.mcf.types.AbstractBlockHeader; +import org.aion.types.Hash256; /** * proof of work chain interface. @@ -12,7 +12,7 @@ * @param <BH> */ @SuppressWarnings("rawtypes") -public interface IPowChain<BLK extends IBlock, BH extends AbstractBlockHeader> +public interface IPowChain<BLK extends Block, BH extends AbstractBlockHeader> extends IGenericChain<BLK, BH> { BigInteger getTotalDifficulty(); diff --git a/modMcf/src/org/aion/mcf/blockchain/ISyncQueue.java b/modMcf/src/org/aion/mcf/blockchain/ISyncQueue.java index 10106d73ca..74f3f7ed23 100644 --- a/modMcf/src/org/aion/mcf/blockchain/ISyncQueue.java +++ b/modMcf/src/org/aion/mcf/blockchain/ISyncQueue.java @@ -2,7 +2,7 @@ import java.util.Collection; import java.util.List; -import org.aion.base.type.IBlock; +import org.aion.interfaces.block.Block; import org.aion.mcf.types.AbstractBlockHeaderWrapper; /** @@ -11,7 +11,7 @@ * @param <BLK> * @param <BHW> */ -public interface ISyncQueue<BLK extends IBlock<?, ?>, BHW extends AbstractBlockHeaderWrapper<?>> { +public interface ISyncQueue<BLK extends Block<?, ?>, BHW extends AbstractBlockHeaderWrapper<?>> { /** Wanted headers */ interface HeadersRequest { diff --git a/modMcf/src/org/aion/mcf/blockchain/TxExecutorBase.java b/modMcf/src/org/aion/mcf/blockchain/TxExecutorBase.java index bb94cd7375..2b234ad698 100644 --- a/modMcf/src/org/aion/mcf/blockchain/TxExecutorBase.java +++ b/modMcf/src/org/aion/mcf/blockchain/TxExecutorBase.java @@ -1,7 +1,7 @@ package org.aion.mcf.blockchain; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.IBlock; +import org.aion.interfaces.block.Block; +import org.aion.interfaces.db.RepositoryCache; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.aion.mcf.db.IBlockStoreBase; @@ -11,7 +11,7 @@ /** Transaction executor base class. */ public abstract class TxExecutorBase< - BLK extends IBlock<?, ?>, + BLK extends Block<?, ?>, TX extends AbstractTransaction, BS extends IBlockStoreBase<?, ?>, TR extends AbstractTxReceipt<?>> { @@ -20,9 +20,9 @@ public abstract class TxExecutorBase< protected TX tx; - protected IRepositoryCache<?, ?> track; + protected RepositoryCache<?, ?> track; - protected IRepositoryCache<?, ?> cacheTrack; + protected RepositoryCache<?, ?> cacheTrack; protected BS blockStore; diff --git a/modMcf/src/org/aion/mcf/blockchain/valid/IBlockHeaderValidRule.java b/modMcf/src/org/aion/mcf/blockchain/valid/IBlockHeaderValidRule.java index 9e27122fec..a4fc956485 100644 --- a/modMcf/src/org/aion/mcf/blockchain/valid/IBlockHeaderValidRule.java +++ b/modMcf/src/org/aion/mcf/blockchain/valid/IBlockHeaderValidRule.java @@ -1,14 +1,14 @@ package org.aion.mcf.blockchain.valid; import java.util.List; -import org.aion.base.type.IBlockHeader; +import org.aion.interfaces.block.BlockHeader; /** * Block header validation rules. * * @param <BH> */ -public interface IBlockHeaderValidRule<BH extends IBlockHeader> extends IValidRule { +public interface IBlockHeaderValidRule<BH extends BlockHeader> extends IValidRule { boolean validate(BH header, BH dependency, List<RuleError> errors); } diff --git a/modMcf/src/org/aion/mcf/blockchain/valid/IValidRule.java b/modMcf/src/org/aion/mcf/blockchain/valid/IValidRule.java index 4ab54834a5..9003e7f773 100644 --- a/modMcf/src/org/aion/mcf/blockchain/valid/IValidRule.java +++ b/modMcf/src/org/aion/mcf/blockchain/valid/IValidRule.java @@ -2,7 +2,7 @@ /** Valication rules interface. */ public interface IValidRule { - public static class RuleError { + class RuleError { public final Class<?> errorClass; public final String error; diff --git a/modMcf/src/org/aion/mcf/config/CfgDb.java b/modMcf/src/org/aion/mcf/config/CfgDb.java index dd98feb9b8..50a90e8ae8 100644 --- a/modMcf/src/org/aion/mcf/config/CfgDb.java +++ b/modMcf/src/org/aion/mcf/config/CfgDb.java @@ -13,8 +13,8 @@ import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamWriter; -import org.aion.base.util.Utils; import org.aion.db.impl.DBVendor; +import org.aion.util.others.Utils; /** @author chris */ public class CfgDb { diff --git a/modMcf/src/org/aion/mcf/config/CfgDbDetails.java b/modMcf/src/org/aion/mcf/config/CfgDbDetails.java index e83a2c98bf..719c4be031 100644 --- a/modMcf/src/org/aion/mcf/config/CfgDbDetails.java +++ b/modMcf/src/org/aion/mcf/config/CfgDbDetails.java @@ -8,8 +8,8 @@ import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamWriter; -import org.aion.base.util.Utils; import org.aion.db.impl.DBVendor; +import org.aion.util.others.Utils; public class CfgDbDetails { diff --git a/modMcf/src/org/aion/mcf/config/CfgPrune.java b/modMcf/src/org/aion/mcf/config/CfgPrune.java index 2feea01177..98d595d2b6 100644 --- a/modMcf/src/org/aion/mcf/config/CfgPrune.java +++ b/modMcf/src/org/aion/mcf/config/CfgPrune.java @@ -4,14 +4,14 @@ import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamWriter; -import org.aion.base.db.IPruneConfig; +import org.aion.interfaces.db.PruneConfig; /** * Configuration for data pruning behavior. * * @author Alexandra Roatis */ -public class CfgPrune implements IPruneConfig { +public class CfgPrune implements PruneConfig { private boolean enabled; private boolean archived; diff --git a/modMcf/src/org/aion/mcf/core/IBlockchain.java b/modMcf/src/org/aion/mcf/core/IBlockchain.java index 2c0d65b634..c301da5630 100644 --- a/modMcf/src/org/aion/mcf/core/IBlockchain.java +++ b/modMcf/src/org/aion/mcf/core/IBlockchain.java @@ -3,14 +3,14 @@ import java.math.BigInteger; import java.util.List; import java.util.Map; -import org.aion.base.type.IBlock; -import org.aion.base.type.ITransaction; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.interfaces.block.Block; +import org.aion.interfaces.tx.Transaction; import org.aion.mcf.blockchain.IPowChain; import org.aion.mcf.types.AbstractBlockHeader; import org.aion.mcf.types.AbstractBlockSummary; import org.aion.mcf.types.AbstractTxReceipt; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; /** * Blockchain interface. @@ -23,9 +23,9 @@ */ @SuppressWarnings("rawtypes") public interface IBlockchain< - BLK extends IBlock, + BLK extends Block, BH extends AbstractBlockHeader, - TX extends ITransaction, + TX extends Transaction, TR extends AbstractTxReceipt, INFO extends AbstractTxInfo> extends IPowChain<BLK, BH> { @@ -124,7 +124,7 @@ void dropImported( // * Returns emptyList() for side chain blocks. // */ // List<BH> getListOfHeadersStartFrom( - // BlockIdentifier identifier, int skip, int limit, boolean reverse); + // BlockIdentifierImpl identifier, int skip, int limit, boolean reverse); List<byte[]> getListOfBodiesByHashes(List<byte[]> hashes); } diff --git a/modMcf/src/org/aion/mcf/core/TxTouchedStorage.java b/modMcf/src/org/aion/mcf/core/TxTouchedStorage.java index cd51900621..d9d948f595 100644 --- a/modMcf/src/org/aion/mcf/core/TxTouchedStorage.java +++ b/modMcf/src/org/aion/mcf/core/TxTouchedStorage.java @@ -3,18 +3,18 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; -import org.aion.base.util.Functional; -import org.aion.mcf.vm.types.DataWord; +import org.aion.interfaces.functional.Functional; +import org.aion.mcf.vm.types.DataWordImpl; import org.apache.commons.collections4.MapUtils; import org.apache.commons.collections4.keyvalue.AbstractKeyValue; public class TxTouchedStorage { - public static class Entry extends AbstractKeyValue<DataWord, DataWord> { + public static class Entry extends AbstractKeyValue<DataWordImpl, DataWordImpl> { private boolean changed; - public Entry(DataWord key, DataWord value, boolean changed) { + public Entry(DataWordImpl key, DataWordImpl value, boolean changed) { super(key, value); this.changed = changed; } @@ -24,12 +24,12 @@ public Entry() { } @Override - protected DataWord setKey(DataWord key) { + protected DataWordImpl setKey(DataWordImpl key) { return super.setKey(key); } @Override - protected DataWord setValue(DataWord value) { + protected DataWordImpl setValue(DataWordImpl value) { return super.setValue(value); } @@ -42,7 +42,7 @@ public void setChanged(boolean changed) { } } - private Map<DataWord, Entry> entries = new HashMap<>(); + private Map<DataWordImpl, Entry> entries = new HashMap<>(); public TxTouchedStorage() {} @@ -60,28 +60,28 @@ public Entry add(Entry entry) { return entries.put(entry.getKey(), entry); } - private Entry add(Map.Entry<DataWord, DataWord> entry, boolean changed) { + private Entry add(Map.Entry<DataWordImpl, DataWordImpl> entry, boolean changed) { return add(new Entry(entry.getKey(), entry.getValue(), changed)); } - public void addReading(Map<DataWord, DataWord> entries) { + public void addReading(Map<DataWordImpl, DataWordImpl> entries) { if (MapUtils.isEmpty(entries)) return; - for (Map.Entry<DataWord, DataWord> entry : entries.entrySet()) { + for (Map.Entry<DataWordImpl, DataWordImpl> entry : entries.entrySet()) { if (!this.entries.containsKey(entry.getKey())) add(entry, false); } } - public void addWriting(Map<DataWord, DataWord> entries) { + public void addWriting(Map<DataWordImpl, DataWordImpl> entries) { if (MapUtils.isEmpty(entries)) return; - for (Map.Entry<DataWord, DataWord> entry : entries.entrySet()) { + for (Map.Entry<DataWordImpl, DataWordImpl> entry : entries.entrySet()) { add(entry, true); } } - private Map<DataWord, DataWord> keyValues(Functional.Function<Entry, Boolean> filter) { - Map<DataWord, DataWord> result = new HashMap<>(); + private Map<DataWordImpl, DataWordImpl> keyValues(Functional.Function<Entry, Boolean> filter) { + Map<DataWordImpl, DataWordImpl> result = new HashMap<>(); for (Entry entry : getEntries()) { if (filter == null || filter.apply(entry)) { result.put(entry.getKey(), entry.getValue()); @@ -90,7 +90,7 @@ private Map<DataWord, DataWord> keyValues(Functional.Function<Entry, Boolean> fi return result; } - public Map<DataWord, DataWord> getChanged() { + public Map<DataWordImpl, DataWordImpl> getChanged() { return keyValues( new Functional.Function<Entry, Boolean>() { @Override @@ -100,7 +100,7 @@ public Boolean apply(Entry entry) { }); } - public Map<DataWord, DataWord> getReadOnly() { + public Map<DataWordImpl, DataWordImpl> getReadOnly() { return keyValues( new Functional.Function<Entry, Boolean>() { @Override @@ -110,7 +110,7 @@ public Boolean apply(Entry entry) { }); } - public Map<DataWord, DataWord> getAll() { + public Map<DataWordImpl, DataWordImpl> getAll() { return keyValues(null); } diff --git a/modMcf/src/org/aion/mcf/db/AbstractContractDetails.java b/modMcf/src/org/aion/mcf/db/AbstractContractDetails.java index 5cfe0e56d2..e774304ac6 100644 --- a/modMcf/src/org/aion/mcf/db/AbstractContractDetails.java +++ b/modMcf/src/org/aion/mcf/db/AbstractContractDetails.java @@ -8,12 +8,12 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; -import org.aion.base.db.IContractDetails; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.interfaces.db.ContractDetails; +import org.aion.types.ByteArrayWrapper; import org.aion.util.conversions.Hex; /** Abstract contract details. */ -public abstract class AbstractContractDetails implements IContractDetails { +public abstract class AbstractContractDetails implements ContractDetails { private boolean dirty = false; private boolean deleted = false; diff --git a/modMcf/src/org/aion/mcf/db/AbstractRepository.java b/modMcf/src/org/aion/mcf/db/AbstractRepository.java index df8cf8a5ee..bc90d740eb 100644 --- a/modMcf/src/org/aion/mcf/db/AbstractRepository.java +++ b/modMcf/src/org/aion/mcf/db/AbstractRepository.java @@ -11,11 +11,11 @@ import java.util.Properties; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import org.aion.base.db.IByteArrayKeyValueDatabase; -import org.aion.base.db.IRepository; -import org.aion.base.db.IRepositoryConfig; -import org.aion.base.type.IBlockHeader; -import org.aion.base.type.ITransaction; +import org.aion.interfaces.block.BlockHeader; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; +import org.aion.interfaces.db.Repository; +import org.aion.interfaces.db.RepositoryConfig; +import org.aion.interfaces.tx.Transaction; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.aion.mcf.config.CfgDb.Names; @@ -31,17 +31,17 @@ /** Abstract Repository class. */ public abstract class AbstractRepository< - BLK extends AbstractBlock<BH, ? extends ITransaction>, - BH extends IBlockHeader, + BLK extends AbstractBlock<BH, ? extends Transaction>, + BH extends BlockHeader, BSB extends IBlockStoreBase<?, ?>> - implements IRepository<AccountState, BSB> { + implements Repository<AccountState, BSB> { // Logger protected static final Logger LOG = AionLoggerFactory.getLogger(LogEnum.DB.name()); protected static final Logger LOGGEN = AionLoggerFactory.getLogger(LogEnum.GEN.name()); // Configuration parameter - protected IRepositoryConfig cfg; + protected RepositoryConfig cfg; /** ********* Database Name Constants ********** */ protected static final String TRANSACTION_DB = Names.TRANSACTION; @@ -64,18 +64,18 @@ public abstract class AbstractRepository< // File(System.getProperty("user.dir"), "database").getAbsolutePath(); /** ******** Database and Cache parameters ************* */ - protected IByteArrayKeyValueDatabase transactionDatabase; - - protected IByteArrayKeyValueDatabase detailsDatabase; - protected IByteArrayKeyValueDatabase storageDatabase; - protected IByteArrayKeyValueDatabase indexDatabase; - protected IByteArrayKeyValueDatabase blockDatabase; - protected IByteArrayKeyValueDatabase stateDatabase; - protected IByteArrayKeyValueDatabase stateArchiveDatabase; - protected IByteArrayKeyValueDatabase txPoolDatabase; - protected IByteArrayKeyValueDatabase pendingTxCacheDatabase; - - protected Collection<IByteArrayKeyValueDatabase> databaseGroup; + protected ByteArrayKeyValueDatabase transactionDatabase; + + protected ByteArrayKeyValueDatabase detailsDatabase; + protected ByteArrayKeyValueDatabase storageDatabase; + protected ByteArrayKeyValueDatabase indexDatabase; + protected ByteArrayKeyValueDatabase blockDatabase; + protected ByteArrayKeyValueDatabase stateDatabase; + protected ByteArrayKeyValueDatabase stateArchiveDatabase; + protected ByteArrayKeyValueDatabase txPoolDatabase; + protected ByteArrayKeyValueDatabase pendingTxCacheDatabase; + + protected Collection<ByteArrayKeyValueDatabase> databaseGroup; protected ArchivedDataSource stateWithArchive; protected JournalPruneDataSource stateDSPrune; diff --git a/modMcf/src/org/aion/mcf/db/AbstractRepositoryCache.java b/modMcf/src/org/aion/mcf/db/AbstractRepositoryCache.java index 5cc0202e84..7a5205934f 100644 --- a/modMcf/src/org/aion/mcf/db/AbstractRepositoryCache.java +++ b/modMcf/src/org/aion/mcf/db/AbstractRepositoryCache.java @@ -9,14 +9,14 @@ import java.util.Map; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import org.aion.base.db.IContractDetails; -import org.aion.base.db.IRepository; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.interfaces.db.ContractDetails; +import org.aion.interfaces.db.Repository; +import org.aion.interfaces.db.RepositoryCache; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.aion.mcf.core.AccountState; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.slf4j.Logger; /** @@ -25,20 +25,20 @@ * @author Alexandra Roatis */ public abstract class AbstractRepositoryCache<BSB extends IBlockStoreBase<?, ?>> - implements IRepositoryCache<AccountState, BSB> { + implements RepositoryCache<AccountState, BSB> { // Logger protected static final Logger LOG = AionLoggerFactory.getLogger(LogEnum.DB.name()); /** the repository being tracked */ - protected IRepository<AccountState, BSB> repository; + protected Repository<AccountState, BSB> repository; /** local accounts cache */ protected Map<Address, AccountState> cachedAccounts; protected ReadWriteLock lockAccounts = new ReentrantReadWriteLock(); /** local contract details cache */ - protected Map<Address, IContractDetails> cachedDetails; + protected Map<Address, ContractDetails> cachedDetails; protected ReadWriteLock lockDetails = new ReentrantReadWriteLock(); @@ -50,7 +50,7 @@ public AccountState createAccount(Address address) { cachedAccounts.put(address, accountState); // TODO: unify contract details initialization from Impl and Track - IContractDetails contractDetails = new ContractDetailsCacheImpl(null); + ContractDetails contractDetails = new ContractDetailsCacheImpl(null); // TODO: refactor to use makeDirty() from AbstractState contractDetails.setDirty(true); cachedDetails.put(address, contractDetails); @@ -115,11 +115,11 @@ public boolean hasAccountState(Address address) { } @Override - public IContractDetails getContractDetails(Address address) { + public ContractDetails getContractDetails(Address address) { lockDetails.readLock().lock(); try { - IContractDetails contractDetails = this.cachedDetails.get(address); + ContractDetails contractDetails = this.cachedDetails.get(address); if (contractDetails == null) { // loads the address into cache @@ -146,7 +146,7 @@ public boolean hasContractDetails(Address address) { lockDetails.readLock().lock(); try { - IContractDetails contractDetails = cachedDetails.get(address); + ContractDetails contractDetails = cachedDetails.get(address); if (contractDetails == null) { // ask repository when not cached @@ -168,13 +168,13 @@ public boolean hasContractDetails(Address address) { public void loadAccountState( Address address, Map<Address, AccountState> accounts, - Map<Address, IContractDetails> details) { + Map<Address, ContractDetails> details) { fullyReadLock(); try { // check if the account is cached locally AccountState accountState = this.cachedAccounts.get(address); - IContractDetails contractDetails = this.cachedDetails.get(address); + ContractDetails contractDetails = this.cachedDetails.get(address); // when account not cached load from repository if (accountState == null) { @@ -275,7 +275,7 @@ public void saveCode(Address address, byte[] code) { // save the code // TODO: why not create contract here directly? also need to check that there is no // preexisting code! - IContractDetails contractDetails = getContractDetails(address); + ContractDetails contractDetails = getContractDetails(address); contractDetails.setCode(code); // TODO: ensure that setDirty is done by the class itself contractDetails.setDirty(true); @@ -327,7 +327,7 @@ public ByteArrayWrapper getStorageValue(Address address, ByteArrayWrapper key) { @Override public Map<ByteArrayWrapper, ByteArrayWrapper> getStorage( Address address, Collection<ByteArrayWrapper> keys) { - IContractDetails details = getContractDetails(address); + ContractDetails details = getContractDetails(address); return (details == null) ? Collections.emptyMap() : details.getStorage(keys); } @@ -343,7 +343,7 @@ public void rollback() { } @Override - public IRepository getSnapshotTo(byte[] root) { + public Repository getSnapshotTo(byte[] root) { return repository.getSnapshotTo(root); } diff --git a/modMcf/src/org/aion/mcf/db/ContractDetailsCacheImpl.java b/modMcf/src/org/aion/mcf/db/ContractDetailsCacheImpl.java index 4532ad03c7..efd5349fe9 100644 --- a/modMcf/src/org/aion/mcf/db/ContractDetailsCacheImpl.java +++ b/modMcf/src/org/aion/mcf/db/ContractDetailsCacheImpl.java @@ -5,19 +5,19 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Objects; -import org.aion.base.db.IByteArrayKeyValueStore; -import org.aion.base.db.IContractDetails; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.vm.api.interfaces.Address; +import org.aion.interfaces.db.ByteArrayKeyValueStore; +import org.aion.interfaces.db.ContractDetails; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; /** Contract details cache implementation. */ public class ContractDetailsCacheImpl extends AbstractContractDetails { private Map<ByteArrayWrapper, ByteArrayWrapper> storage = new HashMap<>(); - public IContractDetails origContract; + public ContractDetails origContract; - public ContractDetailsCacheImpl(IContractDetails origContract) { + public ContractDetailsCacheImpl(ContractDetails origContract) { this.origContract = origContract; if (origContract != null) { if (origContract instanceof AbstractContractDetails) { @@ -184,13 +184,13 @@ public void commit() { /** This method is not supported. */ @Override - public IContractDetails getSnapshotTo(byte[] hash) { + public ContractDetails getSnapshotTo(byte[] hash) { throw new UnsupportedOperationException("No snapshot option during cache state"); } /** This method is not supported. */ @Override - public void setDataSource(IByteArrayKeyValueStore dataSource) { + public void setDataSource(ByteArrayKeyValueStore dataSource) { throw new UnsupportedOperationException("Can't set datasource in cache implementation."); } @@ -220,7 +220,7 @@ public ContractDetailsCacheImpl copy() { "Cannot copy a ContractDetailsCacheImpl whose original contract is itself!"); } - IContractDetails originalContractCopy = + ContractDetails originalContractCopy = (this.origContract == null) ? null : this.origContract.copy(); ContractDetailsCacheImpl contractDetailsCacheCopy = new ContractDetailsCacheImpl(originalContractCopy); diff --git a/modMcf/src/org/aion/mcf/db/DatabaseUtils.java b/modMcf/src/org/aion/mcf/db/DatabaseUtils.java index 55e21019a6..8276dad4a0 100644 --- a/modMcf/src/org/aion/mcf/db/DatabaseUtils.java +++ b/modMcf/src/org/aion/mcf/db/DatabaseUtils.java @@ -9,19 +9,19 @@ import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.Properties; -import org.aion.base.db.IByteArrayKeyValueDatabase; -import org.aion.base.db.PersistenceMethod; import org.aion.db.impl.DatabaseFactory; import org.aion.db.impl.DatabaseFactory.Props; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; +import org.aion.interfaces.db.PersistenceMethod; import org.aion.mcf.db.exception.InvalidFilePathException; import org.slf4j.Logger; /** @author Alexandra Roatis */ public class DatabaseUtils { - public static IByteArrayKeyValueDatabase connectAndOpen(Properties info, Logger LOG) { + public static ByteArrayKeyValueDatabase connectAndOpen(Properties info, Logger LOG) { // get the database object - IByteArrayKeyValueDatabase db = DatabaseFactory.connect(info, LOG.isDebugEnabled()); + ByteArrayKeyValueDatabase db = DatabaseFactory.connect(info, LOG.isDebugEnabled()); // open the database connection db.open(); diff --git a/modMcf/src/org/aion/mcf/db/DetailsDataStore.java b/modMcf/src/org/aion/mcf/db/DetailsDataStore.java index a6060d7c7a..e89a39ad62 100644 --- a/modMcf/src/org/aion/mcf/db/DetailsDataStore.java +++ b/modMcf/src/org/aion/mcf/db/DetailsDataStore.java @@ -1,45 +1,45 @@ package org.aion.mcf.db; -import static org.aion.base.util.ByteArrayWrapper.wrap; +import static org.aion.types.ByteArrayWrapper.wrap; import java.util.HashSet; import java.util.Iterator; import java.util.Optional; import java.util.Set; -import org.aion.base.db.IByteArrayKeyValueDatabase; -import org.aion.base.db.IContractDetails; -import org.aion.base.db.IRepositoryConfig; -import org.aion.base.type.IBlockHeader; -import org.aion.base.type.ITransaction; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.interfaces.block.BlockHeader; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; +import org.aion.interfaces.db.ContractDetails; +import org.aion.interfaces.db.RepositoryConfig; +import org.aion.interfaces.tx.Transaction; import org.aion.mcf.trie.JournalPruneDataSource; import org.aion.mcf.types.AbstractBlock; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; /** Detail data storage , */ public class DetailsDataStore< - BLK extends AbstractBlock<BH, ? extends ITransaction>, BH extends IBlockHeader> { + BLK extends AbstractBlock<BH, ? extends Transaction>, BH extends BlockHeader> { private JournalPruneDataSource storageDSPrune; - private IRepositoryConfig repoConfig; + private RepositoryConfig repoConfig; - private IByteArrayKeyValueDatabase detailsSrc; - private IByteArrayKeyValueDatabase storageSrc; + private ByteArrayKeyValueDatabase detailsSrc; + private ByteArrayKeyValueDatabase storageSrc; private Set<ByteArrayWrapper> removes = new HashSet<>(); public DetailsDataStore() {} public DetailsDataStore( - IByteArrayKeyValueDatabase detailsCache, - IByteArrayKeyValueDatabase storageCache, - IRepositoryConfig repoConfig) { + ByteArrayKeyValueDatabase detailsCache, + ByteArrayKeyValueDatabase storageCache, + RepositoryConfig repoConfig) { this.repoConfig = repoConfig; withDb(detailsCache, storageCache); } public DetailsDataStore<BLK, BH> withDb( - IByteArrayKeyValueDatabase detailsSrc, IByteArrayKeyValueDatabase storageSrc) { + ByteArrayKeyValueDatabase detailsSrc, ByteArrayKeyValueDatabase storageSrc) { this.detailsSrc = detailsSrc; this.storageSrc = storageSrc; this.storageDSPrune = new JournalPruneDataSource(storageSrc); @@ -52,7 +52,7 @@ public DetailsDataStore<BLK, BH> withDb( * @param key * @return */ - public synchronized IContractDetails get(byte[] key) { + public synchronized ContractDetails get(byte[] key) { ByteArrayWrapper wrappedKey = wrap(key); Optional<byte[]> rawDetails = detailsSrc.get(key); @@ -67,7 +67,7 @@ public synchronized IContractDetails get(byte[] key) { } // Found something from cache or database, return it by decoding it. - IContractDetails detailsImpl = repoConfig.contractDetailsImpl(); + ContractDetails detailsImpl = repoConfig.contractDetailsImpl(); detailsImpl.setDataSource(storageDSPrune); detailsImpl.decode(rawDetails.get()); // We can safely get as we checked // if it is present. @@ -75,7 +75,7 @@ public synchronized IContractDetails get(byte[] key) { return detailsImpl; } - public synchronized void update(Address key, IContractDetails contractDetails) { + public synchronized void update(Address key, ContractDetails contractDetails) { contractDetails.setAddress(key); ByteArrayWrapper wrappedKey = wrap(key.toBytes()); @@ -139,12 +139,12 @@ public void syncLargeStorage() { } // Decode the details. - IContractDetails detailsImpl = repoConfig.contractDetailsImpl(); + ContractDetails detailsImpl = repoConfig.contractDetailsImpl(); detailsImpl.setDataSource(storageDSPrune); detailsImpl.decode(rawDetails.get(), true); // We can safely get as we checked if it is present. - // IContractDetails details = entry.getValue(); + // ContractDetails details = entry.getValue(); detailsImpl.syncStorage(); } } @@ -157,6 +157,15 @@ public synchronized Iterator<ByteArrayWrapper> keys() { return new DetailsIteratorWrapper(detailsSrc.keys()); } + public synchronized void close() { + try { + detailsSrc.close(); + storageSrc.close(); + } catch (Exception e) { + throw new RuntimeException("error closing db"); + } + } + /** * A wrapper for the iterator needed by {@link DetailsDataStore} conforming to the {@link * Iterator} interface. @@ -184,13 +193,4 @@ public ByteArrayWrapper next() { return wrap(sourceIterator.next()); } } - - public synchronized void close() { - try { - detailsSrc.close(); - storageSrc.close(); - } catch (Exception e) { - throw new RuntimeException("error closing db"); - } - } } diff --git a/modMcf/src/org/aion/mcf/db/IBlockStoreBase.java b/modMcf/src/org/aion/mcf/db/IBlockStoreBase.java index 6f9070f115..d3e7f8e25c 100644 --- a/modMcf/src/org/aion/mcf/db/IBlockStoreBase.java +++ b/modMcf/src/org/aion/mcf/db/IBlockStoreBase.java @@ -1,7 +1,7 @@ package org.aion.mcf.db; import java.util.List; -import org.aion.base.type.IBlock; +import org.aion.interfaces.block.Block; import org.aion.mcf.types.AbstractBlockHeader; /** @@ -10,7 +10,7 @@ * @param <BLK> * @param <BH> */ -public interface IBlockStoreBase<BLK extends IBlock<?, ?>, BH extends AbstractBlockHeader> { +public interface IBlockStoreBase<BLK extends Block<?, ?>, BH extends AbstractBlockHeader> { byte[] getBlockHashByNumber(long blockNumber); diff --git a/modMcf/src/org/aion/mcf/db/IBlockStorePow.java b/modMcf/src/org/aion/mcf/db/IBlockStorePow.java index 21111701df..4a7cd5c17d 100644 --- a/modMcf/src/org/aion/mcf/db/IBlockStorePow.java +++ b/modMcf/src/org/aion/mcf/db/IBlockStorePow.java @@ -1,7 +1,7 @@ package org.aion.mcf.db; import java.math.BigInteger; -import org.aion.base.type.IBlock; +import org.aion.interfaces.block.Block; import org.aion.mcf.types.AbstractBlockHeader; /** @@ -10,7 +10,7 @@ * @param <BLK> * @param <BH> */ -public interface IBlockStorePow<BLK extends IBlock<?, ?>, BH extends AbstractBlockHeader> +public interface IBlockStorePow<BLK extends Block<?, ?>, BH extends AbstractBlockHeader> extends IBlockStoreBase<BLK, BH> { BigInteger getTotalDifficultyForHash(byte[] hash); diff --git a/modMcf/src/org/aion/mcf/db/TransactionStore.java b/modMcf/src/org/aion/mcf/db/TransactionStore.java index c47e7a3f53..fd5883b0a1 100644 --- a/modMcf/src/org/aion/mcf/db/TransactionStore.java +++ b/modMcf/src/org/aion/mcf/db/TransactionStore.java @@ -1,6 +1,6 @@ package org.aion.mcf.db; -import static org.aion.base.util.Utils.dummy; +import static org.aion.util.others.Utils.dummy; import java.io.Closeable; import java.util.ArrayList; @@ -8,14 +8,14 @@ import java.util.List; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import org.aion.base.db.Flushable; -import org.aion.base.db.IByteArrayKeyValueDatabase; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; +import org.aion.interfaces.db.Flushable; import org.aion.mcf.core.AbstractTxInfo; import org.aion.mcf.ds.ObjectDataSource; import org.aion.mcf.ds.Serializer; import org.aion.mcf.types.AbstractTransaction; import org.aion.mcf.types.AbstractTxReceipt; +import org.aion.types.ByteArrayWrapper; import org.apache.commons.collections4.map.LRUMap; public class TransactionStore< @@ -29,7 +29,7 @@ public class TransactionStore< private final ReadWriteLock lock = new ReentrantReadWriteLock(); public TransactionStore( - IByteArrayKeyValueDatabase src, Serializer<List<INFO>, byte[]> serializer) { + ByteArrayKeyValueDatabase src, Serializer<List<INFO>, byte[]> serializer) { source = new ObjectDataSource(src, serializer); } diff --git a/modMcf/src/org/aion/mcf/ds/ArchivedDataSource.java b/modMcf/src/org/aion/mcf/ds/ArchivedDataSource.java index 9261e7c50e..75280cdd87 100644 --- a/modMcf/src/org/aion/mcf/ds/ArchivedDataSource.java +++ b/modMcf/src/org/aion/mcf/ds/ArchivedDataSource.java @@ -4,19 +4,19 @@ import java.util.Iterator; import java.util.Map; import java.util.Optional; -import org.aion.base.db.IByteArrayKeyValueDatabase; -import org.aion.base.db.IByteArrayKeyValueStore; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; +import org.aion.interfaces.db.ByteArrayKeyValueStore; /** * A data source with archived data that must no be deleted. * * @author Alexandra Roatis */ -public class ArchivedDataSource implements IByteArrayKeyValueStore { +public class ArchivedDataSource implements ByteArrayKeyValueStore { - IByteArrayKeyValueDatabase data, archive; + ByteArrayKeyValueDatabase data, archive; - public ArchivedDataSource(IByteArrayKeyValueDatabase _db, IByteArrayKeyValueDatabase _archive) { + public ArchivedDataSource(ByteArrayKeyValueDatabase _db, ByteArrayKeyValueDatabase _archive) { this.data = _db; this.archive = _archive; } @@ -99,7 +99,7 @@ public void close() { archive.close(); } - public IByteArrayKeyValueDatabase getArchiveDatabase() { + public ByteArrayKeyValueDatabase getArchiveDatabase() { return archive; } } diff --git a/modMcf/src/org/aion/mcf/ds/DataSourceArray.java b/modMcf/src/org/aion/mcf/ds/DataSourceArray.java index 3de2ce400d..43ce82ce46 100644 --- a/modMcf/src/org/aion/mcf/ds/DataSourceArray.java +++ b/modMcf/src/org/aion/mcf/ds/DataSourceArray.java @@ -2,7 +2,7 @@ import java.io.Closeable; import java.util.Optional; -import org.aion.base.db.Flushable; +import org.aion.interfaces.db.Flushable; import org.aion.util.bytes.ByteUtil; import org.aion.util.conversions.Hex; diff --git a/modMcf/src/org/aion/mcf/ds/ObjectDataSource.java b/modMcf/src/org/aion/mcf/ds/ObjectDataSource.java index 927156b55b..6ff80ef61a 100644 --- a/modMcf/src/org/aion/mcf/ds/ObjectDataSource.java +++ b/modMcf/src/org/aion/mcf/ds/ObjectDataSource.java @@ -2,8 +2,8 @@ import java.io.Closeable; import java.util.Optional; -import org.aion.base.db.Flushable; -import org.aion.base.db.IByteArrayKeyValueDatabase; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; +import org.aion.interfaces.db.Flushable; /** * Object Datasource. @@ -12,10 +12,10 @@ */ public class ObjectDataSource<V> implements Flushable, Closeable { - private IByteArrayKeyValueDatabase src; - Serializer<V, byte[]> serializer; + private ByteArrayKeyValueDatabase src; + private Serializer<V, byte[]> serializer; - public ObjectDataSource(IByteArrayKeyValueDatabase src, Serializer<V, byte[]> serializer) { + public ObjectDataSource(ByteArrayKeyValueDatabase src, Serializer<V, byte[]> serializer) { this.src = src; this.serializer = serializer; } @@ -62,7 +62,7 @@ public V get(byte[] key) { } /** Returns the underlying cache source. */ - protected IByteArrayKeyValueDatabase getSrc() { + protected ByteArrayKeyValueDatabase getSrc() { return src; } diff --git a/modMcf/src/org/aion/mcf/ds/XorDataSource.java b/modMcf/src/org/aion/mcf/ds/XorDataSource.java index d004e5020e..17366d3a89 100644 --- a/modMcf/src/org/aion/mcf/ds/XorDataSource.java +++ b/modMcf/src/org/aion/mcf/ds/XorDataSource.java @@ -5,15 +5,15 @@ import java.util.Iterator; import java.util.Map; import java.util.Optional; -import org.aion.base.db.IByteArrayKeyValueStore; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.interfaces.db.ByteArrayKeyValueStore; +import org.aion.types.ByteArrayWrapper; import org.aion.util.bytes.ByteUtil; -public class XorDataSource implements IByteArrayKeyValueStore { - private final IByteArrayKeyValueStore source; +public class XorDataSource implements ByteArrayKeyValueStore { + private final ByteArrayKeyValueStore source; private final byte[] subKey; - public XorDataSource(IByteArrayKeyValueStore source, byte[] subKey) { + public XorDataSource(ByteArrayKeyValueStore source, byte[] subKey) { this.source = source; this.subKey = subKey; } diff --git a/modMcf/src/org/aion/mcf/evt/IListenerBase.java b/modMcf/src/org/aion/mcf/evt/IListenerBase.java index d6955489bd..d3afcdffe2 100644 --- a/modMcf/src/org/aion/mcf/evt/IListenerBase.java +++ b/modMcf/src/org/aion/mcf/evt/IListenerBase.java @@ -1,15 +1,15 @@ package org.aion.mcf.evt; -import org.aion.base.type.IBlock; -import org.aion.base.type.ITransaction; +import org.aion.interfaces.block.Block; +import org.aion.interfaces.tx.Transaction; import org.aion.mcf.blockchain.IPendingStateInternal; import org.aion.mcf.types.AbstractBlockSummary; import org.aion.mcf.types.AbstractTxReceipt; /** Listener base interface. */ public interface IListenerBase< - BLK extends IBlock<?, ?>, - TX extends ITransaction, + BLK extends Block<?, ?>, + TX extends Transaction, TXR extends AbstractTxReceipt<?>, BS extends AbstractBlockSummary<?, ?, ?, ?>> { diff --git a/modMcf/src/org/aion/mcf/evt/IPowListener.java b/modMcf/src/org/aion/mcf/evt/IPowListener.java index 0e0952b6f2..f483083df1 100644 --- a/modMcf/src/org/aion/mcf/evt/IPowListener.java +++ b/modMcf/src/org/aion/mcf/evt/IPowListener.java @@ -1,9 +1,9 @@ package org.aion.mcf.evt; import java.util.List; -import org.aion.base.type.IBlock; -import org.aion.base.type.ITransaction; -import org.aion.base.type.ITxExecSummary; +import org.aion.interfaces.block.Block; +import org.aion.interfaces.tx.Transaction; +import org.aion.interfaces.tx.TxExecSummary; import org.aion.mcf.types.AbstractBlockSummary; import org.aion.mcf.types.AbstractTxReceipt; @@ -16,8 +16,8 @@ * @param <BS> */ public interface IPowListener< - BLK extends IBlock<?, ?>, - TX extends ITransaction, + BLK extends Block<?, ?>, + TX extends Transaction, TXR extends AbstractTxReceipt<?>, BS extends AbstractBlockSummary<?, ?, ?, ?>> extends IListenerBase<BLK, TX, TXR, BS> { @@ -33,5 +33,5 @@ public interface IPowListener< void onVMTraceCreated(String transactionHash, String trace); - void onTransactionExecuted(ITxExecSummary summary); + void onTransactionExecuted(TxExecSummary summary); } diff --git a/modMcf/src/org/aion/mcf/mine/AbstractMineRunner.java b/modMcf/src/org/aion/mcf/mine/AbstractMineRunner.java index 84eddbe0dc..a24aead720 100644 --- a/modMcf/src/org/aion/mcf/mine/AbstractMineRunner.java +++ b/modMcf/src/org/aion/mcf/mine/AbstractMineRunner.java @@ -1,12 +1,12 @@ package org.aion.mcf.mine; -import org.aion.base.type.IBlock; +import org.aion.interfaces.block.Block; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.slf4j.Logger; /** Abstract Miner. */ -public abstract class AbstractMineRunner<BLK extends IBlock<?, ?>> implements IMineRunner { +public abstract class AbstractMineRunner<BLK extends Block<?, ?>> implements IMineRunner { protected static final Logger LOG = AionLoggerFactory.getLogger(LogEnum.CONS.name()); diff --git a/modMcf/src/org/aion/mcf/mine/IMiner.java b/modMcf/src/org/aion/mcf/mine/IMiner.java index 09e9481353..89d38015a9 100644 --- a/modMcf/src/org/aion/mcf/mine/IMiner.java +++ b/modMcf/src/org/aion/mcf/mine/IMiner.java @@ -1,7 +1,7 @@ package org.aion.mcf.mine; import com.google.common.util.concurrent.ListenableFuture; -import org.aion.base.type.IBlock; +import org.aion.interfaces.block.Block; import org.aion.mcf.types.AbstractBlockHeader; /** @@ -10,7 +10,7 @@ * @param <Blk> * @param <BH> */ -public interface IMiner<Blk extends IBlock<?, ?>, BH extends AbstractBlockHeader> { +public interface IMiner<Blk extends Block<?, ?>, BH extends AbstractBlockHeader> { ListenableFuture<Long> mine(Blk block); diff --git a/modMcf/src/org/aion/mcf/mine/IMinerListener.java b/modMcf/src/org/aion/mcf/mine/IMinerListener.java index 77eccc3e0f..0eae458535 100644 --- a/modMcf/src/org/aion/mcf/mine/IMinerListener.java +++ b/modMcf/src/org/aion/mcf/mine/IMinerListener.java @@ -1,13 +1,13 @@ package org.aion.mcf.mine; -import org.aion.base.type.IBlock; +import org.aion.interfaces.block.Block; /** * Miner Listener interface. * * @param <BLK> */ -public interface IMinerListener<BLK extends IBlock<?, ?>> { +public interface IMinerListener<BLK extends Block<?, ?>> { void miningStarted(); diff --git a/modMcf/src/org/aion/mcf/trie/Cache.java b/modMcf/src/org/aion/mcf/trie/Cache.java index 5ee283f42d..71d437c2af 100644 --- a/modMcf/src/org/aion/mcf/trie/Cache.java +++ b/modMcf/src/org/aion/mcf/trie/Cache.java @@ -1,7 +1,7 @@ package org.aion.mcf.trie; -import static org.aion.base.util.ByteArrayWrapper.wrap; import static org.aion.rlp.Value.fromRlpEncoded; +import static org.aion.types.ByteArrayWrapper.wrap; import java.util.ArrayList; import java.util.Arrays; @@ -14,12 +14,12 @@ import java.util.Map.Entry; import java.util.Optional; import java.util.Set; -import org.aion.base.db.IByteArrayKeyValueStore; -import org.aion.base.util.ByteArrayWrapper; import org.aion.crypto.HashUtil; +import org.aion.interfaces.db.ByteArrayKeyValueStore; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.aion.rlp.Value; +import org.aion.types.ByteArrayWrapper; import org.slf4j.Logger; /** Cache class */ @@ -27,12 +27,12 @@ public class Cache { private static final Logger LOG = AionLoggerFactory.getLogger(LogEnum.DB.name()); - private IByteArrayKeyValueStore dataSource; + private ByteArrayKeyValueStore dataSource; private Map<ByteArrayWrapper, Node> nodes = new LinkedHashMap<>(); private Set<ByteArrayWrapper> removedNodes = new HashSet<>(); private boolean isDirty; - public Cache(IByteArrayKeyValueStore dataSource) { + public Cache(ByteArrayKeyValueStore dataSource) { this.dataSource = dataSource; } @@ -152,7 +152,7 @@ public synchronized Map<ByteArrayWrapper, Node> getNodes() { return nodes; } - public synchronized IByteArrayKeyValueStore getDb() { + public synchronized ByteArrayKeyValueStore getDb() { return dataSource; } @@ -174,7 +174,7 @@ public synchronized IByteArrayKeyValueStore getDb() { // } @SuppressWarnings("OptionalGetWithoutIsPresent") - public synchronized void setDB(IByteArrayKeyValueStore kvds) { + public synchronized void setDB(ByteArrayKeyValueStore kvds) { if (this.dataSource == kvds) { return; } diff --git a/modMcf/src/org/aion/mcf/trie/JournalPruneDataSource.java b/modMcf/src/org/aion/mcf/trie/JournalPruneDataSource.java index 77aa6d207b..49943f3ef9 100644 --- a/modMcf/src/org/aion/mcf/trie/JournalPruneDataSource.java +++ b/modMcf/src/org/aion/mcf/trie/JournalPruneDataSource.java @@ -13,12 +13,12 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import org.aion.base.db.IByteArrayKeyValueDatabase; -import org.aion.base.db.IByteArrayKeyValueStore; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; +import org.aion.interfaces.db.ByteArrayKeyValueStore; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.aion.mcf.ds.ArchivedDataSource; +import org.aion.types.ByteArrayWrapper; import org.slf4j.Logger; /** @@ -28,7 +28,7 @@ * submitted to the underlying DataSource with respect to following inserts. E.g. if the key was * deleted at block N and then inserted at block N + 10 this delete is not passed. */ -public class JournalPruneDataSource implements IByteArrayKeyValueStore { +public class JournalPruneDataSource implements ByteArrayKeyValueStore { private final ReadWriteLock lock = new ReentrantReadWriteLock(); private static final Logger LOG = AionLoggerFactory.getLogger(LogEnum.DB.name()); @@ -61,14 +61,14 @@ public String toString() { Map<ByteArrayWrapper, Ref> refCount = new HashMap<>(); - private IByteArrayKeyValueStore src; + private ByteArrayKeyValueStore src; // block hash => updates private LinkedHashMap<ByteArrayWrapper, Updates> blockUpdates = new LinkedHashMap<>(); private Updates currentUpdates = new Updates(); private AtomicBoolean enabled = new AtomicBoolean(false); private final boolean hasArchive; - public JournalPruneDataSource(IByteArrayKeyValueStore src) { + public JournalPruneDataSource(ByteArrayKeyValueStore src) { this.src = src; this.hasArchive = src instanceof ArchivedDataSource; } @@ -403,11 +403,11 @@ public boolean isEmpty() { } } - public IByteArrayKeyValueStore getSrc() { + public ByteArrayKeyValueStore getSrc() { return src; } - public IByteArrayKeyValueDatabase getArchiveSource() { + public ByteArrayKeyValueDatabase getArchiveSource() { if (!hasArchive) { return null; } else { diff --git a/modMcf/src/org/aion/mcf/trie/SecureTrie.java b/modMcf/src/org/aion/mcf/trie/SecureTrie.java index 771cd79fbc..f4cc8d69f6 100644 --- a/modMcf/src/org/aion/mcf/trie/SecureTrie.java +++ b/modMcf/src/org/aion/mcf/trie/SecureTrie.java @@ -3,15 +3,15 @@ import static org.aion.crypto.HashUtil.h256; import java.util.Arrays; -import org.aion.base.db.IByteArrayKeyValueStore; +import org.aion.interfaces.db.ByteArrayKeyValueStore; public class SecureTrie extends TrieImpl implements Trie { - public SecureTrie(IByteArrayKeyValueStore db) { + public SecureTrie(ByteArrayKeyValueStore db) { this(db, ""); } - public SecureTrie(IByteArrayKeyValueStore db, Object root) { + public SecureTrie(ByteArrayKeyValueStore db, Object root) { super(db, root); } @@ -38,7 +38,7 @@ public void delete(byte[] key) { * Returns a copy of this trie. * * <p>The {@link Cache} object returned will be a copy of this object's cache, but the two will - * share the same references to their data sources ({@link IByteArrayKeyValueStore} objects) and + * share the same references to their data sources ({@link ByteArrayKeyValueStore} objects) and * each {@link Node} will retain the same references as the original node's {@link * org.aion.rlp.Value} objects. * diff --git a/modMcf/src/org/aion/mcf/trie/Trie.java b/modMcf/src/org/aion/mcf/trie/Trie.java index 09942b5262..1c6714cca3 100644 --- a/modMcf/src/org/aion/mcf/trie/Trie.java +++ b/modMcf/src/org/aion/mcf/trie/Trie.java @@ -2,8 +2,8 @@ import java.util.Map; import java.util.Set; -import org.aion.base.db.IByteArrayKeyValueDatabase; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; +import org.aion.types.ByteArrayWrapper; /** * Trie interface for the main data structure in Ethereum which is used to store both the account @@ -99,7 +99,7 @@ public interface Trie { */ Map<ByteArrayWrapper, byte[]> getReferencedTrieNodes(byte[] value, int limit); - long saveFullStateToDatabase(byte[] stateRoot, IByteArrayKeyValueDatabase db); + long saveFullStateToDatabase(byte[] stateRoot, ByteArrayKeyValueDatabase db); - long saveDiffStateToDatabase(byte[] stateRoot, IByteArrayKeyValueDatabase db); + long saveDiffStateToDatabase(byte[] stateRoot, ByteArrayKeyValueDatabase db); } diff --git a/modMcf/src/org/aion/mcf/trie/TrieImpl.java b/modMcf/src/org/aion/mcf/trie/TrieImpl.java index 94a5458e76..ac546947b3 100644 --- a/modMcf/src/org/aion/mcf/trie/TrieImpl.java +++ b/modMcf/src/org/aion/mcf/trie/TrieImpl.java @@ -1,13 +1,13 @@ package org.aion.mcf.trie; import static java.util.Arrays.copyOfRange; -import static org.aion.base.util.ByteArrayWrapper.wrap; import static org.aion.crypto.HashUtil.EMPTY_TRIE_HASH; import static org.aion.rlp.CompactEncoder.binToNibbles; import static org.aion.rlp.CompactEncoder.hasTerminator; import static org.aion.rlp.CompactEncoder.packNibbles; import static org.aion.rlp.CompactEncoder.unpackToNibbles; import static org.aion.rlp.RLP.calcElementPrefixSize; +import static org.aion.types.ByteArrayWrapper.wrap; import static org.aion.util.bytes.ByteUtil.matchingNibbleLength; import static org.spongycastle.util.Arrays.concatenate; @@ -22,10 +22,9 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.aion.base.db.IByteArrayKeyValueDatabase; -import org.aion.base.db.IByteArrayKeyValueStore; -import org.aion.base.util.ByteArrayWrapper; import org.aion.crypto.HashUtil; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; +import org.aion.interfaces.db.ByteArrayKeyValueStore; import org.aion.mcf.trie.scan.CollectFullSetOfNodes; import org.aion.mcf.trie.scan.CollectMappings; import org.aion.mcf.trie.scan.CountNodes; @@ -36,6 +35,7 @@ import org.aion.rlp.RLPItem; import org.aion.rlp.RLPList; import org.aion.rlp.Value; +import org.aion.types.ByteArrayWrapper; import org.aion.util.conversions.Hex; /** @@ -75,11 +75,11 @@ public class TrieImpl implements Trie { private boolean pruningEnabled; - public TrieImpl(IByteArrayKeyValueStore db) { + public TrieImpl(ByteArrayKeyValueStore db) { this(db, ""); } - public TrieImpl(IByteArrayKeyValueStore db, Object root) { + public TrieImpl(ByteArrayKeyValueStore db, Object root) { this(new Cache(db), root); } @@ -643,7 +643,7 @@ private void scanTreeLoop(byte[] hash, ScanAction scanAction) { * @param db database containing keys that need not be explored */ private void scanTreeDiffLoop( - byte[] hash, ScanAction scanAction, IByteArrayKeyValueDatabase db) { + byte[] hash, ScanAction scanAction, ByteArrayKeyValueDatabase db) { ArrayList<byte[]> hashes = new ArrayList<>(); hashes.add(hash); @@ -991,13 +991,13 @@ private void appendHashes(Value node, ArrayList<byte[]> hashes) { } @Override - public long saveFullStateToDatabase(byte[] stateRoot, IByteArrayKeyValueDatabase db) { + public long saveFullStateToDatabase(byte[] stateRoot, ByteArrayKeyValueDatabase db) { ExtractToDatabase traceAction = new ExtractToDatabase(db); traceTrie(stateRoot, traceAction); return traceAction.count; } - private void traceDiffTrie(byte[] stateRoot, ScanAction action, IByteArrayKeyValueDatabase db) { + private void traceDiffTrie(byte[] stateRoot, ScanAction action, ByteArrayKeyValueDatabase db) { synchronized (cache) { Value value = new Value(stateRoot); @@ -1010,7 +1010,7 @@ private void traceDiffTrie(byte[] stateRoot, ScanAction action, IByteArrayKeyVal } @Override - public long saveDiffStateToDatabase(byte[] stateRoot, IByteArrayKeyValueDatabase db) { + public long saveDiffStateToDatabase(byte[] stateRoot, ByteArrayKeyValueDatabase db) { ExtractToDatabase traceAction = new ExtractToDatabase(db); traceDiffTrie(stateRoot, traceAction, db); return traceAction.count; diff --git a/modMcf/src/org/aion/mcf/trie/scan/CollectFullSetOfNodes.java b/modMcf/src/org/aion/mcf/trie/scan/CollectFullSetOfNodes.java index bb30c7d11d..7722899699 100644 --- a/modMcf/src/org/aion/mcf/trie/scan/CollectFullSetOfNodes.java +++ b/modMcf/src/org/aion/mcf/trie/scan/CollectFullSetOfNodes.java @@ -2,8 +2,8 @@ import java.util.HashSet; import java.util.Set; -import org.aion.base.util.ByteArrayWrapper; import org.aion.rlp.Value; +import org.aion.types.ByteArrayWrapper; public class CollectFullSetOfNodes implements ScanAction { Set<ByteArrayWrapper> nodes = new HashSet<>(); diff --git a/modMcf/src/org/aion/mcf/trie/scan/CollectMappings.java b/modMcf/src/org/aion/mcf/trie/scan/CollectMappings.java index a7f8d792b0..09b05f3469 100644 --- a/modMcf/src/org/aion/mcf/trie/scan/CollectMappings.java +++ b/modMcf/src/org/aion/mcf/trie/scan/CollectMappings.java @@ -2,8 +2,8 @@ import java.util.HashMap; import java.util.Map; -import org.aion.base.util.ByteArrayWrapper; import org.aion.rlp.Value; +import org.aion.types.ByteArrayWrapper; /** @author Alexandra Roatis */ public class CollectMappings implements ScanAction { diff --git a/modMcf/src/org/aion/mcf/trie/scan/ExtractToDatabase.java b/modMcf/src/org/aion/mcf/trie/scan/ExtractToDatabase.java index e4e023cdaf..b7dcf1243a 100644 --- a/modMcf/src/org/aion/mcf/trie/scan/ExtractToDatabase.java +++ b/modMcf/src/org/aion/mcf/trie/scan/ExtractToDatabase.java @@ -1,6 +1,6 @@ package org.aion.mcf.trie.scan; -import org.aion.base.db.IByteArrayKeyValueDatabase; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; import org.aion.rlp.Value; /** @author Alexandra Roatis */ @@ -8,10 +8,10 @@ public class ExtractToDatabase implements ScanAction { // only the keys are relevant so the value will be this constant byte[] dummy_value = new byte[] {0}; - IByteArrayKeyValueDatabase db; + ByteArrayKeyValueDatabase db; public long count = 0; - public ExtractToDatabase(IByteArrayKeyValueDatabase _db) { + public ExtractToDatabase(ByteArrayKeyValueDatabase _db) { this.db = _db; } diff --git a/modMcf/src/org/aion/mcf/tx/AbstractTxTask.java b/modMcf/src/org/aion/mcf/tx/AbstractTxTask.java index 09884276e9..852834b4fc 100644 --- a/modMcf/src/org/aion/mcf/tx/AbstractTxTask.java +++ b/modMcf/src/org/aion/mcf/tx/AbstractTxTask.java @@ -4,7 +4,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.Callable; -import org.aion.base.type.ITransaction; +import org.aion.interfaces.tx.Transaction; import org.aion.p2p.INode; import org.aion.p2p.IP2pMgr; import org.aion.p2p.Msg; @@ -13,10 +13,10 @@ * @author jin * @modified jay@Sep.2017 */ -// public abstract class AbstractTxTask<TX extends ITransaction, CHANMGR extends +// public abstract class AbstractTxTask<TX extends Transaction, CHANMGR extends // AbstractChanMgr, CHAN extends AbstractChannel> implements Callable<List<TX>> // { -public abstract class AbstractTxTask<TX extends ITransaction, P2P extends IP2pMgr> +public abstract class AbstractTxTask<TX extends Transaction, P2P extends IP2pMgr> implements Callable<List<TX>> { protected final List<TX> tx; diff --git a/modMcf/src/org/aion/mcf/types/AbstractBlock.java b/modMcf/src/org/aion/mcf/types/AbstractBlock.java index 4e9290e75c..1cb882b729 100644 --- a/modMcf/src/org/aion/mcf/types/AbstractBlock.java +++ b/modMcf/src/org/aion/mcf/types/AbstractBlock.java @@ -4,20 +4,20 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import org.aion.base.type.IBlock; -import org.aion.base.type.IBlockHeader; +import org.aion.interfaces.block.Block; +import org.aion.interfaces.block.BlockHeader; import org.aion.rlp.RLP; /** Abstract Block class. */ -public abstract class AbstractBlock<BH extends IBlockHeader, TX extends AbstractTransaction> - implements IBlock<TX, BH> { +public abstract class AbstractBlock<BH extends BlockHeader, TX extends AbstractTransaction> + implements Block<TX, BH> { protected BH header; protected List<TX> transactionsList = new CopyOnWriteArrayList<>(); @Override - public boolean isEqual(IBlock<TX, BH> block) { + public boolean isEqual(Block<TX, BH> block) { return Arrays.equals(this.getHash(), block.getHash()); } @@ -31,7 +31,7 @@ public boolean isEqual(IBlock<TX, BH> block) { * @param block - possible a son of this * @return - true if this block is parent of param block */ - public boolean isParentOf(IBlock<TX, BH> block) { + public boolean isParentOf(Block<TX, BH> block) { return Arrays.equals(this.getHash(), block.getParentHash()); } diff --git a/modMcf/src/org/aion/mcf/types/AbstractBlockHeader.java b/modMcf/src/org/aion/mcf/types/AbstractBlockHeader.java index 42741ba569..c00fc5ddcb 100644 --- a/modMcf/src/org/aion/mcf/types/AbstractBlockHeader.java +++ b/modMcf/src/org/aion/mcf/types/AbstractBlockHeader.java @@ -1,9 +1,8 @@ package org.aion.mcf.types; import java.math.BigInteger; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.log.AionLoggerFactory; -import org.aion.vm.api.interfaces.Address; import org.spongycastle.util.BigIntegers; /** Abstract BlockHeader. */ @@ -22,7 +21,7 @@ public abstract class AbstractBlockHeader { * The 256-bit address to which all fees collected from the successful * mining of this block be transferred; formally */ - protected AionAddress coinbase; + protected Address coinbase; /* * The SHA3 256-bit hash of the root node of the state trie, after all * transactions are executed and finalisations applied @@ -107,7 +106,7 @@ public Address getCoinbase() { } public void setCoinbase(Address coinbase) { - this.coinbase = (AionAddress) coinbase; + this.coinbase = (Address) coinbase; } public byte[] getStateRoot() { diff --git a/modMcf/src/org/aion/mcf/types/AbstractBlockHeaderWrapper.java b/modMcf/src/org/aion/mcf/types/AbstractBlockHeaderWrapper.java index bc8c1432dc..3e0ee6ed4c 100644 --- a/modMcf/src/org/aion/mcf/types/AbstractBlockHeaderWrapper.java +++ b/modMcf/src/org/aion/mcf/types/AbstractBlockHeaderWrapper.java @@ -1,12 +1,12 @@ package org.aion.mcf.types; import java.util.Arrays; -import org.aion.base.type.IBlockHeader; +import org.aion.interfaces.block.BlockHeader; import org.aion.rlp.RLP; import org.aion.util.conversions.Hex; /** AbstractBlockHeaderWrapper */ -public abstract class AbstractBlockHeaderWrapper<BH extends IBlockHeader> { +public abstract class AbstractBlockHeaderWrapper<BH extends BlockHeader> { protected BH header; diff --git a/modMcf/src/org/aion/mcf/types/AbstractBlockSummary.java b/modMcf/src/org/aion/mcf/types/AbstractBlockSummary.java index dab6452575..13c7f38d85 100644 --- a/modMcf/src/org/aion/mcf/types/AbstractBlockSummary.java +++ b/modMcf/src/org/aion/mcf/types/AbstractBlockSummary.java @@ -5,25 +5,24 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.aion.base.type.AionAddress; -import org.aion.base.type.IBlock; -import org.aion.base.type.ITransaction; -import org.aion.base.type.ITxExecSummary; -import org.aion.base.util.Functional; +import org.aion.interfaces.block.Block; +import org.aion.interfaces.functional.Functional; +import org.aion.interfaces.tx.Transaction; +import org.aion.interfaces.tx.TxExecSummary; +import org.aion.types.Address; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.aion.rlp.RLP; import org.aion.rlp.RLPElement; import org.aion.rlp.RLPList; -import org.aion.vm.api.interfaces.Address; import org.slf4j.Logger; /** AbstractBlockSummary */ public class AbstractBlockSummary< - BLK extends IBlock<?, ?>, - TX extends ITransaction, + BLK extends Block<?, ?>, + TX extends Transaction, TXR extends AbstractTxReceipt<TX>, - TXES extends ITxExecSummary> { + TXES extends TxExecSummary> { protected BLK block; protected Map<Address, BigInteger> rewards; @@ -68,7 +67,7 @@ protected static Map<Address, BigInteger> decodeRewards(RLPList rewards) { new Functional.Function<byte[], Address>() { @Override public Address apply(byte[] bytes) { - return AionAddress.wrap(bytes); + return Address.wrap(bytes); } }, new Functional.Function<byte[], BigInteger>() { diff --git a/modMcf/src/org/aion/mcf/types/AbstractBlockWrapper.java b/modMcf/src/org/aion/mcf/types/AbstractBlockWrapper.java index 91e3d2394b..9fc0e7f7bd 100644 --- a/modMcf/src/org/aion/mcf/types/AbstractBlockWrapper.java +++ b/modMcf/src/org/aion/mcf/types/AbstractBlockWrapper.java @@ -1,14 +1,14 @@ package org.aion.mcf.types; -import static org.aion.base.util.TimeUtils.secondsToMillis; +import static org.aion.util.time.TimeUtils.secondsToMillis; import java.math.BigInteger; import java.util.Arrays; -import org.aion.base.type.IBlock; +import org.aion.interfaces.block.Block; import org.aion.rlp.RLP; /** AbstractBlockWrapper */ -public abstract class AbstractBlockWrapper<BLK extends IBlock<?, ?>> { +public abstract class AbstractBlockWrapper<BLK extends Block<?, ?>> { protected static final long SOLID_BLOCK_DURATION_THRESHOLD = secondsToMillis(60); diff --git a/modMcf/src/org/aion/mcf/types/AbstractTransaction.java b/modMcf/src/org/aion/mcf/types/AbstractTransaction.java index 1653cd0006..4aa48aae84 100644 --- a/modMcf/src/org/aion/mcf/types/AbstractTransaction.java +++ b/modMcf/src/org/aion/mcf/types/AbstractTransaction.java @@ -1,15 +1,15 @@ package org.aion.mcf.types; import java.math.BigInteger; -import org.aion.base.type.ITransaction; -import org.aion.base.vm.VirtualMachineSpecs; import org.aion.crypto.ISignature; +import org.aion.interfaces.tx.Transaction; +import org.aion.interfaces.vm.VirtualMachineSpecs; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Address; import org.slf4j.Logger; -public abstract class AbstractTransaction implements ITransaction { +public abstract class AbstractTransaction implements Transaction { private static final int nrgDigits = 64; diff --git a/modMcf/src/org/aion/mcf/types/AbstractTxReceipt.java b/modMcf/src/org/aion/mcf/types/AbstractTxReceipt.java index bd7eccd8c0..b3a7f5668d 100644 --- a/modMcf/src/org/aion/mcf/types/AbstractTxReceipt.java +++ b/modMcf/src/org/aion/mcf/types/AbstractTxReceipt.java @@ -4,15 +4,14 @@ import java.util.ArrayList; import java.util.List; -import org.aion.base.type.ITransaction; -import org.aion.base.type.ITxReceipt; -import org.aion.base.util.Bytesable; +import org.aion.interfaces.Bytesable; +import org.aion.interfaces.tx.Transaction; +import org.aion.interfaces.tx.TxReceipt; import org.aion.mcf.vm.types.Bloom; -import org.aion.mcf.vm.types.Log; import org.aion.vm.api.interfaces.IExecutionLog; -public abstract class AbstractTxReceipt<TX extends ITransaction> - implements Bytesable<Object>, ITxReceipt<TX, IExecutionLog> { +public abstract class AbstractTxReceipt<TX extends Transaction> + implements Bytesable<Object>, TxReceipt<TX, IExecutionLog> { protected TX transaction; diff --git a/modMcf/src/org/aion/mcf/types/BlockIdentifier.java b/modMcf/src/org/aion/mcf/types/BlockIdentifierImpl.java similarity index 74% rename from modMcf/src/org/aion/mcf/types/BlockIdentifier.java rename to modMcf/src/org/aion/mcf/types/BlockIdentifierImpl.java index 6e3945a79a..0366a7918c 100644 --- a/modMcf/src/org/aion/mcf/types/BlockIdentifier.java +++ b/modMcf/src/org/aion/mcf/types/BlockIdentifierImpl.java @@ -3,13 +3,14 @@ import static org.aion.util.bytes.ByteUtil.byteArrayToLong; import java.math.BigInteger; -import org.aion.base.type.IBlockIdentifier; + +import org.aion.interfaces.block.BlockIdentifier; import org.aion.rlp.RLP; import org.aion.rlp.RLPList; import org.aion.util.conversions.Hex; /** Block identifier holds block hash and number <br> */ -public class BlockIdentifier implements IBlockIdentifier { +public class BlockIdentifierImpl implements BlockIdentifier { /** Block hash */ private byte[] hash; @@ -17,12 +18,12 @@ public class BlockIdentifier implements IBlockIdentifier { /** Block number */ private long number; - public BlockIdentifier(RLPList rlp) { + public BlockIdentifierImpl(RLPList rlp) { this.hash = rlp.get(0).getRLPData(); this.number = byteArrayToLong(rlp.get(1).getRLPData()); } - public BlockIdentifier(byte[] hash, long number) { + public BlockIdentifierImpl(byte[] hash, long number) { this.hash = hash; this.number = number; } @@ -44,6 +45,6 @@ public byte[] getEncoded() { @Override public String toString() { - return "BlockIdentifier {" + "hash=" + Hex.toHexString(hash) + ", number=" + number + '}'; + return "BlockIdentifierImpl {" + "hash=" + Hex.toHexString(hash) + ", number=" + number + '}'; } } diff --git a/modMcf/src/org/aion/mcf/valid/BlockNumberRule.java b/modMcf/src/org/aion/mcf/valid/BlockNumberRule.java index 1ec8d38250..8f5aee230e 100644 --- a/modMcf/src/org/aion/mcf/valid/BlockNumberRule.java +++ b/modMcf/src/org/aion/mcf/valid/BlockNumberRule.java @@ -1,9 +1,9 @@ package org.aion.mcf.valid; import java.util.List; -import org.aion.base.type.IBlockHeader; +import org.aion.interfaces.block.BlockHeader; -public class BlockNumberRule<BH extends IBlockHeader> extends DependentBlockHeaderRule<BH> { +public class BlockNumberRule<BH extends BlockHeader> extends DependentBlockHeaderRule<BH> { @Override public boolean validate(BH header, BH parent, List<RuleError> errors) { diff --git a/modMcf/src/org/aion/mcf/valid/DependentBlockHeaderRule.java b/modMcf/src/org/aion/mcf/valid/DependentBlockHeaderRule.java index 72d1a72c11..31446d0285 100644 --- a/modMcf/src/org/aion/mcf/valid/DependentBlockHeaderRule.java +++ b/modMcf/src/org/aion/mcf/valid/DependentBlockHeaderRule.java @@ -1,12 +1,12 @@ package org.aion.mcf.valid; import java.util.List; -import org.aion.base.type.IBlockHeader; +import org.aion.interfaces.block.BlockHeader; import org.aion.mcf.blockchain.valid.AbstractValidRule; import org.aion.mcf.blockchain.valid.IBlockHeaderValidRule; /** A class of rules that requires memory of the previous block */ -public abstract class DependentBlockHeaderRule<BH extends IBlockHeader> extends AbstractValidRule +public abstract class DependentBlockHeaderRule<BH extends BlockHeader> extends AbstractValidRule implements IBlockHeaderValidRule<BH> { /** diff --git a/modMcf/src/org/aion/mcf/valid/GrandParentBlockHeaderValidator.java b/modMcf/src/org/aion/mcf/valid/GrandParentBlockHeaderValidator.java index 7989ce08d7..46890b53a8 100644 --- a/modMcf/src/org/aion/mcf/valid/GrandParentBlockHeaderValidator.java +++ b/modMcf/src/org/aion/mcf/valid/GrandParentBlockHeaderValidator.java @@ -2,11 +2,11 @@ import java.util.LinkedList; import java.util.List; -import org.aion.base.type.IBlockHeader; +import org.aion.interfaces.block.BlockHeader; import org.aion.mcf.blockchain.valid.IValidRule; import org.slf4j.Logger; -public class GrandParentBlockHeaderValidator<BH extends IBlockHeader> +public class GrandParentBlockHeaderValidator<BH extends BlockHeader> extends AbstractBlockHeaderValidator { private List<GrandParentDependantBlockHeaderRule<BH>> rules; diff --git a/modMcf/src/org/aion/mcf/valid/GrandParentDependantBlockHeaderRule.java b/modMcf/src/org/aion/mcf/valid/GrandParentDependantBlockHeaderRule.java index 217bd038cd..12eae92efd 100644 --- a/modMcf/src/org/aion/mcf/valid/GrandParentDependantBlockHeaderRule.java +++ b/modMcf/src/org/aion/mcf/valid/GrandParentDependantBlockHeaderRule.java @@ -1,10 +1,10 @@ package org.aion.mcf.valid; import java.util.List; -import org.aion.base.type.IBlockHeader; +import org.aion.interfaces.block.BlockHeader; import org.aion.mcf.blockchain.valid.AbstractValidRule; -public abstract class GrandParentDependantBlockHeaderRule<BH extends IBlockHeader> +public abstract class GrandParentDependantBlockHeaderRule<BH extends BlockHeader> extends AbstractValidRule { /** diff --git a/modMcf/src/org/aion/mcf/valid/ParentBlockHeaderValidator.java b/modMcf/src/org/aion/mcf/valid/ParentBlockHeaderValidator.java index 9a6bb4a3a7..8d895a515d 100644 --- a/modMcf/src/org/aion/mcf/valid/ParentBlockHeaderValidator.java +++ b/modMcf/src/org/aion/mcf/valid/ParentBlockHeaderValidator.java @@ -2,13 +2,13 @@ import java.util.LinkedList; import java.util.List; -import org.aion.base.type.IBlockHeader; +import org.aion.interfaces.block.BlockHeader; import org.aion.mcf.blockchain.valid.IBlockHeaderValidRule; import org.aion.mcf.blockchain.valid.IValidRule; import org.slf4j.Logger; /** validation rules depending on parent's block header */ -public class ParentBlockHeaderValidator<BH extends IBlockHeader> +public class ParentBlockHeaderValidator<BH extends BlockHeader> extends AbstractBlockHeaderValidator { private List<DependentBlockHeaderRule<BH>> rules; diff --git a/modMcf/src/org/aion/mcf/valid/TimeStampRule.java b/modMcf/src/org/aion/mcf/valid/TimeStampRule.java index f70c685fbe..8fb2ef6692 100644 --- a/modMcf/src/org/aion/mcf/valid/TimeStampRule.java +++ b/modMcf/src/org/aion/mcf/valid/TimeStampRule.java @@ -1,10 +1,10 @@ package org.aion.mcf.valid; import java.util.List; -import org.aion.base.type.IBlockHeader; +import org.aion.interfaces.block.BlockHeader; /** Validates whether the timestamp of the current block is > the timestamp of the parent block */ -public class TimeStampRule<BH extends IBlockHeader> extends DependentBlockHeaderRule<BH> { +public class TimeStampRule<BH extends BlockHeader> extends DependentBlockHeaderRule<BH> { @Override public boolean validate(BH header, BH dependency, List<RuleError> errors) { diff --git a/modMcf/src/org/aion/mcf/valid/TransactionTypeRule.java b/modMcf/src/org/aion/mcf/valid/TransactionTypeRule.java index f467189f2e..3c111f6257 100644 --- a/modMcf/src/org/aion/mcf/valid/TransactionTypeRule.java +++ b/modMcf/src/org/aion/mcf/valid/TransactionTypeRule.java @@ -1,6 +1,6 @@ package org.aion.mcf.valid; -import org.aion.base.vm.VirtualMachineSpecs; +import org.aion.interfaces.vm.VirtualMachineSpecs; /** * Rules for validating transactions based on allowed types. diff --git a/modMcf/src/org/aion/mcf/vm/types/DataWord.java b/modMcf/src/org/aion/mcf/vm/types/DataWordImpl.java similarity index 83% rename from modMcf/src/org/aion/mcf/vm/types/DataWord.java rename to modMcf/src/org/aion/mcf/vm/types/DataWordImpl.java index 91422654c2..9ca10d8b9a 100644 --- a/modMcf/src/org/aion/mcf/vm/types/DataWord.java +++ b/modMcf/src/org/aion/mcf/vm/types/DataWordImpl.java @@ -3,44 +3,44 @@ import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.Arrays; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.vm.IDataWord; +import org.aion.interfaces.vm.DataWord; +import org.aion.types.ByteArrayWrapper; import org.aion.util.bytes.ByteUtil; import org.aion.util.conversions.Hex; /** * Data word is the basic unit data used by virtual machine. The size of a data word is 128 bits. */ -public class DataWord implements Comparable<DataWord>, IDataWord { +public class DataWordImpl implements Comparable<DataWord>, DataWord { public static final BigInteger MAX_VALUE = BigInteger.valueOf(2).pow(128).subtract(BigInteger.ONE); - public static final DataWord ZERO = new DataWord(0); - public static final DataWord ONE = new DataWord(1); + public static final DataWordImpl ZERO = new DataWordImpl(0); + public static final DataWordImpl ONE = new DataWordImpl(1); public static final int BYTES = 16; private byte[] data; - public DataWord() { + public DataWordImpl() { data = new byte[BYTES]; } - public DataWord(int num) { + public DataWordImpl(int num) { ByteBuffer bb = ByteBuffer.allocate(BYTES); bb.position(12); bb.putInt(num); data = bb.array(); } - public DataWord(long num) { + public DataWordImpl(long num) { ByteBuffer bb = ByteBuffer.allocate(BYTES); bb.position(8); bb.putLong(num); data = bb.array(); } - public DataWord(byte[] data) { + public DataWordImpl(byte[] data) { if (data == null) { throw new NullPointerException("Input data"); } else if (data.length == BYTES) { @@ -53,8 +53,8 @@ public DataWord(byte[] data) { } } - public DataWord(BigInteger num) { - // NOTE: DataWord.value() produces a signed positive BigInteger. The byte array + public DataWordImpl(BigInteger num) { + // NOTE: DataWordImpl.value() produces a signed positive BigInteger. The byte array // representation of such a number must prepend a zero byte so that this can be decoded // correctly. This means that a 16-byte array with a non-zero starting bit will become 17 // bytes when BigInteger::toByteArray is called, and therefore we must remove any leading @@ -76,16 +76,16 @@ public DataWord(BigInteger num) { */ private static byte[] removeLargeBigIntegerLeadingZeroByte(BigInteger number) { byte[] bytes = number.toByteArray(); - return ((bytes.length == (DataWord.BYTES + 1)) && (bytes[0] == 0x0)) + return ((bytes.length == (DataWordImpl.BYTES + 1)) && (bytes[0] == 0x0)) ? Arrays.copyOfRange(bytes, 1, bytes.length) : bytes; } - public DataWord(String data) { + public DataWordImpl(String data) { this(Hex.decode(data)); } - public DataWord(ByteArrayWrapper wrapper) { + public DataWordImpl(ByteArrayWrapper wrapper) { this(wrapper.getData()); } @@ -140,7 +140,7 @@ public boolean isNegative() { public DataWord copy() { byte[] bs = new byte[BYTES]; System.arraycopy(data, 0, bs, 0, BYTES); - return new DataWord(bs); + return new DataWordImpl(bs); } @Override @@ -152,7 +152,7 @@ public boolean equals(Object o) { return false; } - DataWord dataWord = (DataWord) o; + DataWordImpl dataWord = (DataWordImpl) o; return Arrays.equals(data, dataWord.data); } @@ -164,7 +164,7 @@ public int hashCode() { @Override public int compareTo(DataWord o) { - return Arrays.compare(this.data, o.data); + return Arrays.compare(this.data, ((DataWordImpl)o).data); } @Override diff --git a/modMcf/src/org/aion/mcf/vm/types/DoubleDataWord.java b/modMcf/src/org/aion/mcf/vm/types/DoubleDataWord.java index 4a2faa639f..fc2de51648 100644 --- a/modMcf/src/org/aion/mcf/vm/types/DoubleDataWord.java +++ b/modMcf/src/org/aion/mcf/vm/types/DoubleDataWord.java @@ -3,17 +3,18 @@ import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.Arrays; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.vm.IDataWord; + +import org.aion.interfaces.vm.DataWord; +import org.aion.types.ByteArrayWrapper; import org.aion.util.bytes.ByteUtil; import org.aion.util.conversions.Hex; /** - * DoubleDataWord is double the size of the basic unit data (DataWord) used by the VM. A + * DoubleDataWord is double the size of the basic unit data (DataWordImpl) used by the VM. A * DoubleDataWord is 256 bits. Its intended use is strictly within pre-compiled contracts, which * often have need of 32-byte storage keys. */ -public class DoubleDataWord implements Comparable<DoubleDataWord>, IDataWord { +public class DoubleDataWord implements Comparable<DoubleDataWord>, DataWord { public static final BigInteger MAX_VALUE = BigInteger.valueOf(2).pow(256).subtract(BigInteger.ONE); @@ -114,7 +115,7 @@ public boolean isNegative() { } @Override - public IDataWord copy() { + public DataWord copy() { byte[] bs = new byte[BYTES]; System.arraycopy(data, 0, bs, 0, BYTES); return new DoubleDataWord(bs); diff --git a/modMcf/src/org/aion/mcf/vm/types/KernelInterfaceForFastVM.java b/modMcf/src/org/aion/mcf/vm/types/KernelInterfaceForFastVM.java index b7c575ab6f..e2175def29 100644 --- a/modMcf/src/org/aion/mcf/vm/types/KernelInterfaceForFastVM.java +++ b/modMcf/src/org/aion/mcf/vm/types/KernelInterfaceForFastVM.java @@ -1,21 +1,21 @@ package org.aion.mcf.vm.types; import java.math.BigInteger; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.vm.VirtualMachineSpecs; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.interfaces.vm.VirtualMachineSpecs; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.mcf.core.AccountState; import org.aion.mcf.db.IBlockStoreBase; import org.aion.mcf.valid.TxNrgRule; -import org.aion.vm.api.interfaces.Address; import org.aion.vm.api.interfaces.KernelInterface; public class KernelInterfaceForFastVM implements KernelInterface { - private IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> repositoryCache; + private RepositoryCache<AccountState, IBlockStoreBase<?, ?>> repositoryCache; private boolean allowNonceIncrement, isLocalCall; public KernelInterfaceForFastVM( - IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> repositoryCache, + RepositoryCache<AccountState, IBlockStoreBase<?, ?>> repositoryCache, boolean allowNonceIncrement, boolean isLocalCall) { @@ -48,7 +48,7 @@ public void rollback() { this.repositoryCache.rollback(); } - public IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> getRepositoryCache() { + public RepositoryCache<AccountState, IBlockStoreBase<?, ?>> getRepositoryCache() { return this.repositoryCache; } @@ -100,7 +100,7 @@ public byte[] getStorage(Address address, byte[] key) { throw new IllegalStateException( "A zero or empty value was retrieved from storage. Storing zeros is not allowed by the FVM. An incorrect put was previously performed instead of an explicit call to the delete method."); } - return (value == null) ? DataWord.ZERO.getData() : alignValueToWordSizeForGet(value); + return (value == null) ? DataWordImpl.ZERO.getData() : alignValueToWordSizeForGet(value); } @Override @@ -194,7 +194,7 @@ private ByteArrayWrapper alignValueToWordSizeForPut(byte[] value) { if (value.length == DoubleDataWord.BYTES) { return new ByteArrayWrapper(new DoubleDataWord(value).getData()); } else { - DataWord valueAsWord = new DataWord(value); + DataWordImpl valueAsWord = new DataWordImpl(value); return (valueAsWord.isZero()) ? valueAsWord.toWrapper() : new ByteArrayWrapper(valueAsWord.getNoLeadZeroesData()); @@ -211,10 +211,10 @@ private ByteArrayWrapper alignValueToWordSizeForPut(byte[] value) { private byte[] alignValueToWordSizeForGet(ByteArrayWrapper wrappedValue) { byte[] value = wrappedValue.getData(); - if (value.length > DataWord.BYTES) { + if (value.length > DataWordImpl.BYTES) { return new DoubleDataWord(value).getData(); } else { - return new DataWord(value).getData(); + return new DataWordImpl(value).getData(); } } @@ -229,7 +229,7 @@ private ByteArrayWrapper alignDataToWordSize(byte[] data) { if (data.length == DoubleDataWord.BYTES) { return new ByteArrayWrapper(new DoubleDataWord(data).getData()); } else { - return new ByteArrayWrapper(new DataWord(data).getData()); + return new ByteArrayWrapper(new DataWordImpl(data).getData()); } } } diff --git a/modMcf/src/org/aion/mcf/vm/types/Log.java b/modMcf/src/org/aion/mcf/vm/types/Log.java index a8e0e0e5e0..073edae7a9 100644 --- a/modMcf/src/org/aion/mcf/vm/types/Log.java +++ b/modMcf/src/org/aion/mcf/vm/types/Log.java @@ -2,7 +2,7 @@ import java.util.ArrayList; import java.util.List; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.crypto.HashUtil; import org.aion.rlp.RLP; import org.aion.rlp.RLPElement; @@ -11,7 +11,6 @@ import org.aion.util.conversions.Hex; import org.aion.vm.api.interfaces.IBloomFilter; import org.aion.vm.api.interfaces.IExecutionLog; -import org.aion.vm.api.interfaces.Address; /** A log is emitted by the LOGX vm instruction. It's composed of address, topics and data. */ public class Log implements IExecutionLog { @@ -28,7 +27,7 @@ public Log(byte[] rlp) { RLPList topics = (RLPList) logInfo.get(1); RLPItem data = (RLPItem) logInfo.get(2); - this.addr = address.getRLPData() != null ? AionAddress.wrap(address.getRLPData()) : null; + this.addr = address.getRLPData() != null ? Address.wrap(address.getRLPData()) : null; this.data = data.getRLPData() != null ? data.getRLPData() : new byte[] {}; for (RLPElement topic1 : topics) { diff --git a/modMcf/test/org/aion/mcf/account/AccountManagerTest.java b/modMcf/test/org/aion/mcf/account/AccountManagerTest.java index a8805ff199..9c8ea9b955 100644 --- a/modMcf/test/org/aion/mcf/account/AccountManagerTest.java +++ b/modMcf/test/org/aion/mcf/account/AccountManagerTest.java @@ -11,10 +11,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; -import org.aion.vm.api.interfaces.Address; + import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -23,7 +23,7 @@ public class AccountManagerTest { private static AccountManager accountManager = AccountManager.inst(); private Address notRegistered = - AionAddress.wrap("a011111111111111111111111111111101010101010101010101010101010101"); + Address.wrap("a011111111111111111111111111111101010101010101010101010101010101"); private final int DEFAULT_TEST_TIMEOUT = 10; private static ECKey k1; @@ -77,11 +77,11 @@ public void testUnlockAccount() { // unlock 2 accounts assertTrue( accountManager.unlockAccount( - AionAddress.wrap(k1.getAddress()), p1, DEFAULT_TEST_TIMEOUT)); + Address.wrap(k1.getAddress()), p1, DEFAULT_TEST_TIMEOUT)); long timeOutTotal1 = Instant.now().getEpochSecond() + DEFAULT_TEST_TIMEOUT; assertTrue( accountManager.unlockAccount( - AionAddress.wrap(k2.getAddress()), p2, DEFAULT_TEST_TIMEOUT)); + Address.wrap(k2.getAddress()), p2, DEFAULT_TEST_TIMEOUT)); long timeOutTotal2 = Instant.now().getEpochSecond() + DEFAULT_TEST_TIMEOUT; // check account manager @@ -111,8 +111,8 @@ public void testUnlockAccountUpdateTimeout() { // update the timeout from 1s to 2s assertTrue( accountManager.unlockAccount( - AionAddress.wrap(k1.getAddress()), p1, DEFAULT_TEST_TIMEOUT)); - assertTrue(accountManager.unlockAccount(AionAddress.wrap(k1.getAddress()), p1, 20)); + Address.wrap(k1.getAddress()), p1, DEFAULT_TEST_TIMEOUT)); + assertTrue(accountManager.unlockAccount(Address.wrap(k1.getAddress()), p1, 20)); // check that the timeout is updated assertThat(accountManager.getAccounts().get(0).getTimeout()) @@ -133,7 +133,7 @@ public void testUnlockAccountWithNotRegisteredKey() { public void testUnlockAccountWithWrongPassword() { assertFalse( accountManager.unlockAccount( - AionAddress.wrap(k1.getAddress()), "not p1", DEFAULT_TEST_TIMEOUT)); + Address.wrap(k1.getAddress()), "not p1", DEFAULT_TEST_TIMEOUT)); // check that no account has been put into the manager assertThat(accountManager.getAccounts().size()).isEqualTo(0); @@ -144,7 +144,7 @@ public void testUnlockAccountTimeoutGreaterThanMax() { // unlock account with timeout greater than max assertTrue( accountManager.unlockAccount( - AionAddress.wrap(k1.getAddress()), p1, AccountManager.UNLOCK_MAX + 10)); + Address.wrap(k1.getAddress()), p1, AccountManager.UNLOCK_MAX + 10)); // check that the recoded timeout is no bigger than max assertThat(accountManager.getAccounts().get(0).getTimeout()) @@ -153,13 +153,13 @@ public void testUnlockAccountTimeoutGreaterThanMax() { // now update the timeout back to a small value so it can be cleared easily during @After assertTrue( accountManager.unlockAccount( - AionAddress.wrap(k1.getAddress()), p1, DEFAULT_TEST_TIMEOUT)); + Address.wrap(k1.getAddress()), p1, DEFAULT_TEST_TIMEOUT)); } @Test public void testUnlockAccountWithNegativeTimeout() { // try to unlock account with a negative integer as the timeout - assertTrue(accountManager.unlockAccount(AionAddress.wrap(k1.getAddress()), p1, -1)); + assertTrue(accountManager.unlockAccount(Address.wrap(k1.getAddress()), p1, -1)); int expectedTimeout = (int) Instant.now().getEpochSecond() + AccountManager.UNLOCK_DEFAULT; // check that the account is created and added to the manager @@ -176,10 +176,10 @@ public void testLockAccount() { // first unlock an account assertTrue( accountManager.unlockAccount( - AionAddress.wrap(k1.getAddress()), p1, DEFAULT_TEST_TIMEOUT)); + Address.wrap(k1.getAddress()), p1, DEFAULT_TEST_TIMEOUT)); // now try to lock it, the timeout will change - assertTrue(accountManager.lockAccount(AionAddress.wrap(k1.getAddress()), p1)); + assertTrue(accountManager.lockAccount(Address.wrap(k1.getAddress()), p1)); // check that the account is now locked List<Account> accountList = accountManager.getAccounts(); @@ -193,10 +193,10 @@ public void testLockAccountNotInManager() { // first unlock an account assertTrue( accountManager.unlockAccount( - AionAddress.wrap(k1.getAddress()), p1, DEFAULT_TEST_TIMEOUT)); + Address.wrap(k1.getAddress()), p1, DEFAULT_TEST_TIMEOUT)); // try to lock a different account - assertTrue(accountManager.lockAccount(AionAddress.wrap(k2.getAddress()), p2)); + assertTrue(accountManager.lockAccount(Address.wrap(k2.getAddress()), p2)); // check that there is still only the first account in the manager assertThat(accountManager.getAccounts().size()).isEqualTo(1); @@ -215,13 +215,13 @@ public void testLockAccountWithWrongPassword() { // first unlock an account assertTrue( accountManager.unlockAccount( - AionAddress.wrap(k1.getAddress()), p1, DEFAULT_TEST_TIMEOUT + 1)); + Address.wrap(k1.getAddress()), p1, DEFAULT_TEST_TIMEOUT + 1)); // check if its there assertThat(accountManager.getAccounts().size()).isEqualTo(1); // try to lock with wrong password - assertFalse(accountManager.lockAccount(AionAddress.wrap(k1.getAddress()), "not p1")); + assertFalse(accountManager.lockAccount(Address.wrap(k1.getAddress()), "not p1")); } @Test @@ -229,10 +229,10 @@ public void testGetKeyReturned() { // first unlock an account assertTrue( accountManager.unlockAccount( - AionAddress.wrap(k1.getAddress()), p1, DEFAULT_TEST_TIMEOUT)); + Address.wrap(k1.getAddress()), p1, DEFAULT_TEST_TIMEOUT)); // retrieve the key - ECKey ret = accountManager.getKey(AionAddress.wrap(k1.getAddress())); + ECKey ret = accountManager.getKey(Address.wrap(k1.getAddress())); // check equality assertArrayEquals(ret.getAddress(), k1.getAddress()); @@ -243,13 +243,13 @@ public void testGetKeyRemoved() { // first unlock an account assertTrue( accountManager.unlockAccount( - AionAddress.wrap(k1.getAddress()), p1, DEFAULT_TEST_TIMEOUT)); + Address.wrap(k1.getAddress()), p1, DEFAULT_TEST_TIMEOUT)); // lock the account - assertTrue(accountManager.lockAccount(AionAddress.wrap(k1.getAddress()), p1)); + assertTrue(accountManager.lockAccount(Address.wrap(k1.getAddress()), p1)); // retrieve key, but instead it is removed - assertNull(accountManager.getKey(AionAddress.wrap(k1.getAddress()))); + assertNull(accountManager.getKey(Address.wrap(k1.getAddress()))); // check that it was removed assertThat(accountManager.getAccounts().size()).isEqualTo(0); @@ -261,7 +261,7 @@ public void testGetKeyNotInMap() { assertThat(accountManager.getAccounts().size()).isEqualTo(0); // try to get a key not in the manager - assertNull(accountManager.getKey(AionAddress.wrap(k1.getAddress()))); + assertNull(accountManager.getKey(Address.wrap(k1.getAddress()))); } @Test @@ -269,11 +269,11 @@ public void testUnlockAndLockMultipleTimes() { // first an account assertTrue( accountManager.unlockAccount( - AionAddress.wrap(k1.getAddress()), p1, AccountManager.UNLOCK_DEFAULT)); + Address.wrap(k1.getAddress()), p1, AccountManager.UNLOCK_DEFAULT)); assertThat(accountManager.getAccounts().size()).isEqualTo(1); // lock k1 and check that timeout is changed - assertTrue(accountManager.lockAccount(AionAddress.wrap(k1.getAddress()), p1)); + assertTrue(accountManager.lockAccount(Address.wrap(k1.getAddress()), p1)); List<Account> accountsList; accountsList = accountManager.getAccounts(); assertThat(accountsList.size()).isEqualTo(1); @@ -282,7 +282,7 @@ public void testUnlockAndLockMultipleTimes() { // now unlock account with k1 again and check that timeout is changed assertTrue( accountManager.unlockAccount( - AionAddress.wrap(k1.getAddress()), p1, AccountManager.UNLOCK_DEFAULT)); + Address.wrap(k1.getAddress()), p1, AccountManager.UNLOCK_DEFAULT)); assertThat(accountManager.getAccounts().size()).isEqualTo(1); assertThat(accountsList.get(0).getTimeout()) .isEqualTo(Instant.now().getEpochSecond() + AccountManager.UNLOCK_DEFAULT); @@ -290,14 +290,14 @@ public void testUnlockAndLockMultipleTimes() { private static void cleanAccountManager() { // lock all the accounts, which modifies the timeout - accountManager.lockAccount(AionAddress.wrap(k1.getAddress()), p1); - accountManager.lockAccount(AionAddress.wrap(k2.getAddress()), p2); - accountManager.lockAccount(AionAddress.wrap(k3.getAddress()), p3); + accountManager.lockAccount(Address.wrap(k1.getAddress()), p1); + accountManager.lockAccount(Address.wrap(k2.getAddress()), p2); + accountManager.lockAccount(Address.wrap(k3.getAddress()), p3); // remove accounts - accountManager.getKey(AionAddress.wrap(k1.getAddress())); - accountManager.getKey(AionAddress.wrap(k2.getAddress())); - accountManager.getKey(AionAddress.wrap(k3.getAddress())); + accountManager.getKey(Address.wrap(k1.getAddress())); + accountManager.getKey(Address.wrap(k2.getAddress())); + accountManager.getKey(Address.wrap(k3.getAddress())); // check that manager is cleared assertThat(accountManager.getAccounts().size()).isEqualTo(0); diff --git a/modMcf/test/org/aion/mcf/account/KeystoreTest.java b/modMcf/test/org/aion/mcf/account/KeystoreTest.java index 2bc2b902a9..42e4a25435 100644 --- a/modMcf/test/org/aion/mcf/account/KeystoreTest.java +++ b/modMcf/test/org/aion/mcf/account/KeystoreTest.java @@ -13,12 +13,12 @@ import java.util.List; import java.util.Map; import java.util.Random; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; import org.aion.util.bytes.ByteUtil; -import org.aion.vm.api.interfaces.Address; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -130,11 +130,11 @@ public void testAccountExport() { assertEquals(addr.substring(2), ByteUtil.toHexString(key.getAddress())); Map<Address, String> arg = new HashMap<>(); - arg.put(AionAddress.wrap(addr), password); + arg.put(Address.wrap(addr), password); Map<Address, ByteArrayWrapper> export = Keystore.exportAccount(arg); - assertTrue(export.containsKey(AionAddress.wrap(addr))); + assertTrue(export.containsKey(Address.wrap(addr))); assertTrue(export.containsValue(ByteArrayWrapper.wrap(key.getPrivKeyBytes()))); filesToRemove.add(addr); } @@ -149,7 +149,7 @@ public void testAccountBackup() { assertEquals(addr.substring(2), ByteUtil.toHexString(key.getAddress())); Map<Address, String> arg = new HashMap<>(); - arg.put(AionAddress.wrap(addr), password); + arg.put(Address.wrap(addr), password); Map<Address, ByteArrayWrapper> export = Keystore.backupAccount(arg); @@ -158,7 +158,7 @@ public void testAccountBackup() { File f = Keystore.getAccountFile(addr.substring(2), password); assertNotNull(f); - assertTrue(export.containsKey(AionAddress.wrap(addr))); + assertTrue(export.containsKey(Address.wrap(addr))); try { assertTrue(export.containsValue(ByteArrayWrapper.wrap(Files.readAllBytes(f.toPath())))); } catch (IOException e) { diff --git a/modMcf/test/org/aion/mcf/db/AionRepositoryCacheTest.java b/modMcf/test/org/aion/mcf/db/AionRepositoryCacheTest.java index 9b7870c98b..9b047acbc5 100644 --- a/modMcf/test/org/aion/mcf/db/AionRepositoryCacheTest.java +++ b/modMcf/test/org/aion/mcf/db/AionRepositoryCacheTest.java @@ -7,18 +7,18 @@ import java.util.List; import java.util.Map; import java.util.Properties; -import org.aion.base.db.IContractDetails; -import org.aion.base.db.IPruneConfig; -import org.aion.base.db.IRepositoryConfig; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.vm.IDataWord; +import org.aion.interfaces.db.ContractDetails; +import org.aion.interfaces.db.PruneConfig; +import org.aion.interfaces.db.RepositoryConfig; +import org.aion.interfaces.vm.DataWord; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.db.impl.DBVendor; import org.aion.db.impl.DatabaseFactory; import org.aion.mcf.config.CfgPrune; -import org.aion.mcf.vm.types.DataWord; import org.aion.mcf.vm.types.DoubleDataWord; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.db.AionRepositoryCache; import org.aion.zero.impl.db.AionRepositoryImpl; import org.aion.zero.impl.db.ContractDetailsAion; @@ -32,20 +32,20 @@ public class AionRepositoryCacheTest { @Before public void setup() { - IRepositoryConfig repoConfig = - new IRepositoryConfig() { + RepositoryConfig repoConfig = + new RepositoryConfig() { @Override public String getDbPath() { return ""; } @Override - public IPruneConfig getPruneConfig() { + public PruneConfig getPruneConfig() { return new CfgPrune(false); } @Override - public IContractDetails contractDetailsImpl() { + public ContractDetails contractDetailsImpl() { return ContractDetailsAion.createForTesting(0, 1000000).getDetails(); } @@ -70,13 +70,13 @@ public void testGetStorageValueNoSuchAddress() { assertNull( cache.getStorageValue( getNewAddress(), - new DataWord(RandomUtils.nextBytes(DataWord.BYTES)).toWrapper())); + new DataWordImpl(RandomUtils.nextBytes(DataWordImpl.BYTES)).toWrapper())); } @Test public void testGetStorageValueIsSingleZero() { Address address = getNewAddress(); - IDataWord key = new DataWord(RandomUtils.nextBytes(DataWord.BYTES)); + DataWord key = new DataWordImpl(RandomUtils.nextBytes(DataWordImpl.BYTES)); cache.removeStorageRow(address, key.toWrapper()); assertNull(cache.getStorageValue(address, key.toWrapper())); @@ -88,7 +88,7 @@ public void testGetStorageValueIsSingleZero() { @Test public void testGetStorageValueIsDoubleZero() { Address address = getNewAddress(); - IDataWord key = new DataWord(RandomUtils.nextBytes(DataWord.BYTES)); + DataWord key = new DataWordImpl(RandomUtils.nextBytes(DataWordImpl.BYTES)); cache.removeStorageRow(address, key.toWrapper()); assertNull(cache.getStorageValue(address, key.toWrapper())); @@ -100,19 +100,19 @@ public void testGetStorageValueIsDoubleZero() { @Test public void testGetStorageValueWithSingleZeroKey() { Address address = getNewAddress(); - ByteArrayWrapper value = new DataWord(RandomUtils.nextBytes(DataWord.BYTES)).toWrapper(); - cache.addStorageRow(address, DataWord.ZERO.toWrapper(), value); - assertEquals(value, cache.getStorageValue(address, DataWord.ZERO.toWrapper())); + ByteArrayWrapper value = new DataWordImpl(RandomUtils.nextBytes(DataWordImpl.BYTES)).toWrapper(); + cache.addStorageRow(address, DataWordImpl.ZERO.toWrapper(), value); + assertEquals(value, cache.getStorageValue(address, DataWordImpl.ZERO.toWrapper())); value = new DoubleDataWord(RandomUtils.nextBytes(DoubleDataWord.BYTES)).toWrapper(); - cache.addStorageRow(address, DataWord.ZERO.toWrapper(), value); - assertEquals(value, cache.getStorageValue(address, DataWord.ZERO.toWrapper())); + cache.addStorageRow(address, DataWordImpl.ZERO.toWrapper(), value); + assertEquals(value, cache.getStorageValue(address, DataWordImpl.ZERO.toWrapper())); } @Test public void testGetStorageValueWithDoubleZeroKey() { Address address = getNewAddress(); - ByteArrayWrapper value = new DataWord(RandomUtils.nextBytes(DataWord.BYTES)).toWrapper(); + ByteArrayWrapper value = new DataWordImpl(RandomUtils.nextBytes(DataWordImpl.BYTES)).toWrapper(); cache.addStorageRow(address, DoubleDataWord.ZERO.toWrapper(), value); assertEquals(value, cache.getStorageValue(address, DoubleDataWord.ZERO.toWrapper())); @@ -126,12 +126,12 @@ public void testGetStorageValueWithZeroKeyAndValue() { Address address = getNewAddress(); // single-single - cache.removeStorageRow(address, DataWord.ZERO.toWrapper()); - assertNull(cache.getStorageValue(address, DataWord.ZERO.toWrapper())); + cache.removeStorageRow(address, DataWordImpl.ZERO.toWrapper()); + assertNull(cache.getStorageValue(address, DataWordImpl.ZERO.toWrapper())); // single-double - cache.removeStorageRow(address, DataWord.ZERO.toWrapper()); - assertNull(cache.getStorageValue(address, DataWord.ZERO.toWrapper())); + cache.removeStorageRow(address, DataWordImpl.ZERO.toWrapper()); + assertNull(cache.getStorageValue(address, DataWordImpl.ZERO.toWrapper())); // double-single cache.removeStorageRow(address, DoubleDataWord.ZERO.toWrapper()); @@ -145,7 +145,7 @@ public void testGetStorageValueWithZeroKeyAndValue() { @Test public void testOverwriteValueWithSingleZero() { Address address = getNewAddress(); - ByteArrayWrapper key = new DataWord(RandomUtils.nextBytes(DataWord.BYTES)).toWrapper(); + ByteArrayWrapper key = new DataWordImpl(RandomUtils.nextBytes(DataWordImpl.BYTES)).toWrapper(); ByteArrayWrapper value = new DoubleDataWord(RandomUtils.nextBytes(DoubleDataWord.BYTES)).toWrapper(); cache.addStorageRow(address, key, value); @@ -159,7 +159,7 @@ public void testOverwriteValueWithDoubleZero() { Address address = getNewAddress(); ByteArrayWrapper key = new DoubleDataWord(RandomUtils.nextBytes(DoubleDataWord.BYTES)).toWrapper(); - ByteArrayWrapper value = new DataWord(RandomUtils.nextBytes(DataWord.BYTES)).toWrapper(); + ByteArrayWrapper value = new DataWordImpl(RandomUtils.nextBytes(DataWordImpl.BYTES)).toWrapper(); cache.addStorageRow(address, key, value); assertEquals(value, cache.getStorageValue(address, key)); cache.removeStorageRow(address, key); @@ -189,7 +189,7 @@ public void testGetStorageValueEnMass() { /** Returns a new random address. */ private Address getNewAddress() { - return new AionAddress(RandomUtils.nextBytes(Address.SIZE)); + return new Address(RandomUtils.nextBytes(Address.SIZE)); } private List<Address> getAddressesInBulk(int num) { @@ -264,10 +264,10 @@ private List<ByteArrayWrapper> getValuesInBulk(int numValues) { return values; } - /** Returns a random DataWord if isSingleWord is true, otherwise a random DoubleDataWord. */ - private IDataWord getRandomWord(boolean isSingleWord) { + /** Returns a random DataWordImpl if isSingleWord is true, otherwise a random DoubleDataWord. */ + private DataWord getRandomWord(boolean isSingleWord) { return (isSingleWord) - ? new DataWord(RandomUtils.nextBytes(DataWord.BYTES)) + ? new DataWordImpl(RandomUtils.nextBytes(DataWordImpl.BYTES)) : new DoubleDataWord(RandomUtils.nextBytes(DoubleDataWord.BYTES)); } } diff --git a/modMcf/test/org/aion/mcf/db/IContractDetailsTest.java b/modMcf/test/org/aion/mcf/db/IContractDetailsTest.java index 3cbc653f59..ddaa8c301d 100644 --- a/modMcf/test/org/aion/mcf/db/IContractDetailsTest.java +++ b/modMcf/test/org/aion/mcf/db/IContractDetailsTest.java @@ -9,10 +9,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.aion.base.db.IContractDetails; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.vm.IDataWord; -import org.aion.mcf.vm.types.DataWord; +import org.aion.interfaces.db.ContractDetails; +import org.aion.interfaces.vm.DataWord; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.ByteArrayWrapper; import org.aion.mcf.vm.types.DoubleDataWord; import org.aion.util.conversions.Hex; import org.aion.zero.db.AionContractDetailsImpl; @@ -23,7 +23,7 @@ public class IContractDetailsTest { // The two ways of instantiating the cache. - private IContractDetails cache1, cache2; + private ContractDetails cache1, cache2; @Before public void setup() { @@ -51,7 +51,7 @@ public void testGetNoSuchDoubleKey() { @Test public void testPutSingleZeroValue() { - ByteArrayWrapper key = new DataWord(RandomUtils.nextBytes(DataWord.BYTES)).toWrapper(); + ByteArrayWrapper key = new DataWordImpl(RandomUtils.nextBytes(DataWordImpl.BYTES)).toWrapper(); checkGetNonExistentPairing(cache1, key); checkGetNonExistentPairing(cache2, key); @@ -62,7 +62,7 @@ public void testPutSingleZeroValue() { @Test public void testPutDoubleZeroValue() { - ByteArrayWrapper key = new DataWord(RandomUtils.nextBytes(DataWord.BYTES)).toWrapper(); + ByteArrayWrapper key = new DataWordImpl(RandomUtils.nextBytes(DataWordImpl.BYTES)).toWrapper(); checkGetNonExistentPairing(cache1, key); checkGetNonExistentPairing(cache2, key); @@ -73,7 +73,7 @@ public void testPutDoubleZeroValue() { @Test public void testPutSingleZeroKey() { - ByteArrayWrapper value = new DataWord(RandomUtils.nextBytes(DataWord.BYTES)).toWrapper(); + ByteArrayWrapper value = new DataWordImpl(RandomUtils.nextBytes(DataWordImpl.BYTES)).toWrapper(); doPutSingleZeroKeyTest(cache1, value); doPutSingleZeroKeyTest(cache2, value); @@ -84,7 +84,7 @@ public void testPutSingleZeroKey() { @Test public void testPutDoubleZeroKey() { - ByteArrayWrapper value = new DataWord(RandomUtils.nextBytes(DataWord.BYTES)).toWrapper(); + ByteArrayWrapper value = new DataWordImpl(RandomUtils.nextBytes(DataWordImpl.BYTES)).toWrapper(); doPutDoubleZeroKeyTest(cache1, value); doPutDoubleZeroKeyTest(cache2, value); @@ -96,18 +96,18 @@ public void testPutDoubleZeroKey() { @Test public void testPutZeroKeyAndValue() { // Try single-single - cache1.delete(DataWord.ZERO.toWrapper()); - ByteArrayWrapper result = cache1.get(DataWord.ZERO.toWrapper()); + cache1.delete(DataWordImpl.ZERO.toWrapper()); + ByteArrayWrapper result = cache1.get(DataWordImpl.ZERO.toWrapper()); assertNull(result); - cache2.delete(DataWord.ZERO.toWrapper()); - assertNull(cache2.get(DataWord.ZERO.toWrapper())); + cache2.delete(DataWordImpl.ZERO.toWrapper()); + assertNull(cache2.get(DataWordImpl.ZERO.toWrapper())); // Try single-double - cache1.delete(DataWord.ZERO.toWrapper()); - result = cache1.get(DataWord.ZERO.toWrapper()); + cache1.delete(DataWordImpl.ZERO.toWrapper()); + result = cache1.get(DataWordImpl.ZERO.toWrapper()); assertNull(result); - cache2.delete(DataWord.ZERO.toWrapper()); - assertNull(cache2.get(DataWord.ZERO.toWrapper())); + cache2.delete(DataWordImpl.ZERO.toWrapper()); + assertNull(cache2.get(DataWordImpl.ZERO.toWrapper())); // Try double-single cache1.delete(DoubleDataWord.ZERO.toWrapper()); @@ -127,25 +127,25 @@ public void testPutZeroKeyAndValue() { @Test public void testPutKeyValueThenOverwriteValueWithZero() { // single-single - ByteArrayWrapper key = new DataWord(RandomUtils.nextBytes(DataWord.BYTES)).toWrapper(); - ByteArrayWrapper value = new DataWord(RandomUtils.nextBytes(DataWord.BYTES)).toWrapper(); + ByteArrayWrapper key = new DataWordImpl(RandomUtils.nextBytes(DataWordImpl.BYTES)).toWrapper(); + ByteArrayWrapper value = new DataWordImpl(RandomUtils.nextBytes(DataWordImpl.BYTES)).toWrapper(); doPutKeyValueThenOverwriteValueWithZero(cache1, key, value); doPutKeyValueThenOverwriteValueWithZero(cache2, key, value); // single-double - value = new DoubleDataWord(RandomUtils.nextBytes(DataWord.BYTES)).toWrapper(); + value = new DoubleDataWord(RandomUtils.nextBytes(DataWordImpl.BYTES)).toWrapper(); doPutKeyValueThenOverwriteValueWithZero(cache1, key, value); doPutKeyValueThenOverwriteValueWithZero(cache2, key, value); // double-single - key = new DoubleDataWord(RandomUtils.nextBytes(DataWord.BYTES)).toWrapper(); - value = new DataWord(RandomUtils.nextBytes(DataWord.BYTES)).toWrapper(); + key = new DoubleDataWord(RandomUtils.nextBytes(DataWordImpl.BYTES)).toWrapper(); + value = new DataWordImpl(RandomUtils.nextBytes(DataWordImpl.BYTES)).toWrapper(); doPutKeyValueThenOverwriteValueWithZero(cache1, key, value); doPutKeyValueThenOverwriteValueWithZero(cache2, key, value); // double-double - key = new DoubleDataWord(RandomUtils.nextBytes(DataWord.BYTES)).toWrapper(); - value = new DoubleDataWord(RandomUtils.nextBytes(DataWord.BYTES)).toWrapper(); + key = new DoubleDataWord(RandomUtils.nextBytes(DataWordImpl.BYTES)).toWrapper(); + value = new DoubleDataWord(RandomUtils.nextBytes(DataWordImpl.BYTES)).toWrapper(); doPutKeyValueThenOverwriteValueWithZero(cache1, key, value); doPutKeyValueThenOverwriteValueWithZero(cache2, key, value); } @@ -316,33 +316,33 @@ public void testCacheUpdatedAndGetWithOriginalAionContract() { // <------------------------------------------HELPERS-------------------------------------------> /** - * Tests calling get() on a DataWord key that is not in cache -- first on a zero-byte key and + * Tests calling get() on a DataWordImpl key that is not in cache -- first on a zero-byte key and * then on a random key. */ - private void doGetNoSuchSingleKeyTest(IContractDetails cache) { - checkGetNonExistentPairing(cache, DataWord.ZERO.toWrapper()); + private void doGetNoSuchSingleKeyTest(ContractDetails cache) { + checkGetNonExistentPairing(cache, DataWordImpl.ZERO.toWrapper()); checkGetNonExistentPairing( - cache, new DataWord(RandomUtils.nextBytes(DataWord.BYTES)).toWrapper()); + cache, new DataWordImpl(RandomUtils.nextBytes(DataWordImpl.BYTES)).toWrapper()); } /** * Tests calling get() on a DoubleDataWord key that is not in cache -- first on a zero-byte key * and then on a random key. */ - private void doGetNoSuchDoubleKeyTest(IContractDetails cache) { + private void doGetNoSuchDoubleKeyTest(ContractDetails cache) { checkGetNonExistentPairing(cache, DoubleDataWord.ZERO.toWrapper()); checkGetNonExistentPairing( cache, new DoubleDataWord(RandomUtils.nextBytes(DoubleDataWord.BYTES)).toWrapper()); } - /** Tests putting value into cache with a zero-byte DataWord key. */ - private void doPutSingleZeroKeyTest(IContractDetails cache, ByteArrayWrapper value) { - cache.put(DataWord.ZERO.toWrapper(), value); - assertEquals(value, cache.get(DataWord.ZERO.toWrapper())); + /** Tests putting value into cache with a zero-byte DataWordImpl key. */ + private void doPutSingleZeroKeyTest(ContractDetails cache, ByteArrayWrapper value) { + cache.put(DataWordImpl.ZERO.toWrapper(), value); + assertEquals(value, cache.get(DataWordImpl.ZERO.toWrapper())); } /** Tests putting value into cache with a zero-byte DoubleDataWord key. */ - private void doPutDoubleZeroKeyTest(IContractDetails cache, ByteArrayWrapper value) { + private void doPutDoubleZeroKeyTest(ContractDetails cache, ByteArrayWrapper value) { cache.put(DoubleDataWord.ZERO.toWrapper(), value); assertEquals(value, cache.get(DoubleDataWord.ZERO.toWrapper())); } @@ -352,9 +352,9 @@ private void doPutDoubleZeroKeyTest(IContractDetails cache, ByteArrayWrapper val * key and then calling get() on that key. */ private void doPutKeyValueThenOverwriteValueWithZero( - IContractDetails cache, ByteArrayWrapper key, ByteArrayWrapper value) { + ContractDetails cache, ByteArrayWrapper key, ByteArrayWrapper value) { - // Test DataWord. + // Test DataWordImpl. cache.put(key, value); assertEquals(value, cache.get(key)); cache.delete(key); @@ -372,7 +372,7 @@ private void doPutKeyValueThenOverwriteValueWithZero( * n'th pair was deleted. */ private void checkAllPairs( - IContractDetails cache, + ContractDetails cache, List<ByteArrayWrapper> keys, List<ByteArrayWrapper> values, int n) { @@ -395,7 +395,7 @@ private void checkAllPairs( * keys and values, where it is assumed every n'th pair was deleted. */ private void checkStorage( - IContractDetails cache, + ContractDetails cache, List<ByteArrayWrapper> keys, List<ByteArrayWrapper> values, int n) { @@ -422,7 +422,7 @@ private void checkStorage( * Iterates over every key in keys -- which are assumed to exist in cache -- and then deletes * any key-value pair in cache for every n'th key in keys. */ - private void deleteEveryNthEntry(IContractDetails cache, List<ByteArrayWrapper> keys, int n) { + private void deleteEveryNthEntry(ContractDetails cache, List<ByteArrayWrapper> keys, int n) { int count = 1; for (ByteArrayWrapper key : keys) { if (count % n == 0) { @@ -434,7 +434,7 @@ private void deleteEveryNthEntry(IContractDetails cache, List<ByteArrayWrapper> /** Puts all of the key-value pairs in keys and values into cache. */ private void massPutIntoCache( - IContractDetails cache, List<ByteArrayWrapper> keys, List<ByteArrayWrapper> values) { + ContractDetails cache, List<ByteArrayWrapper> keys, List<ByteArrayWrapper> values) { int size = keys.size(); assertEquals(size, values.size()); @@ -470,10 +470,10 @@ private List<ByteArrayWrapper> getValuesInBulk(int numValues) { return values; } - /** Returns a random DataWord if isSingleWord is true, otherwise a random DoubleDataWord. */ - private IDataWord getRandomWord(boolean isSingleWord) { + /** Returns a random DataWordImpl if isSingleWord is true, otherwise a random DoubleDataWord. */ + private DataWord getRandomWord(boolean isSingleWord) { return (isSingleWord) - ? new DataWord(RandomUtils.nextBytes(DataWord.BYTES)) + ? new DataWordImpl(RandomUtils.nextBytes(DataWordImpl.BYTES)) : new DoubleDataWord(RandomUtils.nextBytes(DoubleDataWord.BYTES)); } @@ -481,9 +481,9 @@ private IDataWord getRandomWord(boolean isSingleWord) { * Sets a key-value pair with a zero value via cache.setStorage() and ensures that null is * returned when called on that same key. */ - private void doSetZeroValueViaStorageTest(IContractDetails cache) { + private void doSetZeroValueViaStorageTest(ContractDetails cache) { Map<ByteArrayWrapper, ByteArrayWrapper> storage = new HashMap<>(); - ByteArrayWrapper key = new DataWord(RandomUtils.nextBytes(DataWord.BYTES)).toWrapper(); + ByteArrayWrapper key = new DataWordImpl(RandomUtils.nextBytes(DataWordImpl.BYTES)).toWrapper(); storage.put(key, null); cache.setStorage(storage); checkGetNonExistentPairing(cache, key); @@ -491,7 +491,7 @@ private void doSetZeroValueViaStorageTest(IContractDetails cache) { /** Checks cache returns the expected values given its storage is storage. */ private void checkKeyValueMapping( - IContractDetails cache, Map<ByteArrayWrapper, ByteArrayWrapper> storage) { + ContractDetails cache, Map<ByteArrayWrapper, ByteArrayWrapper> storage) { for (ByteArrayWrapper key : storage.keySet()) { ByteArrayWrapper value = storage.get(key); @@ -528,7 +528,7 @@ private Map<ByteArrayWrapper, ByteArrayWrapper> getKeyValueMappingInBulk( * Assumption: key has no valid value mapping in cache. This method calls cache.get(key) and * checks its result. */ - private void checkGetNonExistentPairing(IContractDetails cache, ByteArrayWrapper key) { + private void checkGetNonExistentPairing(ContractDetails cache, ByteArrayWrapper key) { try { assertNull(cache.get(key)); } catch (AssertionError e) { diff --git a/modMcf/test/org/aion/mcf/ds/DataSourceArrayTest.java b/modMcf/test/org/aion/mcf/ds/DataSourceArrayTest.java index 02fe15fb76..8eb2fdb57c 100644 --- a/modMcf/test/org/aion/mcf/ds/DataSourceArrayTest.java +++ b/modMcf/test/org/aion/mcf/ds/DataSourceArrayTest.java @@ -9,9 +9,9 @@ import java.util.Random; import junitparams.JUnitParamsRunner; import junitparams.Parameters; -import org.aion.base.db.IByteArrayKeyValueDatabase; import org.aion.crypto.HashUtil; import org.aion.db.impl.mockdb.MockDB; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; import org.aion.zero.impl.db.AionBlockStore; import org.junit.After; import org.junit.Before; @@ -28,7 +28,7 @@ public class DataSourceArrayTest { private static List<AionBlockStore.BlockInfo> infoList; - private static IByteArrayKeyValueDatabase db; + private static ByteArrayKeyValueDatabase db; private static DataSourceArray<List<AionBlockStore.BlockInfo>> testIndex; private static final Random random = new Random(); diff --git a/modMcf/test/org/aion/mcf/trie/JournalPruneDataSourceTest.java b/modMcf/test/org/aion/mcf/trie/JournalPruneDataSourceTest.java index d4a59cc47f..77b2a76afa 100644 --- a/modMcf/test/org/aion/mcf/trie/JournalPruneDataSourceTest.java +++ b/modMcf/test/org/aion/mcf/trie/JournalPruneDataSourceTest.java @@ -14,8 +14,8 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import org.aion.base.db.IByteArrayKeyValueDatabase; import org.aion.db.impl.DatabaseFactory; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; import org.aion.log.AionLoggerFactory; import org.junit.After; import org.junit.Before; @@ -26,7 +26,7 @@ public class JournalPruneDataSourceTest { private static final String dbName = "TestDB"; - private static final IByteArrayKeyValueDatabase source_db = DatabaseFactory.connect(dbName); + private static final ByteArrayKeyValueDatabase source_db = DatabaseFactory.connect(dbName); private static JournalPruneDataSource db; private static final byte[] k1 = "key1".getBytes(); diff --git a/modMcf/test/org/aion/mcf/trie/TrieTest.java b/modMcf/test/org/aion/mcf/trie/TrieTest.java index 73ed8f0643..fa32928404 100644 --- a/modMcf/test/org/aion/mcf/trie/TrieTest.java +++ b/modMcf/test/org/aion/mcf/trie/TrieTest.java @@ -2,6 +2,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.aion.crypto.HashUtil.EMPTY_TRIE_HASH; +import static org.aion.util.bytes.ByteUtil.hexStringToBytes; import static org.aion.util.bytes.ByteUtil.intToBytes; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -18,7 +19,7 @@ import java.util.Set; import junitparams.JUnitParamsRunner; import junitparams.Parameters; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; import org.aion.crypto.HashUtil; import org.aion.db.impl.mockdb.MockDB; import org.aion.rlp.Value; @@ -969,11 +970,11 @@ public void testGetMissingNodes_wIncompleteTrie() { // removing two of the nodes from the trie Set<ByteArrayWrapper> expected = new HashSet<>(); expected.add( - new ByteArrayWrapper( - "59c2a26cebd0ed50053bba185a7d13e1ae58314e2c37d46c1f7b885fd93b687a")); + new ByteArrayWrapper(hexStringToBytes( + "59c2a26cebd0ed50053bba185a7d13e1ae58314e2c37d46c1f7b885fd93b687a"))); expected.add( - new ByteArrayWrapper( - "ddf1b495a3e98e1897a9b1257d4172d59fcbe0dba23b8b87812ca2a55919d9ab")); + new ByteArrayWrapper(hexStringToBytes( + "ddf1b495a3e98e1897a9b1257d4172d59fcbe0dba23b8b87812ca2a55919d9ab"))); for (ByteArrayWrapper key : expected) { mockDB.delete(key.getData()); diff --git a/modMcf/test/org/aion/valid/BlockNumberRuleTest.java b/modMcf/test/org/aion/valid/BlockNumberRuleTest.java index 9bdab60de7..f1179aa1f4 100644 --- a/modMcf/test/org/aion/valid/BlockNumberRuleTest.java +++ b/modMcf/test/org/aion/valid/BlockNumberRuleTest.java @@ -5,7 +5,7 @@ import java.util.LinkedList; import java.util.List; -import org.aion.base.type.IBlockHeader; +import org.aion.interfaces.block.BlockHeader; import org.aion.mcf.blockchain.valid.IValidRule; import org.aion.mcf.valid.BlockNumberRule; import org.junit.Before; @@ -20,8 +20,8 @@ */ public class BlockNumberRuleTest { - @Mock IBlockHeader mockChildBH; - @Mock IBlockHeader mockParentBH; + @Mock BlockHeader mockChildBH; + @Mock BlockHeader mockParentBH; @Before public void setUp() { diff --git a/modMcf/test/org/aion/valid/DifficultyRuleTest.java b/modMcf/test/org/aion/valid/DifficultyRuleTest.java index 1ffda0b84f..4558075f0a 100644 --- a/modMcf/test/org/aion/valid/DifficultyRuleTest.java +++ b/modMcf/test/org/aion/valid/DifficultyRuleTest.java @@ -8,15 +8,15 @@ import java.math.BigInteger; import java.util.LinkedList; import java.util.List; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.mcf.blockchain.IChainCfg; import org.aion.mcf.blockchain.valid.IValidRule.RuleError; import org.aion.mcf.core.IDifficultyCalculator; import org.aion.util.bytes.ByteUtil; +import org.aion.zero.impl.types.AionBlock; import org.aion.zero.impl.valid.AionDifficultyRule; import org.aion.zero.types.A0BlockHeader; import org.aion.zero.types.AionTransaction; -import org.aion.zero.types.IAionBlock; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -31,7 +31,7 @@ public class DifficultyRuleTest { private A0BlockHeader grandParentHeader; private A0BlockHeader parentHeader; private A0BlockHeader currentHeader; - @Mock IChainCfg<IAionBlock, AionTransaction> mockChainCfg; + @Mock IChainCfg<AionBlock, AionTransaction> mockChainCfg; @Mock IDifficultyCalculator mockDiffCalculator; @Before @@ -43,7 +43,7 @@ public void setUp() { (byte) 0x01, 1, new byte[32], - AionAddress.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), new byte[256], ByteUtil.intToBytes(1), null, @@ -57,7 +57,7 @@ public void setUp() { (byte) 0x01, 2, new byte[32], - AionAddress.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), new byte[256], ByteUtil.intToBytes(1), null, @@ -80,7 +80,7 @@ public void testInvalidDifficultyLength() { (byte) 0x01, 3, new byte[32], - AionAddress.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), new byte[256], new byte[17], null, @@ -113,7 +113,7 @@ public void testDifficultyLength() { (byte) 0x01, 3, new byte[32], - AionAddress.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), new byte[256], ByteUtil.bigIntegerToBytes(BigInteger.ONE, 16), null, diff --git a/modMcf/test/org/aion/valid/TimeStampRuleTest.java b/modMcf/test/org/aion/valid/TimeStampRuleTest.java index c317f04a8b..a03f56dca1 100644 --- a/modMcf/test/org/aion/valid/TimeStampRuleTest.java +++ b/modMcf/test/org/aion/valid/TimeStampRuleTest.java @@ -5,7 +5,7 @@ import java.util.LinkedList; import java.util.List; -import org.aion.base.type.IBlockHeader; +import org.aion.interfaces.block.BlockHeader; import org.aion.mcf.blockchain.valid.IValidRule; import org.aion.mcf.valid.TimeStampRule; import org.junit.Before; @@ -20,8 +20,8 @@ */ public class TimeStampRuleTest { - @Mock IBlockHeader mockHeader; - @Mock IBlockHeader mockDependency; + @Mock BlockHeader mockHeader; + @Mock BlockHeader mockDependency; @Before public void setUp() throws Exception { diff --git a/modP2pImpl/build.gradle b/modP2pImpl/build.gradle index 336add7c50..ae18768b87 100644 --- a/modP2pImpl/build.gradle +++ b/modP2pImpl/build.gradle @@ -9,7 +9,8 @@ sourceSets { } dependencies { - compile project(':modUtil') + compile 'network.aion:util4j:0.4.0' + compile project(':modP2p') compile project(':modLogger') compile files('../lib/miniupnpc_linux.jar') diff --git a/modPrecompiled/build.gradle b/modPrecompiled/build.gradle index a7a5e4ea99..963a049a99 100644 --- a/modPrecompiled/build.gradle +++ b/modPrecompiled/build.gradle @@ -3,11 +3,12 @@ test.dependsOn copyNativeLibsForModuleTests clean.dependsOn deleteNativeLibs dependencies { - compile project(':modAionBase') + compile 'network.aion:vm-api4j:0.4.0' + compile 'network.aion:util4j:0.4.0' + compile project(':modMcf') compile project(':modCrypto') compile project(':modAion') - compile project(':aion_vm_api') compile 'com.google.guava:guava:25.1-jre' compile 'com.google.code.findbugs:jsr305:3.0.2' compile group: 'org.apache.commons', name: 'commons-collections4', version: '4.0' diff --git a/modPrecompiled/src/module-info.java b/modPrecompiled/src/module-info.java index 7a109fb2ab..c06d793bdd 100644 --- a/modPrecompiled/src/module-info.java +++ b/modPrecompiled/src/module-info.java @@ -1,14 +1,13 @@ module aion.precompiled { requires aion.zero; requires aion.mcf; - requires aion.base; requires aion.crypto; requires slf4j.api; requires jsr305; requires commons.collections4; requires com.google.common; - requires aion.vm.api; requires aion.util; + requires aion.vm.api; exports org.aion.precompiled; exports org.aion.precompiled.type; diff --git a/modPrecompiled/src/org/aion/precompiled/ContractFactory.java b/modPrecompiled/src/org/aion/precompiled/ContractFactory.java index e8cda8205f..b968e33e51 100644 --- a/modPrecompiled/src/org/aion/precompiled/ContractFactory.java +++ b/modPrecompiled/src/org/aion/precompiled/ContractFactory.java @@ -1,6 +1,6 @@ package org.aion.precompiled; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.mcf.config.CfgFork; import org.aion.mcf.vm.types.KernelInterfaceForFastVM; import org.aion.precompiled.contracts.ATB.TokenBridgeContract; @@ -9,7 +9,6 @@ import org.aion.precompiled.contracts.TXHashContract; import org.aion.precompiled.contracts.TotalCurrencyContract; import org.aion.precompiled.type.PrecompiledContract; -import org.aion.vm.api.interfaces.Address; import org.aion.vm.api.interfaces.KernelInterface; import org.aion.vm.api.interfaces.TransactionContext; @@ -68,11 +67,11 @@ public PrecompiledContract getPrecompiledContract( new TokenBridgeContract( context, ((KernelInterfaceForFastVM) track).getRepositoryCache(), - AionAddress.wrap(ADDR_TOKEN_BRIDGE_INITIAL_OWNER), - AionAddress.wrap(ADDR_TOKEN_BRIDGE)); + Address.wrap(ADDR_TOKEN_BRIDGE_INITIAL_OWNER), + Address.wrap(ADDR_TOKEN_BRIDGE)); if (!context.getOriginAddress() - .equals(AionAddress.wrap(ADDR_TOKEN_BRIDGE_INITIAL_OWNER)) + .equals(Address.wrap(ADDR_TOKEN_BRIDGE_INITIAL_OWNER)) && !contract.isInitialized()) { return null; } @@ -90,7 +89,7 @@ public PrecompiledContract getPrecompiledContract( : new TotalCurrencyContract( ((KernelInterfaceForFastVM) track).getRepositoryCache(), context.getSenderAddress(), - AionAddress.wrap(ADDR_OWNER)); + Address.wrap(ADDR_OWNER)); default: return null; } @@ -121,7 +120,7 @@ public static boolean isPrecompiledContract(Address address) { * @return the contract address. */ public static Address getTotalCurrencyContractAddress() { - return AionAddress.wrap(ADDR_TOTAL_CURRENCY); + return Address.wrap(ADDR_TOTAL_CURRENCY); } /** @@ -130,7 +129,7 @@ public static Address getTotalCurrencyContractAddress() { * @return the contract address */ public static Address getEdVerifyContractAddress() { - return AionAddress.wrap(ADDR_ED_VERIFY); + return Address.wrap(ADDR_ED_VERIFY); } /** @@ -139,7 +138,7 @@ public static Address getEdVerifyContractAddress() { * @return the contract address */ public static Address getTxHashContractAddress() { - return AionAddress.wrap(ADDR_TX_HASH); + return Address.wrap(ADDR_TX_HASH); } /** @@ -148,6 +147,6 @@ public static Address getTxHashContractAddress() { * @return the contract address */ public static Address getBlake2bHashContractAddress() { - return AionAddress.wrap(ADDR_BLAKE2B_HASH); + return Address.wrap(ADDR_BLAKE2B_HASH); } } diff --git a/modPrecompiled/src/org/aion/precompiled/contracts/ATB/BridgeController.java b/modPrecompiled/src/org/aion/precompiled/contracts/ATB/BridgeController.java index a4dd1dde9a..775049d45f 100644 --- a/modPrecompiled/src/org/aion/precompiled/contracts/ATB/BridgeController.java +++ b/modPrecompiled/src/org/aion/precompiled/contracts/ATB/BridgeController.java @@ -10,14 +10,14 @@ import java.util.Collections; import java.util.List; import javax.annotation.Nonnull; -import org.aion.base.util.ByteUtil; import org.aion.crypto.ISignature; import org.aion.crypto.SignatureFac; import org.aion.mcf.vm.types.Log; import org.aion.precompiled.PrecompiledResultCode; import org.aion.precompiled.PrecompiledTransactionResult; import org.aion.precompiled.PrecompiledUtilities; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Address; +import org.aion.util.bytes.ByteUtil; import org.aion.vm.api.interfaces.TransactionSideEffects; /** diff --git a/modPrecompiled/src/org/aion/precompiled/contracts/ATB/BridgeDeserializer.java b/modPrecompiled/src/org/aion/precompiled/contracts/ATB/BridgeDeserializer.java index aefdf1ab5d..1079e3d461 100644 --- a/modPrecompiled/src/org/aion/precompiled/contracts/ATB/BridgeDeserializer.java +++ b/modPrecompiled/src/org/aion/precompiled/contracts/ATB/BridgeDeserializer.java @@ -2,7 +2,7 @@ import java.math.BigInteger; import javax.annotation.Nonnull; -import org.aion.base.util.ByteUtil; +import org.aion.util.bytes.ByteUtil; public class BridgeDeserializer { diff --git a/modPrecompiled/src/org/aion/precompiled/contracts/ATB/BridgeFuncSig.java b/modPrecompiled/src/org/aion/precompiled/contracts/ATB/BridgeFuncSig.java index 3555cefa22..96c7d15d45 100644 --- a/modPrecompiled/src/org/aion/precompiled/contracts/ATB/BridgeFuncSig.java +++ b/modPrecompiled/src/org/aion/precompiled/contracts/ATB/BridgeFuncSig.java @@ -5,7 +5,7 @@ import java.util.HashMap; import java.util.Map; import javax.annotation.Nonnull; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; enum BridgeFuncSig { SIG_CHANGE_OWNER("changeOwner(address)"), diff --git a/modPrecompiled/src/org/aion/precompiled/contracts/ATB/BridgeStorageConnector.java b/modPrecompiled/src/org/aion/precompiled/contracts/ATB/BridgeStorageConnector.java index f200c6776b..f4ed6fca4c 100644 --- a/modPrecompiled/src/org/aion/precompiled/contracts/ATB/BridgeStorageConnector.java +++ b/modPrecompiled/src/org/aion/precompiled/contracts/ATB/BridgeStorageConnector.java @@ -3,16 +3,16 @@ import java.math.BigInteger; import java.util.Arrays; import javax.annotation.Nonnull; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.crypto.HashUtil; import org.aion.mcf.core.AccountState; import org.aion.mcf.db.IBlockStoreBase; -import org.aion.mcf.vm.types.DataWord; import org.aion.mcf.vm.types.DoubleDataWord; import org.aion.precompiled.PrecompiledUtilities; -import org.aion.vm.api.interfaces.Address; +import org.aion.util.bytes.ByteUtil; /** * Storage layout mapping as the following: @@ -40,17 +40,17 @@ public class BridgeStorageConnector { private enum S_OFFSET { - OWNER(new DataWord(0x0)), - NEW_OWNER(new DataWord(0x1)), - MEMBER_COUNT(new DataWord(0x2)), - MIN_THRESH(new DataWord(0x3)), - RING_LOCKED(new DataWord(0x4)), - RELAYER(new DataWord(0x5)), - INITIALIZED(new DataWord(0x42)); + OWNER(new DataWordImpl(0x0)), + NEW_OWNER(new DataWordImpl(0x1)), + MEMBER_COUNT(new DataWordImpl(0x2)), + MIN_THRESH(new DataWordImpl(0x3)), + RING_LOCKED(new DataWordImpl(0x4)), + RELAYER(new DataWordImpl(0x5)), + INITIALIZED(new DataWordImpl(0x42)); - private final DataWord offset; + private final DataWordImpl offset; - S_OFFSET(DataWord offset) { + S_OFFSET(DataWordImpl offset) { this.offset = offset; } } @@ -66,18 +66,18 @@ private enum M_ID { } } - private final IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> track; + private final RepositoryCache<AccountState, IBlockStoreBase<?, ?>> track; private final Address contractAddress; public BridgeStorageConnector( - @Nonnull final IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> track, + @Nonnull final RepositoryCache<AccountState, IBlockStoreBase<?, ?>> track, @Nonnull final Address contractAddress) { this.track = track; this.contractAddress = contractAddress; } public void setInitialized(final boolean initialized) { - DataWord init = initialized ? new DataWord(1) : new DataWord(0); + DataWordImpl init = initialized ? new DataWordImpl(1) : new DataWordImpl(0); this.setWORD(S_OFFSET.INITIALIZED.offset, init); } @@ -115,7 +115,7 @@ public byte[] getRelayer() { public void setMemberCount(int amount) { assert amount >= 0 : "amount must be positive"; - this.setWORD(S_OFFSET.MEMBER_COUNT.offset, new DataWord(amount)); + this.setWORD(S_OFFSET.MEMBER_COUNT.offset, new DataWordImpl(amount)); } public int getMemberCount() { @@ -126,7 +126,7 @@ public int getMemberCount() { public void setMinThresh(int amount) { assert amount >= 0 : "amount must be positive"; - this.setWORD(S_OFFSET.MIN_THRESH.offset, new DataWord(amount)); + this.setWORD(S_OFFSET.MIN_THRESH.offset, new DataWordImpl(amount)); } public int getMinThresh() { @@ -138,7 +138,7 @@ public int getMinThresh() { // TODO: this can be optimized public void setRingLocked(boolean value) { - DataWord lockedDw = value ? new DataWord(1) : new DataWord(0); + DataWordImpl lockedDw = value ? new DataWordImpl(1) : new DataWordImpl(0); this.setWORD(S_OFFSET.RING_LOCKED.offset, lockedDw); } @@ -154,15 +154,15 @@ public boolean getRingLocked() { public void setActiveMember(@Nonnull final byte[] key, final boolean value) { assert key.length == 32; byte[] h = ByteUtil.chop(HashUtil.h256(ByteUtil.merge(M_ID.ACTIVE_MAP.id, key))); - DataWord hWord = new DataWord(h); - DataWord b = value ? new DataWord(1) : new DataWord(0); + DataWordImpl hWord = new DataWordImpl(h); + DataWordImpl b = value ? new DataWordImpl(1) : new DataWordImpl(0); this.setWORD(hWord, b); } public boolean getActiveMember(@Nonnull final byte[] key) { assert key.length == 32; byte[] h = ByteUtil.chop(HashUtil.h256(ByteUtil.merge(M_ID.ACTIVE_MAP.id, key))); - DataWord hWord = new DataWord(h); + DataWordImpl hWord = new DataWordImpl(h); // C1 covered by getWORD byte[] activeMemberWord = this.getWORD(hWord); @@ -182,7 +182,7 @@ public void setBundle(@Nonnull final byte[] key, @Nonnull final byte[] value) { assert value.length == 32; byte[] h = ByteUtil.chop(HashUtil.h256(ByteUtil.merge(M_ID.BUNDLE_MAP.id, key))); - DataWord hWord = new DataWord(h); + DataWordImpl hWord = new DataWordImpl(h); this.setDWORD(hWord, value); } @@ -198,7 +198,7 @@ public void setBundle(@Nonnull final byte[] key, @Nonnull final byte[] value) { public byte[] getBundle(@Nonnull final byte[] key) { assert key.length == 32; byte[] h = ByteUtil.chop(HashUtil.h256(ByteUtil.merge(M_ID.BUNDLE_MAP.id, key))); - DataWord hWord = new DataWord(h); + DataWordImpl hWord = new DataWordImpl(h); byte[] bundleDoubleWord = this.getDWORD(hWord); if (bundleDoubleWord == null) return ByteUtil.EMPTY_WORD; @@ -211,14 +211,14 @@ public byte[] getBundle(@Nonnull final byte[] key) { // DWORD helpers - private byte[] getWORD(@Nonnull final DataWord key) { + private byte[] getWORD(@Nonnull final DataWordImpl key) { ByteArrayWrapper word = this.track.getStorageValue(contractAddress, key.toWrapper()); // C1 if (word == null || Arrays.equals(word.getData(), ByteUtil.EMPTY_HALFWORD)) return null; return alignBytes(word.getData()); } - private void setWORD(@Nonnull final DataWord key, @Nonnull final DataWord word) { + private void setWORD(@Nonnull final DataWordImpl key, @Nonnull final DataWordImpl word) { if (word.isZero()) { this.track.removeStorageRow(contractAddress, key.toWrapper()); } else { @@ -229,7 +229,7 @@ private void setWORD(@Nonnull final DataWord key, @Nonnull final DataWord word) } } - private void setDWORD(@Nonnull final DataWord key, @Nonnull final byte[] dword) { + private void setDWORD(@Nonnull final DataWordImpl key, @Nonnull final byte[] dword) { assert dword.length > 16; DoubleDataWord ddw = new DoubleDataWord(dword); if (ddw.isZero()) { @@ -239,7 +239,7 @@ private void setDWORD(@Nonnull final DataWord key, @Nonnull final byte[] dword) } } - private byte[] getDWORD(@Nonnull final DataWord key) { + private byte[] getDWORD(@Nonnull final DataWordImpl key) { ByteArrayWrapper word = this.track.getStorageValue(contractAddress, key.toWrapper()); if (word == null) return null; @@ -252,8 +252,8 @@ private byte[] alignBytes(byte[] unalignedBytes) { return null; } - return (unalignedBytes.length > DataWord.BYTES) + return (unalignedBytes.length > DataWordImpl.BYTES) ? new DoubleDataWord(unalignedBytes).getData() - : new DataWord(unalignedBytes).getData(); + : new DataWordImpl(unalignedBytes).getData(); } } diff --git a/modPrecompiled/src/org/aion/precompiled/contracts/ATB/BridgeUtilities.java b/modPrecompiled/src/org/aion/precompiled/contracts/ATB/BridgeUtilities.java index 75be937d40..975a67d1b6 100644 --- a/modPrecompiled/src/org/aion/precompiled/contracts/ATB/BridgeUtilities.java +++ b/modPrecompiled/src/org/aion/precompiled/contracts/ATB/BridgeUtilities.java @@ -4,9 +4,9 @@ import java.nio.ByteBuffer; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import org.aion.base.util.ByteUtil; import org.aion.crypto.HashUtil; import org.aion.precompiled.PrecompiledUtilities; +import org.aion.util.bytes.ByteUtil; public class BridgeUtilities { diff --git a/modPrecompiled/src/org/aion/precompiled/contracts/ATB/TokenBridgeContract.java b/modPrecompiled/src/org/aion/precompiled/contracts/ATB/TokenBridgeContract.java index 4bf228a854..08c9062137 100644 --- a/modPrecompiled/src/org/aion/precompiled/contracts/ATB/TokenBridgeContract.java +++ b/modPrecompiled/src/org/aion/precompiled/contracts/ATB/TokenBridgeContract.java @@ -11,15 +11,14 @@ import java.math.BigInteger; import javax.annotation.Nonnull; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; import org.aion.mcf.core.AccountState; import org.aion.mcf.db.IBlockStoreBase; -import org.aion.mcf.vm.types.DataWord; import org.aion.precompiled.PrecompiledResultCode; import org.aion.precompiled.PrecompiledTransactionResult; import org.aion.precompiled.type.StatefulPrecompiledContract; -import org.aion.vm.api.interfaces.Address; import org.aion.vm.api.interfaces.TransactionContext; import org.aion.zero.types.AionInternalTx; @@ -30,7 +29,7 @@ public class TokenBridgeContract extends StatefulPrecompiledContract implements // queries private final TransactionContext context; - private final IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> track; + private final RepositoryCache<AccountState, IBlockStoreBase<?, ?>> track; private final BridgeStorageConnector connector; private final BridgeController controller; @@ -41,7 +40,7 @@ public class TokenBridgeContract extends StatefulPrecompiledContract implements public TokenBridgeContract( @Nonnull final TransactionContext context, - @Nonnull final IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> track, + @Nonnull final RepositoryCache<AccountState, IBlockStoreBase<?, ?>> track, @Nonnull final Address ownerAddress, @Nonnull final Address contractAddress) { super(track); @@ -244,7 +243,7 @@ private PrecompiledTransactionResult success(@Nonnull final byte[] response) { private boolean isFromAddress(byte[] address) { if (address == null) return false; - return this.context.getSenderAddress().equals(AionAddress.wrap(address)); + return this.context.getSenderAddress().equals(Address.wrap(address)); } /** @@ -267,9 +266,9 @@ public PrecompiledTransactionResult transfer( // assemble an internal transaction Address from = this.contractAddress; - Address recipient = new AionAddress(to); + Address recipient = new Address(to); BigInteger nonce = this.track.getNonce(from); - DataWord valueToSend = new DataWord(value); + DataWordImpl valueToSend = new DataWordImpl(value); byte[] dataToSend = new byte[0]; AionInternalTx tx = newInternalTx(from, recipient, nonce, valueToSend, dataToSend, "call"); @@ -291,7 +290,7 @@ public PrecompiledTransactionResult transfer( * <p>NOTE: copied from {@code Callback} */ private AionInternalTx newInternalTx( - Address from, Address to, BigInteger nonce, DataWord value, byte[] data, String note) { + Address from, Address to, BigInteger nonce, DataWordImpl value, byte[] data, String note) { byte[] parentHash = context.getTransactionHash(); int depth = context.getTransactionStackDepth(); int index = context.getSideEffects().getInternalTransactions().size(); @@ -300,7 +299,7 @@ private AionInternalTx newInternalTx( parentHash, depth, index, - new DataWord(nonce).getData(), + new DataWordImpl(nonce).getData(), from, to, value.getData(), diff --git a/modPrecompiled/src/org/aion/precompiled/contracts/AionAuctionContract.java b/modPrecompiled/src/org/aion/precompiled/contracts/AionAuctionContract.java index 9932e426f3..b467439b70 100644 --- a/modPrecompiled/src/org/aion/precompiled/contracts/AionAuctionContract.java +++ b/modPrecompiled/src/org/aion/precompiled/contracts/AionAuctionContract.java @@ -15,9 +15,10 @@ import java.util.Set; import java.util.Timer; import java.util.TimerTask; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; import org.aion.crypto.ed25519.ECKeyEd25519; @@ -25,12 +26,10 @@ import org.aion.mcf.core.AccountState; import org.aion.mcf.core.IBlockchain; import org.aion.mcf.db.IBlockStoreBase; -import org.aion.mcf.vm.types.DataWord; import org.aion.mcf.vm.types.DoubleDataWord; import org.aion.precompiled.PrecompiledResultCode; import org.aion.precompiled.PrecompiledTransactionResult; import org.aion.precompiled.type.StatefulPrecompiledContract; -import org.aion.vm.api.interfaces.Address; import org.apache.commons.collections4.map.LRUMap; /** @@ -44,27 +43,27 @@ */ public class AionAuctionContract extends StatefulPrecompiledContract { private static final Address AION = - AionAddress.wrap("0xa0eeaeabdbc92953b072afbd21f3e3fd8a4a4f5e6a6e22200db746ab75e9a99a"); + Address.wrap("0xa0eeaeabdbc92953b072afbd21f3e3fd8a4a4f5e6a6e22200db746ab75e9a99a"); private Address activeDomainsAddress = - AionAddress.wrap("0000000000000000000000000000000000000000000000000000000000000600"); + Address.wrap("0000000000000000000000000000000000000000000000000000000000000600"); private Address activeDomainsAddressTime = - AionAddress.wrap("0000000000000000000000000000000000000000000000000000000000000601"); + Address.wrap("0000000000000000000000000000000000000000000000000000000000000601"); private Address activeDomainsAddressName = - AionAddress.wrap("0000000000000000000000000000000000000000000000000000000000000602"); + Address.wrap("0000000000000000000000000000000000000000000000000000000000000602"); private Address activeDomainsAddressValue = - AionAddress.wrap("0000000000000000000000000000000000000000000000000000000000000603"); + Address.wrap("0000000000000000000000000000000000000000000000000000000000000603"); private Address auctionDomainsAddress = - AionAddress.wrap("0000000000000000000000000000000000000000000000000000000000000700"); + Address.wrap("0000000000000000000000000000000000000000000000000000000000000700"); private Address auctionDomainsAddressName = - AionAddress.wrap("0000000000000000000000000000000000000000000000000000000000000702"); + Address.wrap("0000000000000000000000000000000000000000000000000000000000000702"); private Address allAddresses = - AionAddress.wrap("0000000000000000000000000000000000000000000000000000000000000800"); + Address.wrap("0000000000000000000000000000000000000000000000000000000000000800"); private Address domainNameAddressPair = - AionAddress.wrap("0000000000000000000000000000000000000000000000000000000000000801"); + Address.wrap("0000000000000000000000000000000000000000000000000000000000000801"); private Address domainAddressNamePair = - AionAddress.wrap("0000000000000000000000000000000000000000000000000000000000000802"); + Address.wrap("0000000000000000000000000000000000000000000000000000000000000802"); private static final long COST = 20000L; private static final int SIG_LEN = 96; @@ -100,7 +99,7 @@ public class AionAuctionContract extends StatefulPrecompiledContract { * @param address The callerAddress of the calling account, use AION address for testing */ public AionAuctionContract( - IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> track, + RepositoryCache<AccountState, IBlockStoreBase<?, ?>> track, Address address, IBlockchain blockchain) { super(track); @@ -204,7 +203,7 @@ public PrecompiledTransactionResult execute(byte[] input, long nrg) { domainName = domainName.substring(0, 32); Ed25519Signature sig = Ed25519Signature.fromBytes(sign); - Address bidderAddress = AionAddress.wrap(bidderAddressInByte); + Address bidderAddress = Address.wrap(bidderAddressInByte); // user should have the signature signed with its callerAddress byte[] data = new byte[ADDR_LEN]; @@ -217,7 +216,7 @@ public PrecompiledTransactionResult execute(byte[] input, long nrg) { PrecompiledResultCode.FAILURE, nrg - COST, "incorrect signature".getBytes()); } - if (!bidderAddress.equals(AionAddress.wrap(sig.getAddress()))) { + if (!bidderAddress.equals(Address.wrap(sig.getAddress()))) { return new PrecompiledTransactionResult( PrecompiledResultCode.FAILURE, nrg - COST, "incorrect key".getBytes()); } @@ -226,7 +225,7 @@ public PrecompiledTransactionResult execute(byte[] input, long nrg) { if (this.track .getStorageValue( domainNameAddressPair, - new DataWord(blake128(domainName.getBytes())).toWrapper()) + new DataWordImpl(blake128(domainName.getBytes())).toWrapper()) .equals(DoubleDataWord.ZERO)) { domainAddress = createAddressForDomain(domainName); } else { // extract the callerAddress corresponding to the domain name @@ -489,7 +488,7 @@ private void processAuction(Address domainAddress) { addBigIntegerToStorage(domainAddress, BID_KEY_COUNTER, new BigInteger("0")); // remove from auction domains this.track.removeStorageRow( - auctionDomainsAddress, new DataWord(blake128(domainAddress.toBytes())).toWrapper()); + auctionDomainsAddress, new DataWordImpl(blake128(domainAddress.toBytes())).toWrapper()); addToActiveDomains(domainAddress, winnerAddress, domainName, secondHighestBid); printWinner(domainAddress, winnerAddress, secondHighestBid, domainName); } @@ -514,7 +513,7 @@ private byte[] fillByteArray(byte[] inputBytes) { * @return the trimmed byte array */ private boolean isActiveDomain(Address domainAddress) { - DataWord key = new DataWord(blake128(domainAddress.toBytes())); + DataWordImpl key = new DataWordImpl(blake128(domainAddress.toBytes())); return !(this.track .getStorageValue(activeDomainsAddress, key.toWrapper()) .equals(DoubleDataWord.ZERO.toWrapper())); @@ -526,7 +525,7 @@ private boolean isActiveDomain(Address domainAddress) { * @param domainAddress a domain callerAddress */ private boolean isAuctionDomain(Address domainAddress) { - DataWord key = new DataWord(blake128(domainAddress.toBytes())); + DataWordImpl key = new DataWordImpl(blake128(domainAddress.toBytes())); ByteArrayWrapper ret = this.track.getStorageValue(auctionDomainsAddress, key.toWrapper()); return !ret.equals(DoubleDataWord.ZERO.toWrapper()); } @@ -605,22 +604,22 @@ private void removeActiveDomain(Address domainAddress, long expireTime) { printRemoveActiveDomain(domainAddress); // erase this.track.removeStorageRow( - activeDomainsAddress, new DataWord(blake128(domainAddress.toBytes())).toWrapper()); + activeDomainsAddress, new DataWordImpl(blake128(domainAddress.toBytes())).toWrapper()); this.track.removeStorageRow( activeDomainsAddress, - new DataWord(blake128(blake128(domainAddress.toBytes()))).toWrapper()); + new DataWordImpl(blake128(blake128(domainAddress.toBytes()))).toWrapper()); this.track.removeStorageRow( activeDomainsAddressName, - new DataWord(blake128(domainAddress.toBytes())).toWrapper()); + new DataWordImpl(blake128(domainAddress.toBytes())).toWrapper()); this.track.removeStorageRow( activeDomainsAddressName, - new DataWord(blake128(blake128(domainAddress.toBytes()))).toWrapper()); + new DataWordImpl(blake128(blake128(domainAddress.toBytes()))).toWrapper()); this.track.removeStorageRow( activeDomainsAddressValue, - new DataWord(blake128(domainAddress.toBytes())).toWrapper()); + new DataWordImpl(blake128(domainAddress.toBytes())).toWrapper()); this.track.removeStorageRow( activeDomainsAddressTime, - new DataWord(blake128(domainAddress.toBytes())).toWrapper()); + new DataWordImpl(blake128(domainAddress.toBytes())).toWrapper()); } /** @@ -660,7 +659,7 @@ private boolean hasActiveParentDomain(String domainName) { } // check if domain exists, and if it is active - AionAddress parentAddr = null; + Address parentAddr = null; try { parentAddr = getAddressFromName( @@ -687,7 +686,7 @@ private boolean hasActiveParentDomain(String domainName) { */ private Address createAddressForDomain(String domainName) { ECKey domainAddr = ECKeyFac.inst().create(); - Address domainAddress = AionAddress.wrap(domainAddr.getAddress()); + Address domainAddress = Address.wrap(domainAddr.getAddress()); // store callerAddress -> name pair & name -> callerAddress pair addNameToStorage(domainAddressNamePair, domainAddress, domainName); @@ -700,20 +699,20 @@ private Address createAddressForDomain(String domainName) { * @param domainName name of domain * @return callerAddress of domain */ - private AionAddress getAddressFromName(String domainName) { + private Address getAddressFromName(String domainName) { byte[] addrFirstPart = this.track .getStorageValue( domainNameAddressPair, - new DataWord(blake128(domainName.getBytes())).toWrapper()) + new DataWordImpl(blake128(domainName.getBytes())).toWrapper()) .getData(); byte[] addrSecondPart = this.track .getStorageValue( domainNameAddressPair, - new DataWord(blake128(blake128(domainName.getBytes()))).toWrapper()) + new DataWordImpl(blake128(blake128(domainName.getBytes()))).toWrapper()) .getData(); - return AionAddress.wrap(combineTwoBytes(addrFirstPart, addrSecondPart)); + return Address.wrap(combineTwoBytes(addrFirstPart, addrSecondPart)); } /** @@ -726,59 +725,59 @@ private void storeNewAddress(Address domainAddress) { BigInteger counter = getBigIntegerFromStorage(allAddresses, ALL_ADDR_COUNTER_KEY); this.track.addStorageRow( allAddresses, - new DataWord(blake128(ALL_ADDR_COUNTER_KEY.getBytes())).toWrapper(), - new DataWord(counter.add(BigInteger.ONE)).toWrapper()); + new DataWordImpl(blake128(ALL_ADDR_COUNTER_KEY.getBytes())).toWrapper(), + new DataWordImpl(counter.add(BigInteger.ONE)).toWrapper()); addAddressToStorage(allAddresses, ALL_ADDR_KEY + counter, domainAddress); } private Address getAddressFromStorage(Address key, Address key2) { byte[] addrFirstPart = this.track - .getStorageValue(key, new DataWord(blake128(key2.toBytes())).toWrapper()) + .getStorageValue(key, new DataWordImpl(blake128(key2.toBytes())).toWrapper()) .getData(); byte[] addrSecondPart = this.track .getStorageValue( - key, new DataWord(blake128(blake128(key2.toBytes()))).toWrapper()) + key, new DataWordImpl(blake128(blake128(key2.toBytes()))).toWrapper()) .getData(); - return AionAddress.wrap(combineTwoBytes(addrFirstPart, addrSecondPart)); + return Address.wrap(combineTwoBytes(addrFirstPart, addrSecondPart)); } private Address getAddressFromStorage(Address key, String key2) { byte[] addrFirstPart = this.track - .getStorageValue(key, new DataWord(blake128(key2.getBytes())).toWrapper()) + .getStorageValue(key, new DataWordImpl(blake128(key2.getBytes())).toWrapper()) .getData(); byte[] addrSecondPart = this.track .getStorageValue( - key, new DataWord(blake128(blake128(key2.getBytes()))).toWrapper()) + key, new DataWordImpl(blake128(blake128(key2.getBytes()))).toWrapper()) .getData(); - return AionAddress.wrap(combineTwoBytes(addrFirstPart, addrSecondPart)); + return Address.wrap(combineTwoBytes(addrFirstPart, addrSecondPart)); } private BigInteger getBigIntegerFromStorage(Address key, String key2) { ByteArrayWrapper data = this.track.getStorageValue( - key, new DataWord(blake128(key2.getBytes())).toWrapper()); + key, new DataWordImpl(blake128(key2.getBytes())).toWrapper()); return new BigInteger(data.getData()); } private BigInteger getBigIntegerFromStorage(Address key, Address key2) { ByteArrayWrapper data = - this.track.getStorageValue(key, new DataWord(blake128(key2.toBytes())).toWrapper()); + this.track.getStorageValue(key, new DataWordImpl(blake128(key2.toBytes())).toWrapper()); return new BigInteger(data.getData()); } private String getNameFromStorage(Address key, Address key2) { byte[] domainNameFirstPart = this.track - .getStorageValue(key, new DataWord(blake128(key2.toBytes())).toWrapper()) + .getStorageValue(key, new DataWordImpl(blake128(key2.toBytes())).toWrapper()) .getData(); byte[] domainNameSecondPart = this.track .getStorageValue( - key, new DataWord(blake128(blake128(key2.toBytes()))).toWrapper()) + key, new DataWordImpl(blake128(blake128(key2.toBytes()))).toWrapper()) .getData(); String tempDomainName; try { @@ -796,7 +795,7 @@ key, new DataWord(blake128(blake128(key2.toBytes()))).toWrapper()) private Date getDateFromStorage(Address key, Address key2) { byte[] expireDateData = this.track - .getStorageValue(key, new DataWord(blake128(key2.toBytes())).toWrapper()) + .getStorageValue(key, new DataWordImpl(blake128(key2.toBytes())).toWrapper()) .getData(); byte[] trimmedExpireDateData = trimLeadingZeros16(expireDateData); String expireDateStr; @@ -816,12 +815,12 @@ private void addAddressToStorage(Address key, Address key2, Address value) { this.track.addStorageRow( key, - new DataWord(blake128(key2.toBytes())).toWrapper(), - new DataWord(addrFirstPart).toWrapper()); + new DataWordImpl(blake128(key2.toBytes())).toWrapper(), + new DataWordImpl(addrFirstPart).toWrapper()); this.track.addStorageRow( key, - new DataWord(blake128(blake128(key2.toBytes()))).toWrapper(), - new DataWord(addrSecondPart).toWrapper()); + new DataWordImpl(blake128(blake128(key2.toBytes()))).toWrapper(), + new DataWordImpl(addrSecondPart).toWrapper()); } private void addAddressToStorage(Address key, String key2, Address value) { @@ -832,12 +831,12 @@ private void addAddressToStorage(Address key, String key2, Address value) { this.track.addStorageRow( key, - new DataWord(blake128(key2.getBytes())).toWrapper(), - new DataWord(addrFirstPart).toWrapper()); + new DataWordImpl(blake128(key2.getBytes())).toWrapper(), + new DataWordImpl(addrFirstPart).toWrapper()); this.track.addStorageRow( key, - new DataWord(blake128(blake128(key2.getBytes()))).toWrapper(), - new DataWord(addrSecondPart).toWrapper()); + new DataWordImpl(blake128(blake128(key2.getBytes()))).toWrapper(), + new DataWordImpl(addrSecondPart).toWrapper()); } private void addDateToStorage(Address key, Address key2, Date value) { @@ -854,8 +853,8 @@ private void addDateToStorage(Address key, Address key2, Date value) { } this.track.addStorageRow( key, - new DataWord(blake128(key2.toBytes())).toWrapper(), - new DataWord(fillByteArray(date)).toWrapper()); + new DataWordImpl(blake128(key2.toBytes())).toWrapper(), + new DataWordImpl(fillByteArray(date)).toWrapper()); } private void addNameToStorage(Address key, Address key2, String name) { @@ -863,12 +862,12 @@ private void addNameToStorage(Address key, Address key2, String name) { byte[] nameSecondPart = name.substring(16, 32).getBytes(); this.track.addStorageRow( key, - new DataWord(blake128(key2.toBytes())).toWrapper(), - new DataWord(nameFirstPart).toWrapper()); + new DataWordImpl(blake128(key2.toBytes())).toWrapper(), + new DataWordImpl(nameFirstPart).toWrapper()); this.track.addStorageRow( key, - new DataWord(blake128(blake128(key2.toBytes()))).toWrapper(), - new DataWord(nameSecondPart).toWrapper()); + new DataWordImpl(blake128(blake128(key2.toBytes()))).toWrapper(), + new DataWordImpl(nameSecondPart).toWrapper()); } private void addNameToStorage2(Address key, Address key2, String name) { @@ -878,26 +877,26 @@ private void addNameToStorage2(Address key, Address key2, String name) { System.arraycopy(addZeros, 16, value2, 0, 16); this.track.addStorageRow( key, - new DataWord(blake128(key2.toBytes())).toWrapper(), - new DataWord(value1).toWrapper()); + new DataWordImpl(blake128(key2.toBytes())).toWrapper(), + new DataWordImpl(value1).toWrapper()); this.track.addStorageRow( key, - new DataWord(blake128(key2.toBytes())).toWrapper(), - new DataWord(value2).toWrapper()); + new DataWordImpl(blake128(key2.toBytes())).toWrapper(), + new DataWordImpl(value2).toWrapper()); } private void addBigIntegerToStorage(Address key, String key2, BigInteger value) { this.track.addStorageRow( key, - new DataWord(blake128(key2.getBytes())).toWrapper(), - new DataWord(value).toWrapper()); + new DataWordImpl(blake128(key2.getBytes())).toWrapper(), + new DataWordImpl(value).toWrapper()); } private void addBigIntegerToStorage(Address key, Address key2, BigInteger value) { this.track.addStorageRow( key, - new DataWord(blake128(key2.toBytes())).toWrapper(), - new DataWord(value).toWrapper()); + new DataWordImpl(blake128(key2.toBytes())).toWrapper(), + new DataWordImpl(value).toWrapper()); } // tasks @@ -937,7 +936,7 @@ public void run() { // data processing helpers // ---------------------------------------------------------------------------------------// /** - * Combines two length 16 byte[] (usually retrieved from repo as a DataWord(length 16) into a + * Combines two length 16 byte[] (usually retrieved from repo as a DataWordImpl(length 16) into a * length 32 byte[]. * * @param byte1 input1 @@ -999,7 +998,7 @@ private List<AuctionDomainsData> getAllAuctionDomains() { if (!this.track .getStorageValue( auctionDomainsAddress, - new DataWord(blake128(tempDomainAddr.toBytes())).toWrapper()) + new DataWordImpl(blake128(tempDomainAddr.toBytes())).toWrapper()) .equals(DoubleDataWord.ZERO)) { Date tempExpireDate = getDateFromStorage(auctionDomainsAddress, tempDomainAddr); String tempDomainName = getNameFromStorage(domainAddressNamePair, tempDomainAddr); @@ -1053,7 +1052,7 @@ public void displayAllAuctionDomains() { } public void displayMyBidsLRU(ECKey key) { - Address callerAddress = AionAddress.wrap(key.getAddress()); + Address callerAddress = Address.wrap(key.getAddress()); boolean hasNoBids = true; System.out.println( @@ -1083,7 +1082,7 @@ public void displayMyBidsLRU(ECKey key) { } public void displayMyBidForDomainLRU(String domainNameRaw, ECKey key) { - Address callerAddress = AionAddress.wrap(key.getAddress()); + Address callerAddress = Address.wrap(key.getAddress()); System.out.println( "--------------------------AION NAME SERVICE QUERY: displayMyBidForDomainLRU--------------------------"); @@ -1103,7 +1102,7 @@ public void displayMyBidForDomainLRU(String domainNameRaw, ECKey key) { if (this.track .getStorageValue( auctionDomainsAddress, - new DataWord(blake128(domainAddress.toBytes())).toWrapper()) + new DataWordImpl(blake128(domainAddress.toBytes())).toWrapper()) .equals(DoubleDataWord.ZERO)) { System.out.println(" This domain is not in auction\n"); return; @@ -1137,7 +1136,7 @@ public void displayAuctionDomainLRU(String domainNameRaw) { if (this.track .getStorageValue( auctionDomainsAddress, - new DataWord(blake128(domainAddress.toBytes())).toWrapper()) + new DataWordImpl(blake128(domainAddress.toBytes())).toWrapper()) .equals(DoubleDataWord.ZERO)) { System.out.println("The given domain \'" + domainNameRaw + "\' is not in auction\n"); return; diff --git a/modPrecompiled/src/org/aion/precompiled/contracts/AionNameServiceContract.java b/modPrecompiled/src/org/aion/precompiled/contracts/AionNameServiceContract.java index 6ab5be7d38..a970df94e9 100644 --- a/modPrecompiled/src/org/aion/precompiled/contracts/AionNameServiceContract.java +++ b/modPrecompiled/src/org/aion/precompiled/contracts/AionNameServiceContract.java @@ -9,20 +9,19 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.crypto.ECKey; import org.aion.crypto.ed25519.ECKeyEd25519; import org.aion.crypto.ed25519.Ed25519Signature; import org.aion.mcf.core.AccountState; import org.aion.mcf.db.IBlockStoreBase; -import org.aion.mcf.vm.types.DataWord; import org.aion.mcf.vm.types.DoubleDataWord; import org.aion.precompiled.PrecompiledResultCode; import org.aion.precompiled.PrecompiledTransactionResult; import org.aion.precompiled.type.StatefulPrecompiledContract; -import org.aion.vm.api.interfaces.Address; import org.apache.commons.collections4.map.LRUMap; /** @@ -48,19 +47,19 @@ public class AionNameServiceContract extends StatefulPrecompiledContract { private static final String ALL_ADDR_COUNTER_KEY = "allAddressKey"; private Address activeDomainsAddress = - AionAddress.wrap("0000000000000000000000000000000000000000000000000000000000000600"); + Address.wrap("0000000000000000000000000000000000000000000000000000000000000600"); private Address activeDomainsAddressTime = - AionAddress.wrap("0000000000000000000000000000000000000000000000000000000000000601"); + Address.wrap("0000000000000000000000000000000000000000000000000000000000000601"); private Address activeDomainsAddressValue = - AionAddress.wrap("0000000000000000000000000000000000000000000000000000000000000603"); + Address.wrap("0000000000000000000000000000000000000000000000000000000000000603"); private Address allAddresses = - AionAddress.wrap("0000000000000000000000000000000000000000000000000000000000000800"); + Address.wrap("0000000000000000000000000000000000000000000000000000000000000800"); private Address domainAddressNamePair = - AionAddress.wrap("0000000000000000000000000000000000000000000000000000000000000802"); + Address.wrap("0000000000000000000000000000000000000000000000000000000000000802"); private Address registeredDomainAddressName = - AionAddress.wrap("0000000000000000000000000000000000000000000000000000000000000803"); + Address.wrap("0000000000000000000000000000000000000000000000000000000000000803"); private Address registeredDomainNameAddress = - AionAddress.wrap("0000000000000000000000000000000000000000000000000000000000000804"); + Address.wrap("0000000000000000000000000000000000000000000000000000000000000804"); private Address address; private Address ownerAddress; @@ -74,7 +73,7 @@ public class AionNameServiceContract extends StatefulPrecompiledContract { /** Construct a new ANS Contract */ public AionNameServiceContract( - IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> track, + RepositoryCache<AccountState, IBlockStoreBase<?, ?>> track, Address address, Address ownerAddress) { // byte super(track); @@ -91,7 +90,7 @@ public AionNameServiceContract( if (!(getOwnerAddress().equals(ownerAddress)) && !(getOwnerAddress() .equals( - AionAddress.wrap( + Address.wrap( "0000000000000000000000000000000000000000000000000000000000000000")))) { throw new IllegalArgumentException( "The owner address of this domain from repository is different than the given" @@ -184,7 +183,7 @@ public PrecompiledTransactionResult execute(byte[] input, long nrg) { } // verify public key matches owner - if (!this.ownerAddress.equals(AionAddress.wrap(sig.getAddress()))) { + if (!this.ownerAddress.equals(Address.wrap(sig.getAddress()))) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); } @@ -231,7 +230,7 @@ private PrecompiledTransactionResult setResolver( // set the key byte[] combined = combineTwoBytes(hash1, hash2); - this.resolverAddressKey = new AionAddress(combined); + this.resolverAddressKey = new Address(combined); return new PrecompiledTransactionResult(PrecompiledResultCode.SUCCESS, nrg - SET_COST); } @@ -246,7 +245,7 @@ private PrecompiledTransactionResult setTTL( // set the key byte[] combined = combineTwoBytes(hash1, hash2); - this.TTLKey = new AionAddress(combined); + this.TTLKey = new Address(combined); return new PrecompiledTransactionResult(PrecompiledResultCode.SUCCESS, nrg - SET_COST); } @@ -257,15 +256,15 @@ private PrecompiledTransactionResult transferOwnership( if (nrg < TRANSFER_COST) return new PrecompiledTransactionResult(PrecompiledResultCode.OUT_OF_NRG, 0); - if (!isValidOwnerAddress(AionAddress.wrap(combineTwoBytes(addr1, addr2)))) + if (!isValidOwnerAddress(Address.wrap(combineTwoBytes(addr1, addr2)))) return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, nrg); - AionAddress.wrap(combineTwoBytes(addr1, addr2)); + Address.wrap(combineTwoBytes(addr1, addr2)); storeResult(hash1, hash2, addr1, addr2); // set the key byte[] combined = combineTwoBytes(hash1, hash2); - this.ownerAddressKey = new AionAddress(combined); + this.ownerAddressKey = new Address(combined); return new PrecompiledTransactionResult(PrecompiledResultCode.SUCCESS, nrg - TRANSFER_COST); } @@ -282,16 +281,16 @@ private PrecompiledTransactionResult transferSubdomainOwnership( if (nrg < TRANSFER_COST) return new PrecompiledTransactionResult(PrecompiledResultCode.OUT_OF_NRG, 0); - if (!isValidOwnerAddress(AionAddress.wrap(combineTwoBytes(addr1, addr2)))) + if (!isValidOwnerAddress(Address.wrap(combineTwoBytes(addr1, addr2)))) return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, nrg); - Address sdAddress = AionAddress.wrap(subdomainAddress); + Address sdAddress = Address.wrap(subdomainAddress); if (isSubdomain(subdomain)) { this.track.addStorageRow( - sdAddress, new DataWord(hash1).toWrapper(), new DataWord(addr1).toWrapper()); + sdAddress, new DataWordImpl(hash1).toWrapper(), new DataWordImpl(addr1).toWrapper()); this.track.addStorageRow( - sdAddress, new DataWord(hash2).toWrapper(), new DataWord(addr2).toWrapper()); + sdAddress, new DataWordImpl(hash2).toWrapper(), new DataWordImpl(addr2).toWrapper()); return new PrecompiledTransactionResult( PrecompiledResultCode.SUCCESS, nrg - TRANSFER_COST); } @@ -319,9 +318,9 @@ private void setUpKeys() { byte[] combined2 = combineTwoBytes(TTLHash1, TTLHash2); byte[] combined3 = combineTwoBytes(ownerHash1, ownerHash2); - this.resolverAddressKey = new AionAddress(combined); - this.TTLKey = new AionAddress(combined2); - this.ownerAddressKey = new AionAddress(combined3); + this.resolverAddressKey = new Address(combined); + this.TTLKey = new Address(combined2); + this.ownerAddressKey = new Address(combined3); } private byte[] combineTwoBytes(byte[] byte1, byte[] byte2) { @@ -333,9 +332,9 @@ private byte[] combineTwoBytes(byte[] byte1, byte[] byte2) { private void storeResult(byte[] hash1, byte[] hash2, byte[] addr1, byte[] addr2) { this.track.addStorageRow( - this.address, new DataWord(hash1).toWrapper(), new DataWord(addr1).toWrapper()); + this.address, new DataWordImpl(hash1).toWrapper(), new DataWordImpl(addr1).toWrapper()); this.track.addStorageRow( - this.address, new DataWord(hash2).toWrapper(), new DataWord(addr2).toWrapper()); + this.address, new DataWordImpl(hash2).toWrapper(), new DataWordImpl(addr2).toWrapper()); } private Address getValueFromStorage(Address key) { @@ -347,15 +346,15 @@ private Address getValueFromStorage(Address key) { System.arraycopy(byteKey, 16, key2, 0, 16); ByteArrayWrapper data1 = - this.track.getStorageValue(this.address, new DataWord(key1).toWrapper()); + this.track.getStorageValue(this.address, new DataWordImpl(key1).toWrapper()); ByteArrayWrapper data2 = - this.track.getStorageValue(this.address, new DataWord(key2).toWrapper()); + this.track.getStorageValue(this.address, new DataWordImpl(key2).toWrapper()); byte[] addr1 = data1.getData(); byte[] addr2 = data2.getData(); byte[] addrCombined = combineTwoBytes(addr1, addr2); - return (new AionAddress(addrCombined)); + return (new Address(addrCombined)); } private void addToRegistered(Address domainAddress, String domainName) { @@ -377,22 +376,22 @@ private void addToRegistered(Address domainAddress, String domainName) { // store name -> address pair this.track.addStorageRow( registeredDomainNameAddress, - new DataWord(blake128(domainName.getBytes())).toWrapper(), - new DataWord(addressFirstPart).toWrapper()); + new DataWordImpl(blake128(domainName.getBytes())).toWrapper(), + new DataWordImpl(addressFirstPart).toWrapper()); this.track.addStorageRow( registeredDomainNameAddress, - new DataWord(blake128(blake128(domainName.getBytes()))).toWrapper(), - new DataWord(addressSecondPart).toWrapper()); + new DataWordImpl(blake128(blake128(domainName.getBytes()))).toWrapper(), + new DataWordImpl(addressSecondPart).toWrapper()); // store address -> name pair this.track.addStorageRow( registeredDomainAddressName, - new DataWord(blake128(domainAddress.toBytes())).toWrapper(), - new DataWord(nameFirstPart).toWrapper()); + new DataWordImpl(blake128(domainAddress.toBytes())).toWrapper(), + new DataWordImpl(nameFirstPart).toWrapper()); this.track.addStorageRow( registeredDomainAddressName, - new DataWord(blake128(blake128(domainAddress.toBytes()))).toWrapper(), - new DataWord(nameSecondPart).toWrapper()); + new DataWordImpl(blake128(blake128(domainAddress.toBytes()))).toWrapper(), + new DataWordImpl(nameSecondPart).toWrapper()); } private boolean isSubdomain(String subdomainName) { @@ -444,13 +443,13 @@ private boolean isAvailableDomain(Address domainAddress, Address ownerAddress) { ByteArrayWrapper addrFirstPart = this.track.getStorageValue( activeDomainsAddress, - new DataWord(blake128(domainAddress.toBytes())).toWrapper()); + new DataWordImpl(blake128(domainAddress.toBytes())).toWrapper()); ByteArrayWrapper addrSecondPart = this.track.getStorageValue( activeDomainsAddress, - new DataWord(blake128(blake128(domainAddress.toBytes()))).toWrapper()); + new DataWordImpl(blake128(blake128(domainAddress.toBytes()))).toWrapper()); Address addrFromRepo = - AionAddress.wrap( + Address.wrap( combineTwoBytes(addrFirstPart.getData(), addrSecondPart.getData())); return addrFromRepo.equals(ownerAddress); @@ -517,11 +516,11 @@ private String getDomainNameFromAddress(Address domainAddress) { ByteArrayWrapper nameFirstPartData = this.track.getStorageValue( domainAddressNamePair, - new DataWord(blake128(domainAddress.toBytes())).toWrapper()); + new DataWordImpl(blake128(domainAddress.toBytes())).toWrapper()); ByteArrayWrapper nameSecondPartData = this.track.getStorageValue( domainAddressNamePair, - new DataWord(blake128(blake128(domainAddress.toBytes()))).toWrapper()); + new DataWordImpl(blake128(blake128(domainAddress.toBytes()))).toWrapper()); byte[] nameData = trimLeadingZeros( combineTwoBytes(nameFirstPartData.getData(), nameSecondPartData.getData())); @@ -540,7 +539,7 @@ private List<ActiveDomainsData> getAllActiveDomains() { ByteArrayWrapper numberOfDomainsTotalData = this.track.getStorageValue( allAddresses, - new DataWord(blake128(ALL_ADDR_COUNTER_KEY.getBytes())).toWrapper()); + new DataWordImpl(blake128(ALL_ADDR_COUNTER_KEY.getBytes())).toWrapper()); BigInteger numberOfDomainsTotal = new BigInteger(numberOfDomainsTotalData.getData()); int counter = numberOfDomainsTotal.intValue(); @@ -551,43 +550,43 @@ private List<ActiveDomainsData> getAllActiveDomains() { byte[] secondHash = blake128(blake128((ALL_ADDR_KEY + i).getBytes())); byte[] addrFirstPart = this.track - .getStorageValue(allAddresses, new DataWord(firstHash).toWrapper()) + .getStorageValue(allAddresses, new DataWordImpl(firstHash).toWrapper()) .getData(); byte[] addrSecondPart = this.track - .getStorageValue(allAddresses, new DataWord(secondHash).toWrapper()) + .getStorageValue(allAddresses, new DataWordImpl(secondHash).toWrapper()) .getData(); Address tempDomainAddr = - AionAddress.wrap(combineTwoBytes(addrFirstPart, addrSecondPart)); + Address.wrap(combineTwoBytes(addrFirstPart, addrSecondPart)); // if domain exists if (!this.track .getStorageValue( activeDomainsAddress, - new DataWord(blake128(tempDomainAddr.toBytes())).toWrapper()) + new DataWordImpl(blake128(tempDomainAddr.toBytes())).toWrapper()) .equals(DoubleDataWord.ZERO.toWrapper())) { byte[] ownerAddrFirstPart = this.track .getStorageValue( activeDomainsAddress, - new DataWord(blake128(tempDomainAddr.toBytes())) + new DataWordImpl(blake128(tempDomainAddr.toBytes())) .toWrapper()) .getData(); byte[] ownerAddrSecondPart = this.track .getStorageValue( activeDomainsAddress, - new DataWord(blake128(blake128(tempDomainAddr.toBytes()))) + new DataWordImpl(blake128(blake128(tempDomainAddr.toBytes()))) .toWrapper()) .getData(); Address tempOwnerAddr = - AionAddress.wrap(combineTwoBytes(ownerAddrFirstPart, ownerAddrSecondPart)); + Address.wrap(combineTwoBytes(ownerAddrFirstPart, ownerAddrSecondPart)); byte[] expireDateData = this.track .getStorageValue( activeDomainsAddressTime, - new DataWord(blake128(tempDomainAddr.toBytes())) + new DataWordImpl(blake128(tempDomainAddr.toBytes())) .toWrapper()) .getData(); byte[] trimmedExpireDateData = trimLeadingZeros16(expireDateData); @@ -603,14 +602,14 @@ private List<ActiveDomainsData> getAllActiveDomains() { this.track .getStorageValue( domainAddressNamePair, - new DataWord(blake128(tempDomainAddr.toBytes())) + new DataWordImpl(blake128(tempDomainAddr.toBytes())) .toWrapper()) .getData(); byte[] domainNameSecondPart = this.track .getStorageValue( domainAddressNamePair, - new DataWord(blake128(blake128(tempDomainAddr.toBytes()))) + new DataWordImpl(blake128(blake128(tempDomainAddr.toBytes()))) .toWrapper()) .getData(); String tempDomainName = null; @@ -630,7 +629,7 @@ private List<ActiveDomainsData> getAllActiveDomains() { this.track .getStorageValue( activeDomainsAddressValue, - new DataWord(blake128(tempDomainAddr.toBytes())) + new DataWordImpl(blake128(tempDomainAddr.toBytes())) .toWrapper()) .getData(); BigInteger tempValue = new BigInteger(valueData); @@ -669,7 +668,7 @@ public void displayAllActiveDomains() { } public void displayMyDomains(ECKey key) { - Address callerAddress = AionAddress.wrap(key.getAddress()); + Address callerAddress = Address.wrap(key.getAddress()); System.out.println( "----------------------------AION NAME SERVICE QUERY: displayMyDomains-----------------------------"); @@ -725,13 +724,13 @@ public String getRegisteredDomainName(Address domainAddress) { this.track .getStorageValue( domainAddressNamePair, - new DataWord(blake128(domainAddress.toBytes())).toWrapper()) + new DataWordImpl(blake128(domainAddress.toBytes())).toWrapper()) .getData(); byte[] domainNameSecondPart = this.track .getStorageValue( domainAddressNamePair, - new DataWord(blake128(blake128(domainAddress.toBytes()))) + new DataWordImpl(blake128(blake128(domainAddress.toBytes()))) .toWrapper()) .getData(); String domainName; @@ -756,16 +755,16 @@ public Address getRegisteredDomainAddress(String domainName) { this.track .getStorageValue( registeredDomainNameAddress, - new DataWord(blake128(domainName.getBytes())).toWrapper()) + new DataWordImpl(blake128(domainName.getBytes())).toWrapper()) .getData(); byte[] addressSecondPart = this.track .getStorageValue( registeredDomainNameAddress, - new DataWord(blake128(blake128(domainName.getBytes()))).toWrapper()) + new DataWordImpl(blake128(blake128(domainName.getBytes()))).toWrapper()) .getData(); - AionAddress domainAddress = - AionAddress.wrap(combineTwoBytes(addressFirstPart, addressSecondPart)); + Address domainAddress = + Address.wrap(combineTwoBytes(addressFirstPart, addressSecondPart)); if (domainAddress.isZeroAddress()) return null; return domainAddress; } diff --git a/modPrecompiled/src/org/aion/precompiled/contracts/EDVerifyContract.java b/modPrecompiled/src/org/aion/precompiled/contracts/EDVerifyContract.java index c83718116b..1799d7cc84 100644 --- a/modPrecompiled/src/org/aion/precompiled/contracts/EDVerifyContract.java +++ b/modPrecompiled/src/org/aion/precompiled/contracts/EDVerifyContract.java @@ -1,6 +1,6 @@ package org.aion.precompiled.contracts; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.crypto.ed25519.ECKeyEd25519; import org.aion.precompiled.PrecompiledResultCode; import org.aion.precompiled.PrecompiledTransactionResult; @@ -43,7 +43,7 @@ public PrecompiledTransactionResult execute(byte[] input, long nrgLimit) { return new PrecompiledTransactionResult( PrecompiledResultCode.SUCCESS, nrgLimit - COST, - verify ? pubKey : AionAddress.ZERO_ADDRESS().toBytes()); + verify ? pubKey : Address.ZERO_ADDRESS().toBytes()); } catch (Exception e) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); } diff --git a/modPrecompiled/src/org/aion/precompiled/contracts/MultiSignatureContract.java b/modPrecompiled/src/org/aion/precompiled/contracts/MultiSignatureContract.java index 28249317f7..7c80001d0c 100644 --- a/modPrecompiled/src/org/aion/precompiled/contracts/MultiSignatureContract.java +++ b/modPrecompiled/src/org/aion/precompiled/contracts/MultiSignatureContract.java @@ -7,9 +7,10 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.crypto.AddressSpecs; import org.aion.crypto.HashUtil; import org.aion.crypto.ISignature; @@ -17,11 +18,9 @@ import org.aion.crypto.ed25519.Ed25519Signature; import org.aion.mcf.core.AccountState; import org.aion.mcf.db.IBlockStoreBase; -import org.aion.mcf.vm.types.DataWord; import org.aion.precompiled.PrecompiledResultCode; import org.aion.precompiled.PrecompiledTransactionResult; import org.aion.precompiled.type.StatefulPrecompiledContract; -import org.aion.vm.api.interfaces.Address; /** * An N of M implementation of a multi-signature pre-compiled contract. @@ -59,7 +58,7 @@ public final class MultiSignatureContract extends StatefulPrecompiledContract { * @throws IllegalArgumentException if track or caller are null. */ public MultiSignatureContract( - IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> track, Address caller) { + RepositoryCache<AccountState, IBlockStoreBase<?, ?>> track, Address caller) { super(track); if (caller == null) { @@ -308,10 +307,10 @@ private PrecompiledTransactionResult sendTransaction(byte[] input, long nrg) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); } - Address wallet = new AionAddress(Arrays.copyOfRange(input, walletStart, sigsStart)); + Address wallet = new Address(Arrays.copyOfRange(input, walletStart, sigsStart)); List<byte[]> sigs = extractSignatures(Arrays.copyOfRange(input, sigsStart, amountStart)); BigInteger amount = new BigInteger(Arrays.copyOfRange(input, amountStart, nrgStart)); - Address recipient = new AionAddress(Arrays.copyOfRange(input, recipientStart, length)); + Address recipient = new Address(Arrays.copyOfRange(input, recipientStart, length)); ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); buffer.put(Arrays.copyOfRange(input, nrgStart, recipientStart)); @@ -321,7 +320,7 @@ private PrecompiledTransactionResult sendTransaction(byte[] input, long nrg) { if (!isValidTxNrg(nrg)) { return new PrecompiledTransactionResult(PrecompiledResultCode.INVALID_NRG_LIMIT, nrg); } - if (track.getStorageValue(wallet, new DataWord(getMetaDataKey()).toWrapper()) == null) { + if (track.getStorageValue(wallet, new DataWordImpl(getMetaDataKey()).toWrapper()) == null) { // Then wallet is not the address of a multi-sig wallet. return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); } @@ -374,11 +373,11 @@ private Set<Address> extractAddresses(byte[] addresses) { Address addr; boolean addressIsOwner = false; for (int i = 0; i < length; i += ADDR_LEN) { - addr = new AionAddress(Arrays.copyOfRange(addresses, i, i + ADDR_LEN)); + addr = new Address(Arrays.copyOfRange(addresses, i, i + ADDR_LEN)); if (result.contains(addr)) { return null; } - if (track.getStorageValue(addr, new DataWord(getMetaDataKey()).toWrapper()) != null) { + if (track.getStorageValue(addr, new DataWordImpl(getMetaDataKey()).toWrapper()) != null) { return null; } if (addr.equals(this.caller)) { @@ -454,7 +453,7 @@ private Address initNewWallet(Set<Address> owners, long threshold) { byte[] hash = HashUtil.keccak256(content); hash[0] = AION_PREFIX; - Address walletId = new AionAddress(hash); + Address walletId = new Address(hash); track.createAccount(walletId); saveWalletMetaData(walletId, threshold, owners.size()); saveWalletOwners(walletId, owners); @@ -478,7 +477,7 @@ private Address initNewWallet(Set<Address> owners, long threshold) { */ private void saveWalletMetaData(Address walletId, long threshold, long numOwners) { byte[] metaKey = getMetaDataKey(); - byte[] metaValue = new byte[DataWord.BYTES]; + byte[] metaValue = new byte[DataWordImpl.BYTES]; ByteBuffer data = ByteBuffer.allocate(Long.BYTES); data.putLong(threshold); @@ -489,7 +488,7 @@ private void saveWalletMetaData(Address walletId, long threshold, long numOwners System.arraycopy(data.array(), 0, metaValue, Long.BYTES, Long.BYTES); track.addStorageRow( - walletId, new DataWord(metaKey).toWrapper(), new DataWord(metaValue).toWrapper()); + walletId, new DataWordImpl(metaKey).toWrapper(), new DataWordImpl(metaValue).toWrapper()); } /** @@ -515,19 +514,19 @@ private void saveWalletOwners(Address walletId, Set<Address> owners) { secondKey = getOwnerDataKey(false, count); // set the two values for this owner. - firstValue = new byte[DataWord.BYTES]; - secondValue = new byte[DataWord.BYTES]; - System.arraycopy(owner.toBytes(), 0, firstValue, 0, DataWord.BYTES); - System.arraycopy(owner.toBytes(), DataWord.BYTES, secondValue, 0, DataWord.BYTES); + firstValue = new byte[DataWordImpl.BYTES]; + secondValue = new byte[DataWordImpl.BYTES]; + System.arraycopy(owner.toBytes(), 0, firstValue, 0, DataWordImpl.BYTES); + System.arraycopy(owner.toBytes(), DataWordImpl.BYTES, secondValue, 0, DataWordImpl.BYTES); track.addStorageRow( walletId, - new DataWord(firstKey).toWrapper(), - new DataWord(firstValue).toWrapper()); + new DataWordImpl(firstKey).toWrapper(), + new DataWordImpl(firstValue).toWrapper()); track.addStorageRow( walletId, - new DataWord(secondKey).toWrapper(), - new DataWord(secondValue).toWrapper()); + new DataWordImpl(secondKey).toWrapper(), + new DataWordImpl(secondValue).toWrapper()); count++; } } @@ -558,7 +557,7 @@ private boolean areValidSignatures(Address wallet, List<byte[]> signatures, byte if (!signatureIsCorrect(sig, msg)) { return false; } - signer = new AionAddress(AddressSpecs.computeA0Address(Arrays.copyOfRange(sig, 0, 32))); + signer = new Address(AddressSpecs.computeA0Address(Arrays.copyOfRange(sig, 0, 32))); if (txSigners.contains(signer)) { return false; } @@ -569,7 +568,7 @@ private boolean areValidSignatures(Address wallet, List<byte[]> signatures, byte } ByteArrayWrapper metaValue = - track.getStorageValue(wallet, new DataWord(getMetaDataKey()).toWrapper()); + track.getStorageValue(wallet, new DataWordImpl(getMetaDataKey()).toWrapper()); ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); buffer.put(Arrays.copyOfRange(metaValue.getData(), 0, Long.BYTES)); buffer.flip(); @@ -603,9 +602,9 @@ private Set<Address> getOwners(Address walletId) { Set<Address> owners = new HashSet<>(); ByteArrayWrapper metaValue = - track.getStorageValue(walletId, new DataWord(getMetaDataKey()).toWrapper()); + track.getStorageValue(walletId, new DataWordImpl(getMetaDataKey()).toWrapper()); ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); - buffer.put(Arrays.copyOfRange(metaValue.getData(), Long.BYTES, DataWord.BYTES)); + buffer.put(Arrays.copyOfRange(metaValue.getData(), Long.BYTES, DataWordImpl.BYTES)); buffer.flip(); long numOwners = buffer.getLong(); @@ -631,14 +630,14 @@ private Address getOwner(Address walletId, long ownerId) { byte[] ownerDataKey1 = getOwnerDataKey(true, ownerId); ByteArrayWrapper addrPortion = - track.getStorageValue(walletId, new DataWord(ownerDataKey1).toWrapper()); - System.arraycopy(addrPortion.getData(), 0, address, 0, DataWord.BYTES); + track.getStorageValue(walletId, new DataWordImpl(ownerDataKey1).toWrapper()); + System.arraycopy(addrPortion.getData(), 0, address, 0, DataWordImpl.BYTES); byte[] ownerDataKey2 = getOwnerDataKey(false, ownerId); - addrPortion = track.getStorageValue(walletId, new DataWord(ownerDataKey2).toWrapper()); - System.arraycopy(addrPortion.getData(), 0, address, DataWord.BYTES, DataWord.BYTES); + addrPortion = track.getStorageValue(walletId, new DataWordImpl(ownerDataKey2).toWrapper()); + System.arraycopy(addrPortion.getData(), 0, address, DataWordImpl.BYTES, DataWordImpl.BYTES); - return new AionAddress(address); + return new Address(address); } /** @@ -647,7 +646,7 @@ private Address getOwner(Address walletId, long ownerId) { * @return the meta data query key. */ private static byte[] getMetaDataKey() { - byte[] metaKey = new byte[DataWord.BYTES]; + byte[] metaKey = new byte[DataWordImpl.BYTES]; metaKey[0] = (byte) 0x80; return metaKey; } @@ -660,13 +659,13 @@ private static byte[] getMetaDataKey() { * @return the owner data query key. */ private static byte[] getOwnerDataKey(boolean isFirstHalf, long ownerId) { - byte[] ownerKey = new byte[DataWord.BYTES]; + byte[] ownerKey = new byte[DataWordImpl.BYTES]; if (!isFirstHalf) { ownerKey[0] = (byte) 0x40; } ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); buffer.putLong(ownerId); - System.arraycopy(buffer.array(), 0, ownerKey, DataWord.BYTES - Long.BYTES, Long.BYTES); + System.arraycopy(buffer.array(), 0, ownerKey, DataWordImpl.BYTES - Long.BYTES, Long.BYTES); return ownerKey; } diff --git a/modPrecompiled/src/org/aion/precompiled/contracts/TRS/AbstractTRS.java b/modPrecompiled/src/org/aion/precompiled/contracts/TRS/AbstractTRS.java index 542ba7020a..3c57dc3711 100644 --- a/modPrecompiled/src/org/aion/precompiled/contracts/TRS/AbstractTRS.java +++ b/modPrecompiled/src/org/aion/precompiled/contracts/TRS/AbstractTRS.java @@ -6,18 +6,17 @@ import java.nio.ByteBuffer; import java.util.Arrays; import java.util.concurrent.TimeUnit; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.mcf.core.AccountState; import org.aion.mcf.core.IBlockchain; import org.aion.mcf.db.IBlockStoreBase; -import org.aion.mcf.vm.types.DataWord; import org.aion.mcf.vm.types.DoubleDataWord; import org.aion.precompiled.PrecompiledTransactionResult; import org.aion.precompiled.type.StatefulPrecompiledContract; -import org.aion.vm.api.interfaces.Address; +import org.aion.util.bytes.ByteUtil; /** * The purpose of this abstract class is mostly as a place to store important constants and methods @@ -26,7 +25,7 @@ public abstract class AbstractTRS extends StatefulPrecompiledContract { // TODO: grab AION from CfgAion later and preferably aion prefix too. static final Address AION = - AionAddress.wrap("0xa0eeaeabdbc92953b072afbd21f3e3fd8a4a4f5e6a6e22200db746ab75e9a99a"); + Address.wrap("0xa0eeaeabdbc92953b072afbd21f3e3fd8a4a4f5e6a6e22200db746ab75e9a99a"); static final long COST = 21000L; // temporary. private static final long TEST_DURATION = 1; private static final long PERIOD_DURATION = TimeUnit.DAYS.toSeconds(30); @@ -60,7 +59,7 @@ public abstract class AbstractTRS extends StatefulPrecompiledContract { private static final byte EXTRA_FUNDS_PREFIX = (byte) 0x92; private static final int DOUBLE_WORD_SIZE = DoubleDataWord.BYTES; - private static final int SINGLE_WORD_SIZE = DataWord.BYTES; + private static final int SINGLE_WORD_SIZE = DataWordImpl.BYTES; private static final int MAX_DEPOSIT_ROWS = 16; private static final byte NULL_BIT = (byte) 0x80; private static final byte VALID_BIT = (byte) 0x40; @@ -101,7 +100,7 @@ public abstract class AbstractTRS extends StatefulPrecompiledContract { // Constructor. AbstractTRS( - IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> track, + RepositoryCache<AccountState, IBlockStoreBase<?, ?>> track, Address caller, IBlockchain blockchain) { super(track); @@ -196,7 +195,7 @@ void setContractSpecs( */ public Address getContractOwner(Address contract) { ByteArrayWrapper owner = track.getStorageValue(contract, OWNER_KEY); - return (owner == null) ? null : new AionAddress(owner.getData()); + return (owner == null) ? null : new Address(owner.getData()); } /** @@ -739,11 +738,11 @@ public void setTimestamp(Address contract, long timestamp) { if (track.getStorageValue(contract, TIMESTAMP) != null) { return; } - byte[] value = new byte[DataWord.BYTES]; + byte[] value = new byte[DataWordImpl.BYTES]; ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); buffer.putLong(timestamp); - System.arraycopy(buffer.array(), 0, value, DataWord.BYTES - Long.BYTES, Long.BYTES); + System.arraycopy(buffer.array(), 0, value, DataWordImpl.BYTES - Long.BYTES, Long.BYTES); track.addStorageRow(contract, TIMESTAMP, toByteArrayWrapper(value)); } @@ -764,7 +763,7 @@ public long getTimestamp(Address contract) { } ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); buffer.put( - Arrays.copyOfRange(value.getData(), DataWord.BYTES - Long.BYTES, DataWord.BYTES)); + Arrays.copyOfRange(value.getData(), DataWordImpl.BYTES - Long.BYTES, DataWordImpl.BYTES)); buffer.flip(); return buffer.getLong(); } @@ -1671,7 +1670,7 @@ private byte[] makeTotalBalanceKey(int row) { */ private byte[] makeBalanceKey(Address account, int row) { if (account == null) { - return DataWord.ZERO.getData(); + return DataWordImpl.ZERO.getData(); } byte[] balKey = new byte[DOUBLE_WORD_SIZE]; balKey[0] = (byte) (BALANCE_PREFIX | row); @@ -1689,7 +1688,7 @@ private byte[] makeBalanceKey(Address account, int row) { */ private byte[] makeWithdrawalKey(Address account) { if (account == null) { - return DataWord.ZERO.getData(); + return DataWordImpl.ZERO.getData(); } return makeWithdrawalKey(account.toBytes()); } @@ -1704,7 +1703,7 @@ private byte[] makeWithdrawalKey(Address account) { */ private byte[] makeWithdrawalKey(byte[] account) { if (account == null) { - return DataWord.ZERO.getData(); + return DataWordImpl.ZERO.getData(); } byte[] withKey = new byte[DOUBLE_WORD_SIZE]; withKey[0] = WITHDRAW_PREFIX; @@ -1751,7 +1750,7 @@ private byte[] toDoubleWordAlignedArray(BigInteger balance) { */ private static ByteArrayWrapper toByteArrayWrapper(byte[] word) { if (word.length == SINGLE_WORD_SIZE) { - return new DataWord(word).toWrapper(); + return new DataWordImpl(word).toWrapper(); } else if (word.length == DOUBLE_WORD_SIZE) { return new DoubleDataWord(word).toWrapper(); } else { diff --git a/modPrecompiled/src/org/aion/precompiled/contracts/TRS/PrivateTRScontract.java b/modPrecompiled/src/org/aion/precompiled/contracts/TRS/PrivateTRScontract.java index db5e28873d..df6c79ffe5 100644 --- a/modPrecompiled/src/org/aion/precompiled/contracts/TRS/PrivateTRScontract.java +++ b/modPrecompiled/src/org/aion/precompiled/contracts/TRS/PrivateTRScontract.java @@ -1,11 +1,11 @@ package org.aion.precompiled.contracts.TRS; -import org.aion.base.db.IRepositoryCache; +import org.aion.interfaces.db.RepositoryCache; import org.aion.mcf.core.AccountState; import org.aion.mcf.db.IBlockStoreBase; import org.aion.precompiled.PrecompiledTransactionResult; import org.aion.precompiled.type.StatefulPrecompiledContract; -import org.aion.vm.api.interfaces.Address; +import org.aion.types.Address; /** * The PrivateTRScontract is a private version of the TRS contract that is used solely by The Aion @@ -30,7 +30,7 @@ public final class PrivateTRScontract extends StatefulPrecompiledContract { * @param caller The calling address. */ public PrivateTRScontract( - IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> repo, Address caller) { + RepositoryCache<AccountState, IBlockStoreBase<?, ?>> repo, Address caller) { super(repo); this.caller = caller; diff --git a/modPrecompiled/src/org/aion/precompiled/contracts/TRS/TRSqueryContract.java b/modPrecompiled/src/org/aion/precompiled/contracts/TRS/TRSqueryContract.java index 095aca79c6..898d30f8ea 100644 --- a/modPrecompiled/src/org/aion/precompiled/contracts/TRS/TRSqueryContract.java +++ b/modPrecompiled/src/org/aion/precompiled/contracts/TRS/TRSqueryContract.java @@ -5,15 +5,15 @@ import java.math.RoundingMode; import java.nio.ByteBuffer; import java.util.Arrays; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; -import org.aion.base.type.IBlock; + +import org.aion.interfaces.block.Block; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.types.Address; import org.aion.mcf.core.AccountState; import org.aion.mcf.core.IBlockchain; import org.aion.mcf.db.IBlockStoreBase; import org.aion.precompiled.PrecompiledResultCode; import org.aion.precompiled.PrecompiledTransactionResult; -import org.aion.vm.api.interfaces.Address; /** * The TRSqueryContract is 1 of 3 inter-dependent but separate contracts that together make up the @@ -48,7 +48,7 @@ public final class TRSqueryContract extends AbstractTRS { * @param caller The calling address. */ public TRSqueryContract( - IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> repo, + RepositoryCache<AccountState, IBlockStoreBase<?, ?>> repo, Address caller, IBlockchain blockchain) { @@ -214,7 +214,7 @@ private PrecompiledTransactionResult isStarted(byte[] input, long nrgLimit) { } byte[] result = new byte[1]; - Address contract = AionAddress.wrap(Arrays.copyOfRange(input, indexAddress, len)); + Address contract = Address.wrap(Arrays.copyOfRange(input, indexAddress, len)); if (!isOpenFunds(contract) && isContractLive(contract)) { result[0] = 0x1; } @@ -247,7 +247,7 @@ private PrecompiledTransactionResult isLocked(byte[] input, long nrgLimit) { } byte[] result = new byte[1]; - Address contract = AionAddress.wrap(Arrays.copyOfRange(input, indexAddress, len)); + Address contract = Address.wrap(Arrays.copyOfRange(input, indexAddress, len)); if (!isOpenFunds(contract) && isContractLocked(contract)) { result[0] = 0x1; } @@ -281,7 +281,7 @@ private PrecompiledTransactionResult isDirectDepositEnabled(byte[] input, long n } byte[] result = new byte[1]; - Address contract = AionAddress.wrap(Arrays.copyOfRange(input, indexAddress, len)); + Address contract = Address.wrap(Arrays.copyOfRange(input, indexAddress, len)); if (!isOpenFunds(contract) && isDirDepositsEnabled(contract)) { result[0] = 0x1; } @@ -322,7 +322,7 @@ private PrecompiledTransactionResult period(byte[] input, long nrgLimit) { } // Grab the contract address and block number and determine the period. - Address contract = AionAddress.wrap(Arrays.copyOfRange(input, indexAddress, len)); + Address contract = Address.wrap(Arrays.copyOfRange(input, indexAddress, len)); return determinePeriod(contract, blockchain.getBestBlock(), nrgLimit); } @@ -360,7 +360,7 @@ private PrecompiledTransactionResult periodAt(byte[] input, long nrgLimit) { } // Grab the contract address and block number and determine the period. - Address contract = AionAddress.wrap(Arrays.copyOfRange(input, indexAddress, indexBlockNum)); + Address contract = Address.wrap(Arrays.copyOfRange(input, indexAddress, indexBlockNum)); ByteBuffer blockBuf = ByteBuffer.allocate(Long.BYTES); blockBuf.put(Arrays.copyOfRange(input, indexBlockNum, len)); @@ -405,7 +405,7 @@ private PrecompiledTransactionResult availableForWithdrawalAt(byte[] input, long } Address contract = - AionAddress.wrap(Arrays.copyOfRange(input, indexContract, indexTimestamp)); + Address.wrap(Arrays.copyOfRange(input, indexContract, indexTimestamp)); byte[] specs = getContractSpecs(contract); if (specs == null) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); @@ -478,7 +478,7 @@ private PrecompiledTransactionResult availableForWithdrawalAt(byte[] input, long * @param nrg The energy. * @return the period the contract is in at time given by block's timestamp. */ - private PrecompiledTransactionResult determinePeriod(Address contract, IBlock block, long nrg) { + private PrecompiledTransactionResult determinePeriod(Address contract, Block block, long nrg) { // If contract doesn't exist, return an error. ByteBuffer output = ByteBuffer.allocate(Integer.BYTES); diff --git a/modPrecompiled/src/org/aion/precompiled/contracts/TRS/TRSstateContract.java b/modPrecompiled/src/org/aion/precompiled/contracts/TRS/TRSstateContract.java index bcbb3c11bc..c9c384fe9e 100644 --- a/modPrecompiled/src/org/aion/precompiled/contracts/TRS/TRSstateContract.java +++ b/modPrecompiled/src/org/aion/precompiled/contracts/TRS/TRSstateContract.java @@ -4,15 +4,14 @@ import java.math.BigInteger; import java.util.Arrays; import java.util.concurrent.TimeUnit; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.types.Address; import org.aion.crypto.HashUtil; import org.aion.mcf.core.AccountState; import org.aion.mcf.core.IBlockchain; import org.aion.mcf.db.IBlockStoreBase; import org.aion.precompiled.PrecompiledResultCode; import org.aion.precompiled.PrecompiledTransactionResult; -import org.aion.vm.api.interfaces.Address; /** * The TRSstateContract is 1 of 3 inter-dependent but separate contracts that together make up the @@ -45,7 +44,7 @@ public final class TRSstateContract extends AbstractTRS { * @throws NullPointerException if track or caller are null. */ public TRSstateContract( - IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> track, + RepositoryCache<AccountState, IBlockStoreBase<?, ?>> track, Address caller, IBlockchain blockchain) { @@ -239,7 +238,7 @@ private PrecompiledTransactionResult create(byte[] input, long nrgLimit) { System.arraycopy(caller.toBytes(), 0, hashInfo, ownerNonce.length, Address.SIZE); byte[] trsAddr = HashUtil.h256(hashInfo); trsAddr[0] = TRS_PREFIX; - Address contract = new AionAddress(trsAddr); + Address contract = new Address(trsAddr); saveNewContract(contract, isTestContract, isDirectDeposit, periods, percent, precision); return new PrecompiledTransactionResult( @@ -274,7 +273,7 @@ private PrecompiledTransactionResult lock(byte[] input, long nrgLimit) { // The caller must also be the owner of this contract. Address contract = - new AionAddress(Arrays.copyOfRange(input, indexAddr, indexAddr + Address.SIZE)); + new Address(Arrays.copyOfRange(input, indexAddr, indexAddr + Address.SIZE)); if (!caller.equals(getContractOwner(contract))) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); } @@ -329,7 +328,7 @@ private PrecompiledTransactionResult start(byte[] input, long nrgLimit) { // The caller must also be the owner of this contract. Address contract = - new AionAddress(Arrays.copyOfRange(input, indexAddr, indexAddr + Address.SIZE)); + new Address(Arrays.copyOfRange(input, indexAddr, indexAddr + Address.SIZE)); if (!caller.equals(getContractOwner(contract))) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); } @@ -379,7 +378,7 @@ private PrecompiledTransactionResult openFunds(byte[] input, long nrgLimit) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); } - Address contract = AionAddress.wrap(Arrays.copyOfRange(input, indexContract, len)); + Address contract = Address.wrap(Arrays.copyOfRange(input, indexContract, len)); byte[] specs = getContractSpecs(contract); if (specs == null) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); diff --git a/modPrecompiled/src/org/aion/precompiled/contracts/TRS/TRSuseContract.java b/modPrecompiled/src/org/aion/precompiled/contracts/TRS/TRSuseContract.java index 28b2506c11..3d1a78d1da 100644 --- a/modPrecompiled/src/org/aion/precompiled/contracts/TRS/TRSuseContract.java +++ b/modPrecompiled/src/org/aion/precompiled/contracts/TRS/TRSuseContract.java @@ -2,14 +2,13 @@ import java.math.BigInteger; import java.util.Arrays; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.types.Address; import org.aion.mcf.core.AccountState; import org.aion.mcf.core.IBlockchain; import org.aion.mcf.db.IBlockStoreBase; import org.aion.precompiled.PrecompiledResultCode; import org.aion.precompiled.PrecompiledTransactionResult; -import org.aion.vm.api.interfaces.Address; /** * The TRSuseContract is 1 of 3 inter-dependent but separate contracts that together make up the @@ -45,7 +44,7 @@ public final class TRSuseContract extends AbstractTRS { * @param caller The calling address. */ public TRSuseContract( - IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> repo, + RepositoryCache<AccountState, IBlockStoreBase<?, ?>> repo, Address caller, IBlockchain blockchain) { @@ -253,7 +252,7 @@ private PrecompiledTransactionResult deposit(byte[] input, long nrgLimit) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); } - Address contract = AionAddress.wrap(Arrays.copyOfRange(input, indexAddress, indexAmount)); + Address contract = Address.wrap(Arrays.copyOfRange(input, indexAddress, indexAmount)); byte[] specs = getContractSpecs(contract); if (specs == null) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); @@ -314,7 +313,7 @@ private PrecompiledTransactionResult withdraw(byte[] input, long nrgLimit) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); } - Address contract = AionAddress.wrap(Arrays.copyOfRange(input, indexAddress, len)); + Address contract = Address.wrap(Arrays.copyOfRange(input, indexAddress, len)); byte[] specs = getContractSpecs(contract); if (specs == null) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); @@ -374,7 +373,7 @@ private PrecompiledTransactionResult bulkDepositFor(byte[] input, long nrgLimit) return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); } - Address contract = AionAddress.wrap(Arrays.copyOfRange(input, indexContract, indexEntries)); + Address contract = Address.wrap(Arrays.copyOfRange(input, indexContract, indexEntries)); byte[] specs = getContractSpecs(contract); if (specs == null) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); @@ -397,7 +396,7 @@ private PrecompiledTransactionResult bulkDepositFor(byte[] input, long nrgLimit) int amtLen = entryLen - entryAddrLen; int index = indexEntries; byte[] amountBytes; - Address[] beneficiaries = new AionAddress[numEntries]; + Address[] beneficiaries = new Address[numEntries]; BigInteger[] amounts = new BigInteger[numEntries]; for (int i = 0; i < numEntries; i++) { // Put amount in a byte array one byte larger with an empty initial byte so it is @@ -412,7 +411,7 @@ private PrecompiledTransactionResult bulkDepositFor(byte[] input, long nrgLimit) } beneficiaries[i] = - AionAddress.wrap(Arrays.copyOfRange(input, index, index + entryAddrLen)); + Address.wrap(Arrays.copyOfRange(input, index, index + entryAddrLen)); index += 32 + 128; } @@ -458,7 +457,7 @@ private PrecompiledTransactionResult bulkWithdraw(byte[] input, long nrgLimit) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); } - Address contract = AionAddress.wrap(Arrays.copyOfRange(input, indexAddress, len)); + Address contract = Address.wrap(Arrays.copyOfRange(input, indexAddress, len)); byte[] specs = getContractSpecs(contract); if (specs == null) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); @@ -483,7 +482,7 @@ private PrecompiledTransactionResult bulkWithdraw(byte[] input, long nrgLimit) { while (curr != null) { curr[0] = AION_PREFIX; - Address currAcct = new AionAddress(curr); + Address currAcct = new Address(curr); makeWithdrawal(contract, currAcct); curr = getListNext(contract, currAcct); } @@ -524,7 +523,7 @@ private PrecompiledTransactionResult refund(byte[] input, long nrgLimit) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); } - Address contract = AionAddress.wrap(Arrays.copyOfRange(input, indexContract, indexAccount)); + Address contract = Address.wrap(Arrays.copyOfRange(input, indexContract, indexAccount)); byte[] specs = getContractSpecs(contract); if (specs == null) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); @@ -544,7 +543,7 @@ private PrecompiledTransactionResult refund(byte[] input, long nrgLimit) { } // Ensure the account exists (ie. has a positive deposit balance for the contract). - Address account = AionAddress.wrap(Arrays.copyOfRange(input, indexAccount, indexAmount)); + Address account = Address.wrap(Arrays.copyOfRange(input, indexAccount, indexAmount)); BigInteger accountBalance = getDepositBalance(contract, account); if (accountBalance.equals(BigInteger.ZERO)) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); @@ -611,7 +610,7 @@ private PrecompiledTransactionResult depositFor(byte[] input, long nrgLimit) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); } - Address contract = AionAddress.wrap(Arrays.copyOfRange(input, indexContract, indexAccount)); + Address contract = Address.wrap(Arrays.copyOfRange(input, indexContract, indexAccount)); byte[] specs = getContractSpecs(contract); if (specs == null) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); @@ -645,7 +644,7 @@ private PrecompiledTransactionResult depositFor(byte[] input, long nrgLimit) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); } - Address account = AionAddress.wrap(Arrays.copyOfRange(input, indexAccount, indexAmount)); + Address account = Address.wrap(Arrays.copyOfRange(input, indexAccount, indexAmount)); PrecompiledTransactionResult result = makeDeposit(contract, account, amount, nrgLimit); if (result.getResultCode().equals(PrecompiledResultCode.SUCCESS)) { track.flush(); @@ -679,7 +678,7 @@ private PrecompiledTransactionResult addExtraFunds(byte[] input, long nrgLimit) return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); } - Address contract = AionAddress.wrap(Arrays.copyOfRange(input, indexContract, indexAmount)); + Address contract = Address.wrap(Arrays.copyOfRange(input, indexContract, indexAmount)); byte[] specs = getContractSpecs(contract); if (specs == null) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); @@ -793,7 +792,7 @@ private void listAddToHead(Address contract, Address account) { head[0] = AION_PREFIX; setListPrevious( contract, - AionAddress.wrap(head), + Address.wrap(head), Arrays.copyOf(account.toBytes(), Address.SIZE)); } diff --git a/modPrecompiled/src/org/aion/precompiled/contracts/TotalCurrencyContract.java b/modPrecompiled/src/org/aion/precompiled/contracts/TotalCurrencyContract.java index b281752ca4..3322d0ebf4 100644 --- a/modPrecompiled/src/org/aion/precompiled/contracts/TotalCurrencyContract.java +++ b/modPrecompiled/src/org/aion/precompiled/contracts/TotalCurrencyContract.java @@ -1,19 +1,18 @@ package org.aion.precompiled.contracts; import java.math.BigInteger; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; -import org.aion.base.util.BIUtil; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.crypto.ed25519.ECKeyEd25519; import org.aion.crypto.ed25519.Ed25519Signature; import org.aion.mcf.core.AccountState; import org.aion.mcf.db.IBlockStoreBase; -import org.aion.mcf.vm.types.DataWord; import org.aion.precompiled.PrecompiledResultCode; import org.aion.precompiled.PrecompiledTransactionResult; import org.aion.precompiled.type.StatefulPrecompiledContract; -import org.aion.vm.api.interfaces.Address; +import org.aion.util.biginteger.BIUtil; /** A pre-compiled contract for retrieving and updating the total amount of currency. */ public class TotalCurrencyContract extends StatefulPrecompiledContract { @@ -31,7 +30,7 @@ public class TotalCurrencyContract extends StatefulPrecompiledContract { * @param ownerAddress */ public TotalCurrencyContract( - IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> track, + RepositoryCache<AccountState, IBlockStoreBase<?, ?>> track, Address address, Address ownerAddress) { super(track); @@ -97,7 +96,7 @@ private PrecompiledTransactionResult queryNetworkBalance(int input, long nrg) { } ByteArrayWrapper balanceData = - this.track.getStorageValue(this.address, new DataWord(input).toWrapper()); + this.track.getStorageValue(this.address, new DataWordImpl(input).toWrapper()); return new PrecompiledTransactionResult( PrecompiledResultCode.SUCCESS, nrg - COST, balanceData.getData()); } @@ -114,7 +113,7 @@ private PrecompiledTransactionResult executeUpdateTotalBalance(byte[] input, lon // process input data int offset = 0; - DataWord chainId = new DataWord(input[0]); + DataWordImpl chainId = new DataWordImpl(input[0]); offset++; byte signum = input[1]; @@ -142,7 +141,7 @@ private PrecompiledTransactionResult executeUpdateTotalBalance(byte[] input, lon } // verify public key matches owner - if (!this.ownerAddress.equals(AionAddress.wrap(sig.getAddress()))) { + if (!this.ownerAddress.equals(Address.wrap(sig.getAddress()))) { return new PrecompiledTransactionResult(PrecompiledResultCode.FAILURE, 0); } @@ -173,11 +172,11 @@ private PrecompiledTransactionResult executeUpdateTotalBalance(byte[] input, lon this.track.addStorageRow( this.address, chainId.toWrapper(), - wrapValueForPut(new DataWord(finalValue.toByteArray()))); + wrapValueForPut(new DataWordImpl(finalValue.toByteArray()))); return new PrecompiledTransactionResult(PrecompiledResultCode.SUCCESS, nrg - COST); } - private static ByteArrayWrapper wrapValueForPut(DataWord value) { + private static ByteArrayWrapper wrapValueForPut(DataWordImpl value) { return (value.isZero()) ? value.toWrapper() : new ByteArrayWrapper(value.getNoLeadZeroesData()); } } diff --git a/modPrecompiled/src/org/aion/precompiled/type/StatefulPrecompiledContract.java b/modPrecompiled/src/org/aion/precompiled/type/StatefulPrecompiledContract.java index 5fc741c7ee..094d2ee111 100644 --- a/modPrecompiled/src/org/aion/precompiled/type/StatefulPrecompiledContract.java +++ b/modPrecompiled/src/org/aion/precompiled/type/StatefulPrecompiledContract.java @@ -1,6 +1,6 @@ package org.aion.precompiled.type; -import org.aion.base.db.IRepositoryCache; +import org.aion.interfaces.db.RepositoryCache; import org.aion.mcf.core.AccountState; import org.aion.mcf.db.IBlockStoreBase; @@ -13,7 +13,7 @@ public abstract class StatefulPrecompiledContract implements PrecompiledContract { public static final long TX_NRG_MIN = 20_999; public static final long TX_NRG_MAX = 2_000_001; - protected final IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> track; + protected final RepositoryCache<AccountState, IBlockStoreBase<?, ?>> track; /** * Constructs a new StatefulPrecompiledContract. @@ -21,7 +21,7 @@ public abstract class StatefulPrecompiledContract implements PrecompiledContract * @param track */ public StatefulPrecompiledContract( - IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> track) { + RepositoryCache<AccountState, IBlockStoreBase<?, ?>> track) { if (track == null) { throw new IllegalArgumentException("Null track."); diff --git a/modPrecompiled/test/org/aion/precompiled/TRS/TRShelpers.java b/modPrecompiled/test/org/aion/precompiled/TRS/TRShelpers.java index 935eaedc65..9ebcc9131e 100644 --- a/modPrecompiled/test/org/aion/precompiled/TRS/TRShelpers.java +++ b/modPrecompiled/test/org/aion/precompiled/TRS/TRShelpers.java @@ -15,14 +15,14 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; import org.aion.mcf.core.AccountState; import org.aion.mcf.db.IBlockStoreBase; -import org.aion.mcf.vm.types.DataWord; import org.aion.mcf.vm.types.DoubleDataWord; import org.aion.precompiled.PrecompiledResultCode; import org.aion.precompiled.PrecompiledTransactionResult; @@ -30,7 +30,7 @@ import org.aion.precompiled.contracts.TRS.TRSqueryContract; import org.aion.precompiled.contracts.TRS.TRSstateContract; import org.aion.precompiled.contracts.TRS.TRSuseContract; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.impl.StandaloneBlockchain; import org.aion.zero.impl.core.IAionBlockchain; import org.aion.zero.impl.types.AionBlock; @@ -42,15 +42,15 @@ class TRShelpers { static final BigInteger DEFAULT_BALANCE = BigInteger.TEN; private IAionBlockchain blockchain = StandaloneBlockchain.inst(); Address AION = - AionAddress.wrap("0xa0eeaeabdbc92953b072afbd21f3e3fd8a4a4f5e6a6e22200db746ab75e9a99a"); - IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> repo; + Address.wrap("0xa0eeaeabdbc92953b072afbd21f3e3fd8a4a4f5e6a6e22200db746ab75e9a99a"); + RepositoryCache<AccountState, IBlockStoreBase<?, ?>> repo; List<Address> tempAddrs; ECKey senderKey; long COST = 21000L; // Returns a new account with initial balance balance that exists in the repo. Address getNewExistentAccount(BigInteger balance) { - Address acct = AionAddress.wrap(ECKeyFac.inst().create().getAddress()); + Address acct = Address.wrap(ECKeyFac.inst().create().getAddress()); acct.toBytes()[0] = (byte) 0xA0; repo.createAccount(acct); repo.addBalance(acct, balance); @@ -89,7 +89,7 @@ Address createTRScontract( if (!res.getResultCode().equals(PrecompiledResultCode.SUCCESS)) { fail("Unable to create contract!"); } - Address contract = new AionAddress(res.getReturnData()); + Address contract = new Address(res.getReturnData()); tempAddrs.add(contract); repo.incrementNonce(owner); repo.flush(); @@ -732,7 +732,7 @@ byte[] getMaxDepositInput(Address contract) { // Makes input for numBeneficiaries beneficiaries who each receive a deposit amount deposits. byte[] makeBulkDepositForInput(Address contract, int numBeneficiaries, BigInteger deposits) { - Address[] beneficiaries = new AionAddress[numBeneficiaries]; + Address[] beneficiaries = new Address[numBeneficiaries]; BigInteger[] amounts = new BigInteger[numBeneficiaries]; for (int i = 0; i < numBeneficiaries; i++) { beneficiaries[i] = getNewExistentAccount(BigInteger.ZERO); @@ -745,7 +745,7 @@ byte[] makeBulkDepositForInput(Address contract, int numBeneficiaries, BigIntege byte[] makeBulkDepositForInputwithSelf( Address contract, Address self, int numOthers, BigInteger deposits) { - Address[] beneficiaries = new AionAddress[numOthers + 1]; + Address[] beneficiaries = new Address[numOthers + 1]; BigInteger[] amounts = new BigInteger[numOthers + 1]; for (int i = 0; i < numOthers; i++) { beneficiaries[i] = getNewExistentAccount(BigInteger.ZERO); @@ -781,7 +781,7 @@ Address getLinkedListHead(AbstractTRS trs, Address contract) { return null; } head[0] = (byte) 0xA0; - return new AionAddress(head); + return new Address(head); } // Returns the next account in the linked list after current, or null if no next. @@ -792,7 +792,7 @@ Address getLinkedListNext(AbstractTRS trs, Address contract, Address current) { return null; } next[0] = (byte) 0xA0; - return new AionAddress(next); + return new Address(next); } // Returns the previous account in the linked list prior to current, or null if no previous. @@ -802,7 +802,7 @@ Address getLinkedListPrev(AbstractTRS trs, Address contract, Address current) { return null; } prev[0] = (byte) 0xA0; - return new AionAddress(prev); + return new Address(prev); } // Checks that each account is paid out correctly when they withdraw in a non-final period. @@ -924,8 +924,8 @@ byte[] getTrueContractOutput() { // Returns a new ByteArrayWrapper that wraps data. Here so we can switch types easy if needed. ByteArrayWrapper newDataWordStub(byte[] data) { - if (data.length == DataWord.BYTES) { - return new DataWord(data).toWrapper(); + if (data.length == DataWordImpl.BYTES) { + return new DataWordImpl(data).toWrapper(); } else if (data.length == DoubleDataWord.BYTES) { return new DoubleDataWord(data).toWrapper(); } else { diff --git a/modPrecompiled/test/org/aion/precompiled/TRS/TRSlinkedListTest.java b/modPrecompiled/test/org/aion/precompiled/TRS/TRSlinkedListTest.java index 5e1c78a2ff..80e26e7180 100644 --- a/modPrecompiled/test/org/aion/precompiled/TRS/TRSlinkedListTest.java +++ b/modPrecompiled/test/org/aion/precompiled/TRS/TRSlinkedListTest.java @@ -11,12 +11,12 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.Set; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.precompiled.PrecompiledResultCode; import org.aion.precompiled.contracts.DummyRepo; import org.aion.precompiled.contracts.TRS.AbstractTRS; import org.aion.precompiled.contracts.TRS.TRSuseContract; -import org.aion.vm.api.interfaces.Address; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -482,7 +482,7 @@ private void checkRemoveTailOfLargerList(Address contract, Address owner, int li // unique too. TRSuseContract trs = newTRSuseContract(owner); Address next = getLinkedListHead(trs, contract); - Address head = new AionAddress(next.toBytes()); + Address head = new Address(next.toBytes()); Set<Address> addressesInList = new HashSet<>(); for (int i = 0; i < listSize; i++) { if (i == listSize - 1) { @@ -542,7 +542,7 @@ private void checkRemoveInteriorOfLargerList(Address contract, Address owner, in // unique too. TRSuseContract trs = newTRSuseContract(owner); Address next = getLinkedListHead(trs, contract); - Address head = new AionAddress(next.toBytes()); + Address head = new Address(next.toBytes()); Address mid = null; Set<Address> addressesInList = new HashSet<>(); for (int i = 0; i < listSize; i++) { @@ -553,7 +553,7 @@ private void checkRemoveInteriorOfLargerList(Address contract, Address owner, in assertNotNull(next); assertFalse(addressesInList.contains(next)); addressesInList.add(next); - mid = new AionAddress(next.toBytes()); + mid = new Address(next.toBytes()); } else { next = getLinkedListNext(trs, contract, next); assertNotNull(next); diff --git a/modPrecompiled/test/org/aion/precompiled/TRS/TRSqueryContractTest.java b/modPrecompiled/test/org/aion/precompiled/TRS/TRSqueryContractTest.java index 74bb4e8125..e7c35f7893 100644 --- a/modPrecompiled/test/org/aion/precompiled/TRS/TRSqueryContractTest.java +++ b/modPrecompiled/test/org/aion/precompiled/TRS/TRSqueryContractTest.java @@ -10,8 +10,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Set; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteUtil; +import org.aion.types.Address; import org.aion.mcf.vm.types.DoubleDataWord; import org.aion.precompiled.PrecompiledResultCode; import org.aion.precompiled.PrecompiledTransactionResult; @@ -19,7 +18,8 @@ import org.aion.precompiled.contracts.TRS.AbstractTRS; import org.aion.precompiled.contracts.TRS.TRSqueryContract; import org.aion.precompiled.type.StatefulPrecompiledContract; -import org.aion.vm.api.interfaces.Address; + +import org.aion.util.bytes.ByteUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -142,7 +142,7 @@ public void testIsLiveNonExistentContract() { // Test on a contract address that looks real, uses TRS prefix. byte[] phony = acct.toBytes(); phony[0] = (byte) 0xC0; - input = getIsLiveInput(new AionAddress(phony)); + input = getIsLiveInput(new Address(phony)); res = newTRSqueryContract(acct).execute(input, COST); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); @@ -217,7 +217,7 @@ public void testIsLockedNonExistentContract() { // Test on a contract address that looks real, uses TRS prefix. byte[] phony = acct.toBytes(); phony[0] = (byte) 0xC0; - input = getIsLockedInput(new AionAddress(phony)); + input = getIsLockedInput(new Address(phony)); res = newTRSqueryContract(acct).execute(input, COST); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); @@ -292,7 +292,7 @@ public void testIsDepoEnabledNonExistentContract() { // Test on a contract address that looks real, uses TRS prefix. byte[] phony = acct.toBytes(); phony[0] = (byte) 0xC0; - input = getIsDirDepoEnabledInput(new AionAddress(phony)); + input = getIsDirDepoEnabledInput(new Address(phony)); res = newTRSqueryContract(acct).execute(input, COST); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); diff --git a/modPrecompiled/test/org/aion/precompiled/TRS/TRSstateContractTest.java b/modPrecompiled/test/org/aion/precompiled/TRS/TRSstateContractTest.java index 1a89737d97..242caf81e8 100644 --- a/modPrecompiled/test/org/aion/precompiled/TRS/TRSstateContractTest.java +++ b/modPrecompiled/test/org/aion/precompiled/TRS/TRSstateContractTest.java @@ -12,8 +12,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.concurrent.TimeUnit; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteUtil; +import org.aion.types.Address; import org.aion.mcf.vm.types.DoubleDataWord; import org.aion.precompiled.PrecompiledResultCode; import org.aion.precompiled.PrecompiledTransactionResult; @@ -21,7 +20,8 @@ import org.aion.precompiled.contracts.TRS.AbstractTRS; import org.aion.precompiled.contracts.TRS.TRSstateContract; import org.aion.precompiled.type.StatefulPrecompiledContract; -import org.aion.vm.api.interfaces.Address; + +import org.aion.util.bytes.ByteUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -169,13 +169,13 @@ public void testCreateCorrectOwner() { Address caller = getNewExistentAccount(BigInteger.ZERO); TRSstateContract trs = newTRSstateContract(caller); PrecompiledTransactionResult res = trs.execute(input, COST); - Address contract = new AionAddress(res.getReturnData()); + Address contract = new Address(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(caller, getOwner(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); } @Test @@ -183,12 +183,12 @@ public void testCreateTestModeCorrectOwner() { byte[] input = getCreateInput(true, false, 1, BigInteger.ZERO, 0); TRSstateContract trs = newTRSstateContract(AION); PrecompiledTransactionResult res = trs.execute(input, COST); - Address contract = AionAddress.wrap(res.getReturnData()); + Address contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - assertEquals(AION, getOwner(trs, new AionAddress(res.getReturnData()))); + assertEquals(AION, getOwner(trs, new Address(res.getReturnData()))); } @Test @@ -199,13 +199,13 @@ public void testCreateVerifyPeriods() { Address caller = getNewExistentAccount(BigInteger.ZERO); TRSstateContract trs = newTRSstateContract(caller); PrecompiledTransactionResult res = trs.execute(input, COST); - Address contract = AionAddress.wrap(res.getReturnData()); + Address contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(periods, getPeriods(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); repo.incrementNonce(caller); repo.flush(); @@ -214,13 +214,13 @@ public void testCreateVerifyPeriods() { periods = 1200; input = getCreateInput(false, false, periods, BigInteger.ZERO, 0); res = trs.execute(input, COST); - contract = AionAddress.wrap(res.getReturnData()); + contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(periods, getPeriods(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); } @Test @@ -230,13 +230,13 @@ public void testCreateTestModeVerifyPeriods() { byte[] input = getCreateInput(true, false, periods, BigInteger.ZERO, 0); TRSstateContract trs = newTRSstateContract(AION); PrecompiledTransactionResult res = trs.execute(input, COST); - Address contract = AionAddress.wrap(res.getReturnData()); + Address contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(periods, getPeriods(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); repo.incrementNonce(AION); repo.flush(); @@ -245,13 +245,13 @@ public void testCreateTestModeVerifyPeriods() { periods = 1200; input = getCreateInput(true, false, periods, BigInteger.ZERO, 0); res = trs.execute(input, COST); - contract = AionAddress.wrap(res.getReturnData()); + contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(periods, getPeriods(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); } @Test @@ -266,39 +266,39 @@ public void testCreateVerifyPeriodsByteBoundary() { Address caller = getNewExistentAccount(BigInteger.ZERO); TRSstateContract trs = newTRSstateContract(caller); PrecompiledTransactionResult res = trs.execute(input, COST); - Address contract = AionAddress.wrap(res.getReturnData()); + Address contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(periods, getPeriods(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); repo.incrementNonce(caller); repo.flush(); input = getCreateInput(false, false, periods2, BigInteger.ZERO, 0); res = trs.execute(input, COST); - contract = AionAddress.wrap(res.getReturnData()); + contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(periods2, getPeriods(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); repo.incrementNonce(caller); repo.flush(); input = getCreateInput(false, false, periods3, BigInteger.ZERO, 0); res = trs.execute(input, COST); - contract = AionAddress.wrap(res.getReturnData()); + contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(periods3, getPeriods(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); } @Test @@ -308,26 +308,26 @@ public void testCreateVerifyIsTest() { byte[] input = getCreateInput(isTest, false, 1, BigInteger.ZERO, 0); TRSstateContract trs = newTRSstateContract(getNewExistentAccount(BigInteger.ZERO)); PrecompiledTransactionResult res = trs.execute(input, COST); - Address contract = AionAddress.wrap(res.getReturnData()); + Address contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(isTest, isTestContract(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); // When true isTest = true; input = getCreateInput(isTest, false, 1, BigInteger.ZERO, 0); trs = newTRSstateContract(AION); res = trs.execute(input, COST); - contract = AionAddress.wrap(res.getReturnData()); + contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(isTest, isTestContract(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); } @Test @@ -338,13 +338,13 @@ public void testCreateVerifyIsDirectDeposit() { byte[] input = getCreateInput(false, isDirectDeposit, 1, BigInteger.ZERO, 0); TRSstateContract trs = newTRSstateContract(caller); PrecompiledTransactionResult res = trs.execute(input, COST); - Address contract = AionAddress.wrap(res.getReturnData()); + Address contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(isDirectDeposit, isDirectDepositEnabled(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); repo.incrementNonce(caller); repo.flush(); @@ -353,13 +353,13 @@ public void testCreateVerifyIsDirectDeposit() { isDirectDeposit = true; input = getCreateInput(false, isDirectDeposit, 1, BigInteger.ZERO, 0); res = trs.execute(input, COST); - contract = AionAddress.wrap(res.getReturnData()); + contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(isDirectDeposit, isDirectDepositEnabled(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); } @Test @@ -369,13 +369,13 @@ public void testCreateVerifyZeroPercentage() { byte[] input = getCreateInput(false, false, 1, BigInteger.ZERO, 0); TRSstateContract trs = newTRSstateContract(caller); PrecompiledTransactionResult res = trs.execute(input, COST); - Address contract = AionAddress.wrap(res.getReturnData()); + Address contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(BigDecimal.ZERO, getPercentage(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); repo.incrementNonce(caller); repo.flush(); @@ -383,13 +383,13 @@ public void testCreateVerifyZeroPercentage() { // Test 18 shifts. input = getCreateInput(false, false, 1, BigInteger.ZERO, 18); res = trs.execute(input, COST); - contract = AionAddress.wrap(res.getReturnData()); + contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(BigDecimal.ZERO.movePointLeft(18), getPercentage(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); } @Test @@ -400,13 +400,13 @@ public void testCreateVerifyOneHundredPercentage() { byte[] input = getCreateInput(false, false, 1, raw, 0); TRSstateContract trs = newTRSstateContract(caller); PrecompiledTransactionResult res = trs.execute(input, COST); - Address contract = AionAddress.wrap(res.getReturnData()); + Address contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(new BigDecimal(raw), getPercentage(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); repo.incrementNonce(caller); repo.flush(); @@ -415,13 +415,13 @@ public void testCreateVerifyOneHundredPercentage() { raw = new BigInteger("100000000000000000000"); input = getCreateInput(false, false, 1, raw, 18); res = trs.execute(input, COST); - contract = AionAddress.wrap(res.getReturnData()); + contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(new BigDecimal(raw).movePointLeft(18), getPercentage(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); } @Test @@ -477,13 +477,13 @@ public void testCreateVerify18DecimalPercentage() { byte[] input = getCreateInput(false, false, 1, raw, 18); TRSstateContract trs = newTRSstateContract(caller); PrecompiledTransactionResult res = trs.execute(input, COST); - Address contract = AionAddress.wrap(res.getReturnData()); + Address contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(new BigDecimal(raw).movePointLeft(18), getPercentage(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); repo.incrementNonce(caller); repo.flush(); @@ -493,13 +493,13 @@ public void testCreateVerify18DecimalPercentage() { input = getCreateInput(false, false, 1, raw, 18); trs = newTRSstateContract(caller); res = trs.execute(input, COST); - contract = AionAddress.wrap(res.getReturnData()); + contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(new BigDecimal(raw).movePointLeft(18), getPercentage(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); repo.incrementNonce(caller); repo.flush(); @@ -509,13 +509,13 @@ public void testCreateVerify18DecimalPercentage() { input = getCreateInput(false, false, 1, raw, 18); trs = newTRSstateContract(caller); res = trs.execute(input, COST); - contract = AionAddress.wrap(res.getReturnData()); + contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(new BigDecimal(raw).movePointLeft(18), getPercentage(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); } @Test @@ -543,13 +543,13 @@ public void testCreateValidAndInvalidPercentagesByShifting() { input = getCreateInput(false, false, 1, raw, shifts); trs = newTRSstateContract(caller); res = trs.execute(input, COST); - Address contract = AionAddress.wrap(res.getReturnData()); + Address contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(new BigDecimal(raw).movePointLeft(shifts), getPercentage(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); repo.incrementNonce(caller); repo.flush(); @@ -559,13 +559,13 @@ public void testCreateValidAndInvalidPercentagesByShifting() { input = getCreateInput(false, false, 1, raw, shifts); trs = newTRSstateContract(caller); res = trs.execute(input, COST); - contract = AionAddress.wrap(res.getReturnData()); + contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(new BigDecimal(raw).movePointLeft(shifts), getPercentage(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); repo.incrementNonce(caller); repo.flush(); @@ -575,13 +575,13 @@ public void testCreateValidAndInvalidPercentagesByShifting() { input = getCreateInput(false, false, 1, raw, shifts); trs = newTRSstateContract(caller); res = trs.execute(input, COST); - contract = AionAddress.wrap(res.getReturnData()); + contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); assertEquals(new BigDecimal(raw).movePointLeft(shifts), getPercentage(trs, contract)); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); } @Test @@ -591,7 +591,7 @@ public void testCreateTRSaddressDeterministic() { byte[] input = getCreateInput(false, false, 1, BigInteger.ZERO, 0); TRSstateContract trs = newTRSstateContract(caller); PrecompiledTransactionResult res = trs.execute(input, COST); - Address contract = AionAddress.wrap(res.getReturnData()); + Address contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); tempAddrs.add(contract); @@ -605,9 +605,9 @@ public void testCreateTRSaddressDeterministic() { assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); - assertNotEquals(contract, AionAddress.wrap(res.getReturnData())); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); - contract = AionAddress.wrap(res.getReturnData()); + assertNotEquals(contract, Address.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); + contract = Address.wrap(res.getReturnData()); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); @@ -615,10 +615,10 @@ public void testCreateTRSaddressDeterministic() { // Same caller as original & nonce hasn't changed, should be same contract addr returned. trs = newTRSstateContract(caller); res = trs.execute(input, COST); - contract = AionAddress.wrap(res.getReturnData()); + contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); - assertEquals(contract, AionAddress.wrap(res.getReturnData())); + assertEquals(contract, Address.wrap(res.getReturnData())); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); @@ -630,10 +630,10 @@ public void testCreateTRSaddressDeterministic() { res = trs.execute(input, COST); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); - assertNotEquals(contract, AionAddress.wrap(res.getReturnData())); - tempAddrs.add(AionAddress.wrap(res.getReturnData())); + assertNotEquals(contract, Address.wrap(res.getReturnData())); + tempAddrs.add(Address.wrap(res.getReturnData())); - contract = AionAddress.wrap(res.getReturnData()); + contract = Address.wrap(res.getReturnData()); assertFalse(isContractLocked(trs, contract)); assertFalse(isContractLive(trs, contract)); } @@ -645,7 +645,7 @@ public void testCreateSuccessNrgLeft() { byte[] input = getCreateInput(false, false, 1, BigInteger.ZERO, 0); TRSstateContract trs = newTRSstateContract(caller); PrecompiledTransactionResult res = trs.execute(input, COST + diff); - Address contract = AionAddress.wrap(res.getReturnData()); + Address contract = Address.wrap(res.getReturnData()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(diff, res.getEnergyRemaining()); tempAddrs.add(contract); @@ -665,7 +665,7 @@ public void testCreateContractHasTimestamp() { long after = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); - Address contract = AionAddress.wrap(res.getReturnData()); + Address contract = Address.wrap(res.getReturnData()); long timestamp = getContractTimestamp(trs, contract); assertTrue(timestamp >= before); diff --git a/modPrecompiled/test/org/aion/precompiled/TRS/TRSuseContractTest.java b/modPrecompiled/test/org/aion/precompiled/TRS/TRSuseContractTest.java index 6ee288676a..1cfcd06908 100644 --- a/modPrecompiled/test/org/aion/precompiled/TRS/TRSuseContractTest.java +++ b/modPrecompiled/test/org/aion/precompiled/TRS/TRSuseContractTest.java @@ -11,8 +11,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Set; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteUtil; +import org.aion.types.Address; import org.aion.crypto.ECKeyFac; import org.aion.mcf.vm.types.DoubleDataWord; import org.aion.precompiled.PrecompiledResultCode; @@ -21,7 +20,8 @@ import org.aion.precompiled.contracts.TRS.AbstractTRS; import org.aion.precompiled.contracts.TRS.TRSuseContract; import org.aion.precompiled.type.StatefulPrecompiledContract; -import org.aion.vm.api.interfaces.Address; + +import org.aion.util.bytes.ByteUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -152,7 +152,7 @@ public void testDepositToNonExistentContract() { System.arraycopy(acct.toBytes(), 0, addr, 0, Address.SIZE); addr[0] = (byte) 0xC0; - input = getDepositInput(AionAddress.wrap(addr), BigInteger.TWO); + input = getDepositInput(Address.wrap(addr), BigInteger.TWO); res = trs.execute(input, COST); assertEquals(PrecompiledResultCode.FAILURE, res.getResultCode()); assertEquals(0, res.getEnergyRemaining()); @@ -1985,7 +1985,7 @@ public void testRefundBadTRScontract() { // Test TRS address with TRS prefix, so it looks legit. byte[] addr = ECKeyFac.inst().create().getAddress(); addr[0] = (byte) 0xC0; - contract = new AionAddress(addr); + contract = new Address(addr); tempAddrs.add(contract); input = getRefundInput(contract, acct, BigInteger.ZERO); res = newTRSuseContract(acct).execute(input, COST); @@ -2469,7 +2469,7 @@ public void testDepositForContractAddressIsInvalid() { Address other = getNewExistentAccount(DEFAULT_BALANCE); byte[] addr = Arrays.copyOf(other.toBytes(), other.toBytes().length); addr[0] = (byte) 0xC0; - Address contract = new AionAddress(addr); + Address contract = new Address(addr); byte[] input = getDepositForInput(contract, other, BigInteger.ONE); PrecompiledTransactionResult res = newTRSuseContract(owner).execute(input, COST); diff --git a/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeControllerOwnerTest.java b/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeControllerOwnerTest.java index cabc1f57da..30b1c9a7f7 100644 --- a/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeControllerOwnerTest.java +++ b/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeControllerOwnerTest.java @@ -4,12 +4,12 @@ import static org.aion.precompiled.contracts.ATB.BridgeTestUtils.dummyContext; import java.util.List; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteUtil; +import org.aion.types.Address; import org.aion.crypto.HashUtil; import org.aion.fastvm.ExecutionContext; import org.aion.precompiled.contracts.DummyRepo; -import org.aion.vm.api.interfaces.Address; + +import org.aion.util.bytes.ByteUtil; import org.aion.vm.api.interfaces.IExecutionLog; import org.aion.vm.api.interfaces.TransactionSideEffects; import org.junit.Before; @@ -22,9 +22,9 @@ public class BridgeControllerOwnerTest { private TransactionSideEffects result; private static final Address CONTRACT_ADDR = - new AionAddress(HashUtil.h256("contractAddress".getBytes())); + new Address(HashUtil.h256("contractAddress".getBytes())); private static final Address OWNER_ADDR = - new AionAddress(HashUtil.h256("ownerAddress".getBytes())); + new Address(HashUtil.h256("ownerAddress".getBytes())); @Before public void beforeEach() { diff --git a/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeControllerRingTest.java b/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeControllerRingTest.java index e77e987b3d..562c4867b7 100644 --- a/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeControllerRingTest.java +++ b/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeControllerRingTest.java @@ -3,12 +3,12 @@ import static com.google.common.truth.Truth.assertThat; import static org.aion.precompiled.contracts.ATB.BridgeTestUtils.dummyContext; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; import org.aion.crypto.HashUtil; import org.aion.precompiled.contracts.DummyRepo; -import org.aion.vm.api.interfaces.Address; + import org.junit.Before; import org.junit.Test; @@ -17,9 +17,9 @@ public class BridgeControllerRingTest { private BridgeStorageConnector connector; private BridgeController controller; private static final Address CONTRACT_ADDR = - new AionAddress(HashUtil.h256("contractAddress".getBytes())); + new Address(HashUtil.h256("contractAddress".getBytes())); private static final Address OWNER_ADDR = - new AionAddress(HashUtil.h256("ownerAddress".getBytes())); + new Address(HashUtil.h256("ownerAddress".getBytes())); private static final ECKey members[] = new ECKey[] { diff --git a/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeRingInitializationTest.java b/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeRingInitializationTest.java index 6b014b31ff..cfe5260655 100644 --- a/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeRingInitializationTest.java +++ b/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeRingInitializationTest.java @@ -3,10 +3,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.aion.precompiled.contracts.ATB.BridgeTestUtils.dummyContext; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.crypto.HashUtil; import org.aion.precompiled.contracts.DummyRepo; -import org.aion.vm.api.interfaces.Address; + import org.junit.Before; import org.junit.Test; @@ -15,9 +15,9 @@ public class BridgeRingInitializationTest { private BridgeStorageConnector connector; private BridgeController controller; private static final Address CONTRACT_ADDR = - new AionAddress(HashUtil.h256("contractAddress".getBytes())); + new Address(HashUtil.h256("contractAddress".getBytes())); private static final Address OWNER_ADDR = - new AionAddress(HashUtil.h256("ownerAddress".getBytes())); + new Address(HashUtil.h256("ownerAddress".getBytes())); @Before public void beforeEach() { diff --git a/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeStorageConnectorTest.java b/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeStorageConnectorTest.java index 6a7bb6bc35..1049706d38 100644 --- a/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeStorageConnectorTest.java +++ b/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeStorageConnectorTest.java @@ -2,23 +2,22 @@ import static com.google.common.truth.Truth.assertThat; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteUtil; +import org.aion.types.Address; import org.aion.crypto.HashUtil; import org.aion.precompiled.contracts.DummyRepo; -import org.aion.vm.api.interfaces.Address; + +import org.aion.util.bytes.ByteUtil; import org.junit.Before; import org.junit.Test; public class BridgeStorageConnectorTest { private BridgeStorageConnector connector; - private static final Address contractAddress = AionAddress.ZERO_ADDRESS(); + private static final Address contractAddress = Address.ZERO_ADDRESS(); @Before public void beforeEach() { DummyRepo repo = new DummyRepo(); - this.connector = new BridgeStorageConnector((IRepositoryCache) repo, contractAddress); + this.connector = new BridgeStorageConnector(repo, contractAddress); } // should be null diff --git a/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeTestUtils.java b/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeTestUtils.java index 76905b2105..73a07bc4d6 100644 --- a/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeTestUtils.java +++ b/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeTestUtils.java @@ -1,15 +1,15 @@ package org.aion.precompiled.contracts.ATB; -import org.aion.base.type.AionAddress; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; import org.aion.crypto.AddressSpecs; import org.aion.crypto.HashUtil; import org.aion.fastvm.ExecutionContext; -import org.aion.mcf.vm.types.DataWord; -import org.aion.vm.api.interfaces.Address; + public class BridgeTestUtils { static ExecutionContext dummyContext() { - return context(AionAddress.ZERO_ADDRESS(), AionAddress.ZERO_ADDRESS(), new byte[0]); + return context(Address.ZERO_ADDRESS(), Address.ZERO_ADDRESS(), new byte[0]); } static ExecutionContext context(Address from, Address to, byte[] txData) { @@ -17,20 +17,20 @@ static ExecutionContext context(Address from, Address to, byte[] txData) { final Address address = to; final Address origin = from; final Address caller = origin; - final DataWord nrgPrice = DataWord.ONE; + final DataWordImpl nrgPrice = DataWordImpl.ONE; final long nrgLimit = 21000L; - final DataWord callValue = DataWord.ZERO; + final DataWordImpl callValue = DataWordImpl.ZERO; final byte[] callData = txData; final int callDepth = 1; final int flag = 0; final int kind = 0; final Address blockCoinbase = - new AionAddress( + new Address( AddressSpecs.computeA0Address(HashUtil.h256("coinbase".getBytes()))); long blockNumber = 0; long blockTimestamp = 0; long blockNrgLimit = 0; - DataWord blockDifficulty = DataWord.ZERO; + DataWordImpl blockDifficulty = DataWordImpl.ZERO; return new ExecutionContext( null, diff --git a/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeTransferTest.java b/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeTransferTest.java index f6b32acff9..ab57029616 100644 --- a/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeTransferTest.java +++ b/modPrecompiled/test/org/aion/precompiled/contracts/ATB/BridgeTransferTest.java @@ -6,14 +6,14 @@ import java.math.BigInteger; import java.security.SecureRandom; import java.util.List; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; import org.aion.crypto.HashUtil; import org.aion.fastvm.ExecutionContext; import org.aion.precompiled.PrecompiledUtilities; import org.aion.precompiled.contracts.DummyRepo; -import org.aion.vm.api.interfaces.Address; + import org.aion.vm.api.interfaces.IExecutionLog; import org.junit.Before; import org.junit.Test; @@ -27,9 +27,9 @@ public class BridgeTransferTest { private ExecutionContext context; private static final Address CONTRACT_ADDR = - new AionAddress(HashUtil.h256("contractAddress".getBytes())); + new Address(HashUtil.h256("contractAddress".getBytes())); private static final Address OWNER_ADDR = - new AionAddress(HashUtil.h256("ownerAddress".getBytes())); + new Address(HashUtil.h256("ownerAddress".getBytes())); private static final ECKey members[] = new ECKey[] { @@ -143,7 +143,7 @@ public void testBridgeNotEnoughSignatures() { assertThat(results).isNotNull(); assertThat(results.controllerResult).isEqualTo(ErrCode.INVALID_SIGNATURE_BOUNDS); assertThat(this.repo.getBalance(CONTRACT_ADDR)).isEqualTo(BigInteger.ONE); - assertThat(this.repo.getBalance(new AionAddress(recipient))).isEqualTo(BigInteger.ZERO); + assertThat(this.repo.getBalance(new Address(recipient))).isEqualTo(BigInteger.ZERO); } @Test @@ -181,7 +181,7 @@ public void testLowerBoundSignature() { assertThat(results).isNotNull(); assertThat(results.controllerResult).isEqualTo(ErrCode.NO_ERROR); assertThat(this.repo.getBalance(CONTRACT_ADDR)).isEqualTo(BigInteger.ZERO); - assertThat(this.repo.getBalance(new AionAddress(recipient))).isEqualTo(BigInteger.ONE); + assertThat(this.repo.getBalance(new Address(recipient))).isEqualTo(BigInteger.ONE); } @Test @@ -219,7 +219,7 @@ public void testBeyondUpperBoundSignatures() { assertThat(results).isNotNull(); assertThat(results.controllerResult).isEqualTo(ErrCode.INVALID_SIGNATURE_BOUNDS); assertThat(this.repo.getBalance(CONTRACT_ADDR)).isEqualTo(BigInteger.ONE); - assertThat(this.repo.getBalance(new AionAddress(recipient))).isEqualTo(BigInteger.ZERO); + assertThat(this.repo.getBalance(new Address(recipient))).isEqualTo(BigInteger.ZERO); } @Test @@ -255,7 +255,7 @@ public void testTransferZeroBundle() { assertThat(results).isNotNull(); assertThat(results.controllerResult).isEqualTo(ErrCode.INVALID_TRANSFER); assertThat(this.repo.getBalance(CONTRACT_ADDR)).isEqualTo(BigInteger.ONE); - assertThat(this.repo.getBalance(new AionAddress(recipient))).isEqualTo(BigInteger.ZERO); + assertThat(this.repo.getBalance(new Address(recipient))).isEqualTo(BigInteger.ZERO); } static class ResultHashTuple { @@ -325,7 +325,7 @@ public void testDoubleBundleSend() { // one transfer should have gone through, second shouldn't assertThat(this.repo.getBalance(CONTRACT_ADDR)).isEqualTo(BigInteger.ONE); - assertThat(this.repo.getBalance(new AionAddress(recipient))).isEqualTo(BigInteger.ONE); + assertThat(this.repo.getBalance(new Address(recipient))).isEqualTo(BigInteger.ONE); } // Transfer 511 times, ONE per transfer @@ -354,7 +354,7 @@ public void testBundleMultipleTransferSameRecipient() { assertThat(tuple.results.controllerResult).isEqualTo(ErrCode.NO_ERROR); assertThat(this.repo.getBalance(CONTRACT_ADDR)).isEqualTo(BigInteger.ZERO); - assertThat(this.repo.getBalance(AionAddress.wrap(recipient))) + assertThat(this.repo.getBalance(Address.wrap(recipient))) .isEqualTo(transferTotalBigInteger); // 511 transfer events + 1 distributed event diff --git a/modPrecompiled/test/org/aion/precompiled/contracts/ATB/TokenBridgeContractTest.java b/modPrecompiled/test/org/aion/precompiled/contracts/ATB/TokenBridgeContractTest.java index d2e7ada5a5..474520cc81 100644 --- a/modPrecompiled/test/org/aion/precompiled/contracts/ATB/TokenBridgeContractTest.java +++ b/modPrecompiled/test/org/aion/precompiled/contracts/ATB/TokenBridgeContractTest.java @@ -6,15 +6,14 @@ import java.math.BigInteger; import java.util.Arrays; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.crypto.AddressSpecs; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; import org.aion.crypto.HashUtil; import org.aion.fastvm.ExecutionContext; -import org.aion.mcf.vm.types.DataWord; import org.aion.precompiled.PrecompiledResultCode; import org.aion.precompiled.PrecompiledTransactionResult; import org.aion.precompiled.PrecompiledUtilities; @@ -23,7 +22,8 @@ import org.aion.precompiled.encoding.AddressFVM; import org.aion.precompiled.encoding.ListFVM; import org.aion.precompiled.encoding.Uint128FVM; -import org.aion.vm.api.interfaces.Address; + +import org.aion.util.bytes.ByteUtil; import org.aion.vm.api.interfaces.IExecutionLog; import org.aion.vm.api.interfaces.InternalTransactionInterface; import org.junit.Before; @@ -49,9 +49,9 @@ public class TokenBridgeContractTest { }; private static final Address CONTRACT_ADDR = - new AionAddress(HashUtil.h256("contractAddress".getBytes())); + new Address(HashUtil.h256("contractAddress".getBytes())); private static final Address OWNER_ADDR = - new AionAddress(HashUtil.h256("ownerAddress".getBytes())); + new Address(HashUtil.h256("ownerAddress".getBytes())); private static final long DEFAULT_NRG = 21000L; @@ -120,7 +120,7 @@ public void testGetNewOwnerNotOwnerAddress() { this.contract = new TokenBridgeContract( context( - AionAddress.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), CONTRACT_ADDR, ByteUtil.EMPTY_BYTE_ARRAY), this.repository, @@ -176,7 +176,7 @@ public void testInitializeRingNotOwner() { this.contract = new TokenBridgeContract( context( - AionAddress.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), CONTRACT_ADDR, ByteUtil.EMPTY_BYTE_ARRAY), this.repository, @@ -235,7 +235,7 @@ public void testTransfer() { // need to change the execution context ExecutionContext submitBundleContext = context( - new AionAddress(members[0].getAddress()), + new Address(members[0].getAddress()), CONTRACT_ADDR, ByteUtil.EMPTY_BYTE_ARRAY); this.contract = @@ -333,7 +333,7 @@ public void testTransfer() { assertThat(transferResult.getResultCode()).isEqualTo(PrecompiledResultCode.SUCCESS); for (BridgeTransfer b : transfers) { - assertThat(this.repository.getBalance(new AionAddress(b.getRecipient()))) + assertThat(this.repository.getBalance(new Address(b.getRecipient()))) .isEqualTo(BigInteger.ONE); } assertThat(this.repository.getBalance(CONTRACT_ADDR)).isEqualTo(BigInteger.ZERO); @@ -360,7 +360,7 @@ public void testTransfer() { // verify that the recipient is what we intended (in the order we submitted) assertThat(tx.getDestinationAddress()) - .isEqualTo(new AionAddress(transfers[i].getRecipient())); + .isEqualTo(new Address(transfers[i].getRecipient())); i++; } @@ -429,7 +429,7 @@ public void testNonA0AddressTransfer() { // need to change the execution context ExecutionContext submitBundleContext = context( - new AionAddress(members[0].getAddress()), + new Address(members[0].getAddress()), CONTRACT_ADDR, ByteUtil.EMPTY_BYTE_ARRAY); this.contract = @@ -526,7 +526,7 @@ public void testNonA0AddressTransfer() { assertThat(transferResult.getResultCode()).isEqualTo(PrecompiledResultCode.SUCCESS); for (BridgeTransfer b : transfers) { - assertThat(this.repository.getBalance(new AionAddress(b.getRecipient()))) + assertThat(this.repository.getBalance(new Address(b.getRecipient()))) .isEqualTo(BigInteger.ONE); } assertThat(this.repository.getBalance(CONTRACT_ADDR)).isEqualTo(BigInteger.ZERO); @@ -553,7 +553,7 @@ public void testNonA0AddressTransfer() { // verify that the recipient is what we intended (in the order we submitted) assertThat(tx.getDestinationAddress()) - .isEqualTo(new AionAddress(transfers[i].getRecipient())); + .isEqualTo(new Address(transfers[i].getRecipient())); i++; } @@ -622,7 +622,7 @@ public void testTransferNotRelayer() { // need to change the execution context ExecutionContext submitBundleContext = context( - new AionAddress(members[0].getAddress()), + new Address(members[0].getAddress()), CONTRACT_ADDR, ByteUtil.EMPTY_BYTE_ARRAY); this.contract = @@ -692,7 +692,7 @@ public void testTransferNotRelayer() { // we create a new token bridge contract here because we // need to change the execution context ExecutionContext incorrectRelaySubmitBundleContext = - context(AionAddress.ZERO_ADDRESS(), CONTRACT_ADDR, ByteUtil.EMPTY_BYTE_ARRAY); + context(Address.ZERO_ADDRESS(), CONTRACT_ADDR, ByteUtil.EMPTY_BYTE_ARRAY); this.contract = new TokenBridgeContract( incorrectRelaySubmitBundleContext, @@ -753,7 +753,7 @@ public void testTransfersGreaterThanMaxListSize() { // override defaults ExecutionContext submitBundleContext = context( - new AionAddress(members[0].getAddress()), + new Address(members[0].getAddress()), CONTRACT_ADDR, ByteUtil.EMPTY_BYTE_ARRAY); this.repository.addBalance(CONTRACT_ADDR, BigInteger.valueOf(1024)); @@ -828,7 +828,7 @@ public void testTransfersGreaterThanMaxListSize() { assertThat(transferResult.getResultCode()).isEqualTo(PrecompiledResultCode.FAILURE); for (BridgeTransfer b : transfers) { - assertThat(this.repository.getBalance(new AionAddress(b.getRecipient()))) + assertThat(this.repository.getBalance(new Address(b.getRecipient()))) .isEqualTo(BigInteger.ZERO); } assertThat(this.repository.getBalance(CONTRACT_ADDR)).isEqualTo(BigInteger.valueOf(1024)); @@ -876,7 +876,7 @@ public void testAlreadySubmittedBundle() { // need to change the execution context ExecutionContext submitBundleContext = context( - new AionAddress(members[0].getAddress()), + new Address(members[0].getAddress()), CONTRACT_ADDR, ByteUtil.EMPTY_BYTE_ARRAY); this.contract = @@ -1010,7 +1010,7 @@ public void testTransferRingLocked() { // need to change the execution context ExecutionContext submitBundleContext = context( - new AionAddress(members[0].getAddress()), + new Address(members[0].getAddress()), CONTRACT_ADDR, ByteUtil.EMPTY_BYTE_ARRAY); this.contract = @@ -1106,7 +1106,7 @@ public void testTransferRingLocked() { // check that nothing has been modified from the failed transfer for (BridgeTransfer b : transfers) { - assertThat(this.repository.getBalance(new AionAddress(b.getRecipient()))) + assertThat(this.repository.getBalance(new Address(b.getRecipient()))) .isEqualTo(BigInteger.ZERO); } assertThat(this.repository.getBalance(CONTRACT_ADDR)).isEqualTo(BigInteger.valueOf(10)); @@ -1145,7 +1145,7 @@ public void testTransferInvalidReLayer() { // need to change the execution context ExecutionContext submitBundleContext = context( - new AionAddress(members[0].getAddress()), + new Address(members[0].getAddress()), CONTRACT_ADDR, ByteUtil.EMPTY_BYTE_ARRAY); this.contract = @@ -1241,7 +1241,7 @@ public void testTransferInvalidReLayer() { // check that nothing has been modified from the failed transfer for (BridgeTransfer b : transfers) { - assertThat(this.repository.getBalance(new AionAddress(b.getRecipient()))) + assertThat(this.repository.getBalance(new Address(b.getRecipient()))) .isEqualTo(BigInteger.ZERO); } assertThat(this.repository.getBalance(CONTRACT_ADDR)).isEqualTo(BigInteger.valueOf(10)); @@ -1289,7 +1289,7 @@ public void testTransferLessThanMinimumRequiredValidators() { // need to change the execution context ExecutionContext submitBundleContext = context( - new AionAddress(members[0].getAddress()), + new Address(members[0].getAddress()), CONTRACT_ADDR, ByteUtil.EMPTY_BYTE_ARRAY); this.contract = @@ -1384,7 +1384,7 @@ public void testTransferLessThanMinimumRequiredValidators() { // check that nothing has been modified from the failed transfer for (BridgeTransfer b : transfers) { - assertThat(this.repository.getBalance(new AionAddress(b.getRecipient()))) + assertThat(this.repository.getBalance(new Address(b.getRecipient()))) .isEqualTo(BigInteger.ZERO); } assertThat(this.repository.getBalance(CONTRACT_ADDR)).isEqualTo(BigInteger.valueOf(10)); @@ -1432,7 +1432,7 @@ public void testTransferInsufficientValidatorSignatures() { // need to change the execution context ExecutionContext submitBundleContext = context( - new AionAddress(members[0].getAddress()), + new Address(members[0].getAddress()), CONTRACT_ADDR, ByteUtil.EMPTY_BYTE_ARRAY); this.contract = @@ -1529,7 +1529,7 @@ public void testTransferInsufficientValidatorSignatures() { // check that nothing has been changed from the failed transfer for (BridgeTransfer b : transfers) { - assertThat(this.repository.getBalance(new AionAddress(b.getRecipient()))) + assertThat(this.repository.getBalance(new Address(b.getRecipient()))) .isEqualTo(BigInteger.ZERO); } assertThat(this.repository.getBalance(CONTRACT_ADDR)).isEqualTo(BigInteger.valueOf(10)); @@ -1578,7 +1578,7 @@ public void testTransferOutOfBoundsListMeta() { // need to change the execution context ExecutionContext submitBundleContext = context( - new AionAddress(members[0].getAddress()), + new Address(members[0].getAddress()), CONTRACT_ADDR, ByteUtil.EMPTY_BYTE_ARRAY); this.contract = @@ -1678,7 +1678,7 @@ public void testTransferOutOfBoundsListMeta() { // check that nothing has been changed from the failed transfer for (BridgeTransfer b : transfers) { - assertThat(this.repository.getBalance(new AionAddress(b.getRecipient()))) + assertThat(this.repository.getBalance(new Address(b.getRecipient()))) .isEqualTo(BigInteger.ZERO); } assertThat(this.repository.getBalance(CONTRACT_ADDR)).isEqualTo(BigInteger.valueOf(10)); @@ -1731,10 +1731,10 @@ public void testTransferToSameAddressTwiceInOneBundle() { int i = 0; for (BridgeTransfer b : transfers) { if (i == 2 || i == 3) { - assertThat(this.repository.getBalance(new AionAddress(b.getRecipient()))) + assertThat(this.repository.getBalance(new Address(b.getRecipient()))) .isEqualTo(BigInteger.TWO); } else { - assertThat(this.repository.getBalance(new AionAddress(b.getRecipient()))) + assertThat(this.repository.getBalance(new Address(b.getRecipient()))) .isEqualTo(BigInteger.ONE); } i++; @@ -1763,7 +1763,7 @@ public void testTransferToSameAddressTwiceInOneBundle() { // verify that the recipient is what we intended (in the order we submitted) assertThat(tx.getDestinationAddress()) - .isEqualTo(new AionAddress(transfers[i].getRecipient())); + .isEqualTo(new Address(transfers[i].getRecipient())); i++; } @@ -1835,7 +1835,7 @@ public void testTransferHugeListOffset() { // check that nothing has been changed from the failed transfer for (BridgeTransfer b : transfers) { - assertThat(this.repository.getBalance(new AionAddress(b.getRecipient()))) + assertThat(this.repository.getBalance(new Address(b.getRecipient()))) .isEqualTo(BigInteger.ZERO); } assertThat(this.repository.getBalance(CONTRACT_ADDR)).isEqualTo(BigInteger.valueOf(10)); @@ -1885,7 +1885,7 @@ public void testTransferHugeListLength() { // check that nothing has been changed from the failed transfer for (BridgeTransfer b : transfers) { - assertThat(this.repository.getBalance(new AionAddress(b.getRecipient()))) + assertThat(this.repository.getBalance(new Address(b.getRecipient()))) .isEqualTo(BigInteger.ZERO); } assertThat(this.repository.getBalance(CONTRACT_ADDR)).isEqualTo(BigInteger.valueOf(10)); @@ -2013,7 +2013,7 @@ private ReturnDataFromSetup setupForTest(BridgeTransfer[] transfers, ECKey[] mem // need to change the execution context ExecutionContext submitBundleContext = context( - new AionAddress(members[0].getAddress()), + new Address(members[0].getAddress()), CONTRACT_ADDR, ByteUtil.EMPTY_BYTE_ARRAY); this.contract = @@ -2121,7 +2121,7 @@ public void testRingLocked() { PrecompiledTransactionResult transferResult = this.contract.execute(callPayload, DEFAULT_NRG); assertThat(transferResult.getResultCode()).isEqualTo(PrecompiledResultCode.SUCCESS); - assertThat(transferResult.getReturnData()).isEqualTo(DataWord.ONE.getData()); + assertThat(transferResult.getReturnData()).isEqualTo(DataWordImpl.ONE.getData()); // lock the ring this.connector.setRingLocked(false); @@ -2133,7 +2133,7 @@ public void testRingLocked() { PrecompiledTransactionResult transferResult2 = this.contract.execute(callPayload2, DEFAULT_NRG); assertThat(transferResult2.getResultCode()).isEqualTo(PrecompiledResultCode.SUCCESS); - assertThat(transferResult2.getReturnData()).isEqualTo(DataWord.ZERO.getData()); + assertThat(transferResult2.getReturnData()).isEqualTo(DataWordImpl.ZERO.getData()); } @Test @@ -2167,7 +2167,7 @@ public void testMinThreshold() { this.contract.execute(callPayload, DEFAULT_NRG); assertThat(transferResult.getResultCode()).isEqualTo(PrecompiledResultCode.SUCCESS); assertThat(transferResult.getReturnData()) - .isEqualTo(new DataWord(new BigInteger("3")).getData()); + .isEqualTo(new DataWordImpl(new BigInteger("3")).getData()); // explicitly set the min threshold to 5 this.connector.setMinThresh(5); @@ -2180,7 +2180,7 @@ public void testMinThreshold() { this.contract.execute(callPayload2, DEFAULT_NRG); assertThat(transferResult2.getResultCode()).isEqualTo(PrecompiledResultCode.SUCCESS); assertThat(transferResult2.getReturnData()) - .isEqualTo(new DataWord(new BigInteger("5")).getData()); + .isEqualTo(new DataWordImpl(new BigInteger("5")).getData()); // try setting threshold greater than number of validator members this.connector.setMinThresh(10); @@ -2193,7 +2193,7 @@ public void testMinThreshold() { this.contract.execute(callPayload3, DEFAULT_NRG); assertThat(transferResult3.getResultCode()).isEqualTo(PrecompiledResultCode.SUCCESS); assertThat(transferResult3.getReturnData()) - .isEqualTo(new DataWord(new BigInteger("10")).getData()); + .isEqualTo(new DataWordImpl(new BigInteger("10")).getData()); } @Test @@ -2227,7 +2227,7 @@ public void testMemberCount() { this.contract.execute(callPayload, DEFAULT_NRG); assertThat(transferResult.getResultCode()).isEqualTo(PrecompiledResultCode.SUCCESS); assertThat(transferResult.getReturnData()) - .isEqualTo(new DataWord(new BigInteger("5")).getData()); + .isEqualTo(new DataWordImpl(new BigInteger("5")).getData()); // explicitly set the member count to 10 this.connector.setMemberCount(10); @@ -2240,7 +2240,7 @@ public void testMemberCount() { this.contract.execute(callPayload2, DEFAULT_NRG); assertThat(transferResult2.getResultCode()).isEqualTo(PrecompiledResultCode.SUCCESS); assertThat(transferResult2.getReturnData()) - .isEqualTo(new DataWord(new BigInteger("10")).getData()); + .isEqualTo(new DataWordImpl(new BigInteger("10")).getData()); } @Test @@ -2376,7 +2376,7 @@ public void testAddRingMemberNotOwner() { this.contract = new TokenBridgeContract( context( - AionAddress.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), CONTRACT_ADDR, ByteUtil.EMPTY_BYTE_ARRAY), this.repository, @@ -2528,7 +2528,7 @@ public void testRemoveRingMemberNotOwner() { this.contract = new TokenBridgeContract( context( - AionAddress.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), CONTRACT_ADDR, ByteUtil.EMPTY_BYTE_ARRAY), this.repository, @@ -2583,7 +2583,7 @@ public void testSetReplayer() { assertThat(result.getResultCode()).isEqualTo(PrecompiledResultCode.SUCCESS); // caller not owner - fail - Address address1 = AionAddress.wrap(ECKeyFac.inst().create().getAddress()); + Address address1 = Address.wrap(ECKeyFac.inst().create().getAddress()); this.contract = new TokenBridgeContract( context(address1, CONTRACT_ADDR, ByteUtil.EMPTY_BYTE_ARRAY), @@ -2609,7 +2609,7 @@ public void testFallbackTransaction() { this.contract = new TokenBridgeContract( context( - AionAddress.ZERO_ADDRESS(), + Address.ZERO_ADDRESS(), CONTRACT_ADDR, ByteUtil.EMPTY_BYTE_ARRAY), this.repository, diff --git a/modPrecompiled/test/org/aion/precompiled/contracts/AionAuctionContractTest.java b/modPrecompiled/test/org/aion/precompiled/contracts/AionAuctionContractTest.java index 888d2da35f..7429b754d0 100644 --- a/modPrecompiled/test/org/aion/precompiled/contracts/AionAuctionContractTest.java +++ b/modPrecompiled/test/org/aion/precompiled/contracts/AionAuctionContractTest.java @@ -6,8 +6,8 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; import org.aion.crypto.HashUtil; @@ -18,7 +18,7 @@ import org.aion.mcf.db.IBlockStoreBase; import org.aion.precompiled.PrecompiledResultCode; import org.aion.precompiled.PrecompiledTransactionResult; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.impl.StandaloneBlockchain; import org.aion.zero.impl.types.AionBlock; import org.aion.zero.types.AionTransaction; @@ -33,12 +33,12 @@ public class AionAuctionContractTest { // use this addr for test to trigger test time periods private static final Address AION = - AionAddress.wrap("0xa0eeaeabdbc92953b072afbd21f3e3fd8a4a4f5e6a6e22200db746ab75e9a99a"); + Address.wrap("0xa0eeaeabdbc92953b072afbd21f3e3fd8a4a4f5e6a6e22200db746ab75e9a99a"); private Address domainAddress1 = - AionAddress.wrap("a011111111111111111111111111111101010101010101010101010101010101"); + Address.wrap("a011111111111111111111111111111101010101010101010101010101010101"); private String domainName1 = "bion.aion"; private String domainName2 = "cion.aion.aion"; - private IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> repo; + private RepositoryCache<AccountState, IBlockStoreBase<?, ?>> repo; private AionAuctionContract testAAC; private ECKey defaultKey; @@ -62,21 +62,21 @@ public void setup() { repo = new DummyRepo(); defaultKey = ECKeyFac.inst().create(); testAAC = new AionAuctionContract(repo, AION, blockchain); - repo.createAccount(AionAddress.wrap(defaultKey.getAddress())); - repo.addBalance(AionAddress.wrap(defaultKey.getAddress()), new BigInteger("4000000")); + repo.createAccount(Address.wrap(defaultKey.getAddress())); + repo.addBalance(Address.wrap(defaultKey.getAddress()), new BigInteger("4000000")); k = ECKeyFac.inst().create(); k2 = ECKeyFac.inst().create(); k3 = ECKeyFac.inst().create(); k4 = ECKeyFac.inst().create(); - repo.createAccount(AionAddress.wrap(k.getAddress())); - repo.createAccount(AionAddress.wrap(k2.getAddress())); - repo.createAccount(AionAddress.wrap(k3.getAddress())); - repo.createAccount(AionAddress.wrap(k4.getAddress())); - repo.addBalance(AionAddress.wrap(k.getAddress()), new BigInteger("10000")); - repo.addBalance(AionAddress.wrap(k2.getAddress()), new BigInteger("10000")); - repo.addBalance(AionAddress.wrap(k3.getAddress()), new BigInteger("10000")); - repo.addBalance(AionAddress.wrap(k4.getAddress()), new BigInteger("10000")); + repo.createAccount(Address.wrap(k.getAddress())); + repo.createAccount(Address.wrap(k2.getAddress())); + repo.createAccount(Address.wrap(k3.getAddress())); + repo.createAccount(Address.wrap(k4.getAddress())); + repo.addBalance(Address.wrap(k.getAddress()), new BigInteger("10000")); + repo.addBalance(Address.wrap(k2.getAddress()), new BigInteger("10000")); + repo.addBalance(Address.wrap(k3.getAddress()), new BigInteger("10000")); + repo.addBalance(Address.wrap(k4.getAddress()), new BigInteger("10000")); } @Test() @@ -87,7 +87,7 @@ public void newTest() { byte[] combined = setupInputs( "12312421412.41dsfsdgsdg.aion", - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), amount.toByteArray(), defaultKey); AionAuctionContract aac = new AionAuctionContract(repo, AION, blockchain); @@ -108,36 +108,36 @@ public void testWithANS() { BigInteger amount = new BigInteger("1000"); byte[] combined = - setupInputs(domainName2, AionAddress.wrap(k.getAddress()), amount.toByteArray(), k); + setupInputs(domainName2, Address.wrap(k.getAddress()), amount.toByteArray(), k); AionAuctionContract aac = new AionAuctionContract(repo, AION, blockchain); PrecompiledTransactionResult result = aac.execute(combined, inputEnergy); BigInteger amount4 = new BigInteger("6000"); byte[] combined4 = setupInputs( - domainName2, AionAddress.wrap(k4.getAddress()), amount4.toByteArray(), k4); + domainName2, Address.wrap(k4.getAddress()), amount4.toByteArray(), k4); AionAuctionContract aac4 = new AionAuctionContract(repo, AION, blockchain); aac4.execute(combined4, inputEnergy); BigInteger amount2 = new BigInteger("5000"); byte[] combined2 = setupInputs( - domainName2, AionAddress.wrap(k2.getAddress()), amount2.toByteArray(), k2); + domainName2, Address.wrap(k2.getAddress()), amount2.toByteArray(), k2); AionAuctionContract aac2 = new AionAuctionContract(repo, AION, blockchain); aac2.execute(combined2, inputEnergy); BigInteger amount3 = new BigInteger("2000"); byte[] combined3 = setupInputs( - domainName2, AionAddress.wrap(k3.getAddress()), amount3.toByteArray(), k3); + domainName2, Address.wrap(k3.getAddress()), amount3.toByteArray(), k3); AionAuctionContract aac3 = new AionAuctionContract(repo, AION, blockchain); aac3.execute(combined3, inputEnergy); // check balances after bidding - assertEquals(9000, repo.getBalance(AionAddress.wrap(k.getAddress())).intValue()); - assertEquals(5000, repo.getBalance(AionAddress.wrap(k2.getAddress())).intValue()); - assertEquals(8000, repo.getBalance(AionAddress.wrap(k3.getAddress())).intValue()); - assertEquals(4000, repo.getBalance(AionAddress.wrap(k4.getAddress())).intValue()); // winner + assertEquals(9000, repo.getBalance(Address.wrap(k.getAddress())).intValue()); + assertEquals(5000, repo.getBalance(Address.wrap(k2.getAddress())).intValue()); + assertEquals(8000, repo.getBalance(Address.wrap(k3.getAddress())).intValue()); + assertEquals(4000, repo.getBalance(Address.wrap(k4.getAddress())).intValue()); // winner try { Thread.sleep(3 * 1000L); @@ -146,18 +146,18 @@ public void testWithANS() { } // check balances after auction, balances should be returned - assertEquals(10000, repo.getBalance(AionAddress.wrap(k.getAddress())).intValue()); - assertEquals(10000, repo.getBalance(AionAddress.wrap(k2.getAddress())).intValue()); - assertEquals(10000, repo.getBalance(AionAddress.wrap(k3.getAddress())).intValue()); + assertEquals(10000, repo.getBalance(Address.wrap(k.getAddress())).intValue()); + assertEquals(10000, repo.getBalance(Address.wrap(k2.getAddress())).intValue()); + assertEquals(10000, repo.getBalance(Address.wrap(k3.getAddress())).intValue()); assertEquals( 5000, - repo.getBalance(AionAddress.wrap(k4.getAddress())).intValue()); // deposits 5000 + repo.getBalance(Address.wrap(k4.getAddress())).intValue()); // deposits 5000 AionNameServiceContract ansc2 = new AionNameServiceContract( repo, - AionAddress.wrap(result.getReturnData()), - AionAddress.wrap(k4.getAddress())); + Address.wrap(result.getReturnData()), + Address.wrap(k4.getAddress())); assertEquals(PrecompiledResultCode.SUCCESS, result.getResultCode()); } @@ -166,28 +166,28 @@ public void testCheckBidBalances() { final long inputEnergy = 24000L; BigInteger amount = new BigInteger("1000"); byte[] combined = - setupInputs(domainName1, AionAddress.wrap(k.getAddress()), amount.toByteArray(), k); + setupInputs(domainName1, Address.wrap(k.getAddress()), amount.toByteArray(), k); AionAuctionContract aac = new AionAuctionContract(repo, AION, blockchain); aac.execute(combined, inputEnergy); BigInteger amount2 = new BigInteger("3000"); byte[] combined2 = setupInputs( - domainName1, AionAddress.wrap(k2.getAddress()), amount2.toByteArray(), k2); + domainName1, Address.wrap(k2.getAddress()), amount2.toByteArray(), k2); AionAuctionContract aac2 = new AionAuctionContract(repo, AION, blockchain); aac2.execute(combined2, inputEnergy); BigInteger amount3 = new BigInteger("2000"); byte[] combined3 = setupInputs( - domainName1, AionAddress.wrap(k3.getAddress()), amount3.toByteArray(), k3); + domainName1, Address.wrap(k3.getAddress()), amount3.toByteArray(), k3); AionAuctionContract aac3 = new AionAuctionContract(repo, AION, blockchain); aac3.execute(combined3, inputEnergy); BigInteger amount4 = new BigInteger("5000"); byte[] combined4 = setupInputs( - domainName1, AionAddress.wrap(k4.getAddress()), amount4.toByteArray(), k4); + domainName1, Address.wrap(k4.getAddress()), amount4.toByteArray(), k4); AionAuctionContract aac4 = new AionAuctionContract(repo, AION, blockchain); aac4.execute(combined4, inputEnergy); @@ -198,31 +198,31 @@ public void testCheckBidBalances() { } // check balances after bidding - assertEquals(9000, repo.getBalance(AionAddress.wrap(k.getAddress())).intValue()); - assertEquals(7000, repo.getBalance(AionAddress.wrap(k2.getAddress())).intValue()); - assertEquals(8000, repo.getBalance(AionAddress.wrap(k3.getAddress())).intValue()); - assertEquals(5000, repo.getBalance(AionAddress.wrap(k4.getAddress())).intValue()); + assertEquals(9000, repo.getBalance(Address.wrap(k.getAddress())).intValue()); + assertEquals(7000, repo.getBalance(Address.wrap(k2.getAddress())).intValue()); + assertEquals(8000, repo.getBalance(Address.wrap(k3.getAddress())).intValue()); + assertEquals(5000, repo.getBalance(Address.wrap(k4.getAddress())).intValue()); BigInteger amount6 = new BigInteger("2000"); byte[] combined6 = setupInputs( - domainName2, AionAddress.wrap(k.getAddress()), amount6.toByteArray(), k); + domainName2, Address.wrap(k.getAddress()), amount6.toByteArray(), k); AionAuctionContract aac6 = new AionAuctionContract(repo, AION, blockchain); aac6.execute(combined6, inputEnergy); BigInteger amount7 = new BigInteger("4000"); byte[] combined7 = setupInputs( - domainName2, AionAddress.wrap(k2.getAddress()), amount7.toByteArray(), k2); + domainName2, Address.wrap(k2.getAddress()), amount7.toByteArray(), k2); AionAuctionContract aac7 = new AionAuctionContract(repo, AION, blockchain); aac7.execute(combined7, inputEnergy); // check balances after bidding both domains - assertEquals(7000, repo.getBalance(AionAddress.wrap(k.getAddress())).intValue()); - assertEquals(3000, repo.getBalance(AionAddress.wrap(k2.getAddress())).intValue()); - assertEquals(8000, repo.getBalance(AionAddress.wrap(k3.getAddress())).intValue()); - assertEquals(5000, repo.getBalance(AionAddress.wrap(k4.getAddress())).intValue()); + assertEquals(7000, repo.getBalance(Address.wrap(k.getAddress())).intValue()); + assertEquals(3000, repo.getBalance(Address.wrap(k2.getAddress())).intValue()); + assertEquals(8000, repo.getBalance(Address.wrap(k3.getAddress())).intValue()); + assertEquals(5000, repo.getBalance(Address.wrap(k4.getAddress())).intValue()); try { Thread.sleep(2500L); @@ -231,10 +231,10 @@ public void testCheckBidBalances() { } // check balances after both auctions are complete, winners should have their deposits gone - assertEquals(10000, repo.getBalance(AionAddress.wrap(k.getAddress())).intValue()); - assertEquals(8000, repo.getBalance(AionAddress.wrap(k2.getAddress())).intValue()); - assertEquals(10000, repo.getBalance(AionAddress.wrap(k3.getAddress())).intValue()); - assertEquals(7000, repo.getBalance(AionAddress.wrap(k4.getAddress())).intValue()); + assertEquals(10000, repo.getBalance(Address.wrap(k.getAddress())).intValue()); + assertEquals(8000, repo.getBalance(Address.wrap(k2.getAddress())).intValue()); + assertEquals(10000, repo.getBalance(Address.wrap(k3.getAddress())).intValue()); + assertEquals(7000, repo.getBalance(Address.wrap(k4.getAddress())).intValue()); try { Thread.sleep(2 * 1000L); @@ -244,10 +244,10 @@ public void testCheckBidBalances() { // check balances after both domains become inactive, all accounts should have their // original balance - assertEquals(10000, repo.getBalance(AionAddress.wrap(k.getAddress())).intValue()); - assertEquals(10000, repo.getBalance(AionAddress.wrap(k2.getAddress())).intValue()); - assertEquals(10000, repo.getBalance(AionAddress.wrap(k3.getAddress())).intValue()); - assertEquals(10000, repo.getBalance(AionAddress.wrap(k4.getAddress())).intValue()); + assertEquals(10000, repo.getBalance(Address.wrap(k.getAddress())).intValue()); + assertEquals(10000, repo.getBalance(Address.wrap(k2.getAddress())).intValue()); + assertEquals(10000, repo.getBalance(Address.wrap(k3.getAddress())).intValue()); + assertEquals(10000, repo.getBalance(Address.wrap(k4.getAddress())).intValue()); } @Test() @@ -261,20 +261,20 @@ public void testQuery() { BigInteger amount4 = new BigInteger("4000"); byte[] combined = - setupInputs("aion.aion", AionAddress.wrap(k.getAddress()), amount.toByteArray(), k); + setupInputs("aion.aion", Address.wrap(k.getAddress()), amount.toByteArray(), k); AionAuctionContract aac = new AionAuctionContract(repo, AION, blockchain); aac.execute(combined, DEFAULT_INPUT_NRG); byte[] combined2 = setupInputs( - "aaaa.aion", AionAddress.wrap(k.getAddress()), amount2.toByteArray(), k); + "aaaa.aion", Address.wrap(k.getAddress()), amount2.toByteArray(), k); AionAuctionContract aac2 = new AionAuctionContract(repo, AION, blockchain); aac.execute(combined2, DEFAULT_INPUT_NRG); byte[] combined3 = setupInputs( "bbbb.aaaa.aion", - AionAddress.wrap(k2.getAddress()), + Address.wrap(k2.getAddress()), amount3.toByteArray(), k2); AionAuctionContract aac3 = new AionAuctionContract(repo, AION, blockchain); @@ -282,7 +282,7 @@ public void testQuery() { byte[] combined8 = setupInputs( - "aion.aion", AionAddress.wrap(k.getAddress()), amount2.toByteArray(), k); + "aion.aion", Address.wrap(k.getAddress()), amount2.toByteArray(), k); AionAuctionContract aac8 = new AionAuctionContract(repo, AION, blockchain); aac.execute(combined8, DEFAULT_INPUT_NRG); @@ -291,22 +291,22 @@ public void testQuery() { // displayAuctionDomainLRU Tests, show with debug aac.execute( - setupInputs("111.aion", AionAddress.wrap(k.getAddress()), amount.toByteArray(), k), + setupInputs("111.aion", Address.wrap(k.getAddress()), amount.toByteArray(), k), DEFAULT_INPUT_NRG); aac.execute( - setupInputs("222.aion", AionAddress.wrap(k.getAddress()), amount.toByteArray(), k), + setupInputs("222.aion", Address.wrap(k.getAddress()), amount.toByteArray(), k), DEFAULT_INPUT_NRG); aac.execute( - setupInputs("333.aion", AionAddress.wrap(k.getAddress()), amount.toByteArray(), k), + setupInputs("333.aion", Address.wrap(k.getAddress()), amount.toByteArray(), k), DEFAULT_INPUT_NRG); aac.execute( - setupInputs("444.aion", AionAddress.wrap(k.getAddress()), amount.toByteArray(), k), + setupInputs("444.aion", Address.wrap(k.getAddress()), amount.toByteArray(), k), DEFAULT_INPUT_NRG); aac.execute( - setupInputs("555.aion", AionAddress.wrap(k.getAddress()), amount.toByteArray(), k), + setupInputs("555.aion", Address.wrap(k.getAddress()), amount.toByteArray(), k), DEFAULT_INPUT_NRG); aac.execute( - setupInputs("666.aion", AionAddress.wrap(k.getAddress()), amount.toByteArray(), k), + setupInputs("666.aion", Address.wrap(k.getAddress()), amount.toByteArray(), k), DEFAULT_INPUT_NRG); aac.displayAuctionDomainLRU("111.aion"); // 1 aac.displayAuctionDomainLRU("222.aion"); // 1 2 @@ -334,7 +334,7 @@ public void testQuery() { byte[] combined4 = setupInputs( "cccc.aaaa.aion", - AionAddress.wrap(k.getAddress()), + Address.wrap(k.getAddress()), amount3.toByteArray(), k); AionAuctionContract aac4 = new AionAuctionContract(repo, AION, blockchain); @@ -343,7 +343,7 @@ public void testQuery() { byte[] combined6 = setupInputs( "cccc.aaaa.aion", - AionAddress.wrap(k2.getAddress()), + Address.wrap(k2.getAddress()), amount.toByteArray(), k2); AionAuctionContract aac6 = new AionAuctionContract(repo, AION, blockchain); @@ -352,7 +352,7 @@ public void testQuery() { byte[] combined7 = setupInputs( "cccc.aaaa.aion", - AionAddress.wrap(k2.getAddress()), + Address.wrap(k2.getAddress()), amount4.toByteArray(), k2); AionAuctionContract aac7 = new AionAuctionContract(repo, AION, blockchain); @@ -384,7 +384,7 @@ public void testActiveDomainTimeExtensionRequestPass() { BigInteger amount = new BigInteger("1000"); byte[] combined = - setupInputs(domainName1, AionAddress.wrap(k.getAddress()), amount.toByteArray(), k); + setupInputs(domainName1, Address.wrap(k.getAddress()), amount.toByteArray(), k); AionAuctionContract aac = new AionAuctionContract(repo, AION, blockchain); aac.execute(combined, inputEnergy); @@ -395,11 +395,11 @@ public void testActiveDomainTimeExtensionRequestPass() { } // try to extend - should work - byte[] combined2 = setupForExtension(domainName1, AionAddress.wrap(k.getAddress())); + byte[] combined2 = setupForExtension(domainName1, Address.wrap(k.getAddress())); PrecompiledTransactionResult res = aac.execute(combined2, inputEnergy); // try to extend 2nd time in a row - should be denied - byte[] combined3 = setupForExtension(domainName1, AionAddress.wrap(k.getAddress())); + byte[] combined3 = setupForExtension(domainName1, Address.wrap(k.getAddress())); PrecompiledTransactionResult res2 = aac.execute(combined3, inputEnergy); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); @@ -421,7 +421,7 @@ public void testActiveDomainTimeExtensionRequestFailure() { BigInteger amount = new BigInteger("1000"); byte[] combined = - setupInputs(domainName1, AionAddress.wrap(k.getAddress()), amount.toByteArray(), k); + setupInputs(domainName1, Address.wrap(k.getAddress()), amount.toByteArray(), k); AionAuctionContract aac = new AionAuctionContract(repo, AION, blockchain); aac.execute(combined, inputEnergy); @@ -432,7 +432,7 @@ public void testActiveDomainTimeExtensionRequestFailure() { } // try to extend - should not work since owner is incorrect - byte[] combined2 = setupForExtension(domainName1, AionAddress.wrap(k2.getAddress())); + byte[] combined2 = setupForExtension(domainName1, Address.wrap(k2.getAddress())); PrecompiledTransactionResult res = aac.execute(combined2, inputEnergy); assertEquals(PrecompiledResultCode.FAILURE, res.getResultCode()); @@ -445,7 +445,7 @@ public void testHasActiveParentDomain() { byte[] combined = setupInputs( - "parent.aion", AionAddress.wrap(k.getAddress()), amount.toByteArray(), k); + "parent.aion", Address.wrap(k.getAddress()), amount.toByteArray(), k); AionAuctionContract aac = new AionAuctionContract(repo, AION, blockchain); aac.execute(combined, inputEnergy); @@ -459,7 +459,7 @@ public void testHasActiveParentDomain() { byte[] combined2 = setupInputs( "child.parent.aion", - AionAddress.wrap(k.getAddress()), + Address.wrap(k.getAddress()), amount.toByteArray(), k); AionAuctionContract aac2 = new AionAuctionContract(repo, AION, blockchain); @@ -472,7 +472,7 @@ public void testHasActiveParentDomain() { public void testUnregisteredDomain() { ECKey k = ECKeyFac.inst().create(); AionNameServiceContract ansc = - new AionNameServiceContract(repo, domainAddress1, AionAddress.wrap(k.getAddress())); + new AionNameServiceContract(repo, domainAddress1, Address.wrap(k.getAddress())); } @Test() @@ -480,7 +480,7 @@ public void testInvalidDomainNames() { byte[] combined = setupInputs( "aa.aion", - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), defaultBidAmount.toByteArray(), defaultKey); AionAuctionContract aac = new AionAuctionContract(repo, AION, blockchain); @@ -490,7 +490,7 @@ public void testInvalidDomainNames() { byte[] combined2 = setupInputs( "#$%aion.aion", - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), defaultBidAmount.toByteArray(), defaultKey); AionAuctionContract aac2 = new AionAuctionContract(repo, AION, blockchain); @@ -500,7 +500,7 @@ public void testInvalidDomainNames() { byte[] combined3 = setupInputs( "withoutdotaion", - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), defaultBidAmount.toByteArray(), defaultKey); AionAuctionContract aac3 = new AionAuctionContract(repo, AION, blockchain); @@ -510,7 +510,7 @@ public void testInvalidDomainNames() { byte[] combined4 = setupInputs( "ai.ai.ai.ai.aion", - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), defaultBidAmount.toByteArray(), defaultKey); AionAuctionContract aac4 = new AionAuctionContract(repo, AION, blockchain); @@ -520,7 +520,7 @@ public void testInvalidDomainNames() { byte[] combined5 = setupInputs( "network.aion", - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), defaultBidAmount.toByteArray(), defaultKey); AionAuctionContract aac5 = new AionAuctionContract(repo, AION, blockchain); @@ -534,7 +534,7 @@ public void testBidderAddressDoesNotExist() { byte[] combined = setupInputs( "bion.bion.aion", - AionAddress.wrap(notExistKey.getAddress()), + Address.wrap(notExistKey.getAddress()), defaultBidAmount.toByteArray(), notExistKey); AionAuctionContract aac = new AionAuctionContract(repo, AION, blockchain); @@ -546,13 +546,13 @@ public void testBidderAddressDoesNotExist() { @Test public void testInsufficientBalance() { ECKey poorKey = ECKeyFac.inst().create(); - repo.createAccount(AionAddress.wrap(poorKey.getAddress())); - repo.addBalance(AionAddress.wrap(poorKey.getAddress()), new BigInteger("100")); + repo.createAccount(Address.wrap(poorKey.getAddress())); + repo.addBalance(Address.wrap(poorKey.getAddress()), new BigInteger("100")); byte[] combined3 = setupInputs( domainName1, - AionAddress.wrap(poorKey.getAddress()), + Address.wrap(poorKey.getAddress()), defaultBidAmount.toByteArray(), poorKey); PrecompiledTransactionResult result = testAAC.execute(combined3, DEFAULT_INPUT_NRG); @@ -565,13 +565,13 @@ public void testIncorrectInputLength() { byte[] input = setupInputs( domainName1, - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), defaultBidAmount.toByteArray(), defaultKey); byte[] wrongInput = new byte[130]; byte[] wrongInput2 = new byte[131]; byte[] wrongInput3 = - setupForExtension(domainName1, AionAddress.wrap(defaultKey.getAddress())); + setupForExtension(domainName1, Address.wrap(defaultKey.getAddress())); byte[] wrongInput5 = new byte[wrongInput3.length]; byte[] wrongInput4 = new byte[input.length - 2]; @@ -595,7 +595,7 @@ public void testIncorrectSignature() { byte[] input = setupInputs( domainName1, - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), defaultBidAmount.toByteArray(), defaultKey); // modify the signature in the 65th byte (arbitrarily) @@ -614,7 +614,7 @@ public void testIncorrectPublicKey() { byte[] input = setupInputs( domainName1, - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), defaultBidAmount.toByteArray(), anotherKey); PrecompiledTransactionResult result = testAAC.execute(input, DEFAULT_INPUT_NRG); @@ -629,7 +629,7 @@ public void testInsufficientEnergy() { byte[] input = setupInputs( domainName1, - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), defaultBidAmount.toByteArray(), defaultKey); PrecompiledTransactionResult result = testAAC.execute(input, 18000); @@ -645,7 +645,7 @@ public void testNegativeBidValue() { byte[] input = setupInputs( domainName1, - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), negativeBidAmount.toByteArray(), defaultKey); PrecompiledTransactionResult result = testAAC.execute(input, DEFAULT_INPUT_NRG); @@ -660,7 +660,7 @@ public void testRequestedDomainIsAlreadyActive() { byte[] input = setupInputs( domainName1, - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), defaultBidAmount.toByteArray(), defaultKey); testAAC.execute(input, DEFAULT_INPUT_NRG); @@ -679,7 +679,7 @@ public void testRequestedDomainIsAlreadyActive() { byte[] input2 = setupInputs( domainName1, - AionAddress.wrap(k2.getAddress()), + Address.wrap(k2.getAddress()), bidAmount2.toByteArray(), k2); PrecompiledTransactionResult result2 = aac2.execute(input2, DEFAULT_INPUT_NRG); @@ -695,7 +695,7 @@ public void testOverwritingBidsWithSmallerValue() { byte[] input = setupInputs( domainName1, - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), defaultBidAmount.toByteArray(), defaultKey); PrecompiledTransactionResult result = testAAC.execute(input, DEFAULT_INPUT_NRG); @@ -704,7 +704,7 @@ public void testOverwritingBidsWithSmallerValue() { byte[] input2 = setupInputs( domainName1, - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), newBidAmount.toByteArray(), defaultKey); PrecompiledTransactionResult result2 = testAAC.execute(input2, DEFAULT_INPUT_NRG); @@ -713,7 +713,7 @@ public void testOverwritingBidsWithSmallerValue() { byte[] input3 = setupInputs( domainName1, - AionAddress.wrap(k2.getAddress()), + Address.wrap(k2.getAddress()), anotherBid.toByteArray(), k2); PrecompiledTransactionResult result3 = testAAC.execute(input3, DEFAULT_INPUT_NRG); @@ -731,7 +731,7 @@ public void testOverwritingBidsWithLargerValue() { byte[] input3 = setupInputs( domainName1, - AionAddress.wrap(k2.getAddress()), + Address.wrap(k2.getAddress()), anotherBid.toByteArray(), k2); PrecompiledTransactionResult result3 = testAAC.execute(input3, DEFAULT_INPUT_NRG); @@ -739,7 +739,7 @@ public void testOverwritingBidsWithLargerValue() { byte[] input = setupInputs( domainName1, - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), defaultBidAmount.toByteArray(), defaultKey); PrecompiledTransactionResult result = testAAC.execute(input, DEFAULT_INPUT_NRG); @@ -748,7 +748,7 @@ public void testOverwritingBidsWithLargerValue() { byte[] input2 = setupInputs( domainName1, - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), newBidAmount.toByteArray(), defaultKey); PrecompiledTransactionResult result2 = testAAC.execute(input2, DEFAULT_INPUT_NRG); @@ -765,7 +765,7 @@ public void testRequestInactiveDomain() { byte[] input = setupInputs( domainName1, - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), defaultBidAmount.toByteArray(), defaultKey); testAAC.execute(input, DEFAULT_INPUT_NRG); @@ -775,7 +775,7 @@ public void testRequestInactiveDomain() { byte[] input3 = setupInputs( domainName1, - AionAddress.wrap(k3.getAddress()), + Address.wrap(k3.getAddress()), anotherAmount.toByteArray(), k3); aac3.execute(input3, DEFAULT_INPUT_NRG); @@ -791,7 +791,7 @@ public void testRequestInactiveDomain() { byte[] input2 = setupInputs( domainName1, - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), defaultBidAmount.toByteArray(), defaultKey); PrecompiledTransactionResult result2 = testAAC.execute(input2, DEFAULT_INPUT_NRG); @@ -877,12 +877,12 @@ private static AionBlock createBundleAndCheck( StandaloneBlockchain bc, ECKey key, AionBlock parentBlock) { byte[] ZERO_BYTE = new byte[0]; - BigInteger accountNonce = bc.getRepository().getNonce(new AionAddress(key.getAddress())); + BigInteger accountNonce = bc.getRepository().getNonce(new Address(key.getAddress())); List<AionTransaction> transactions = new ArrayList<>(); // create 100 transactions per bundle for (int i = 0; i < 100; i++) { - Address destAddr = new AionAddress(HashUtil.h256(accountNonce.toByteArray())); + Address destAddr = new Address(HashUtil.h256(accountNonce.toByteArray())); AionTransaction sendTransaction = new AionTransaction( accountNonce.toByteArray(), diff --git a/modPrecompiled/test/org/aion/precompiled/contracts/AionNameServiceContractTest.java b/modPrecompiled/test/org/aion/precompiled/contracts/AionNameServiceContractTest.java index 6ac6f0467c..26f192ff07 100644 --- a/modPrecompiled/test/org/aion/precompiled/contracts/AionNameServiceContractTest.java +++ b/modPrecompiled/test/org/aion/precompiled/contracts/AionNameServiceContractTest.java @@ -10,8 +10,9 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; import org.aion.crypto.HashUtil; @@ -21,10 +22,9 @@ import org.aion.mcf.core.IBlockchain; import org.aion.mcf.core.ImportResult; import org.aion.mcf.db.IBlockStoreBase; -import org.aion.mcf.vm.types.DataWord; import org.aion.precompiled.PrecompiledResultCode; import org.aion.precompiled.PrecompiledTransactionResult; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.impl.StandaloneBlockchain; import org.aion.zero.impl.types.AionBlock; import org.aion.zero.types.AionTransaction; @@ -50,42 +50,42 @@ public class AionNameServiceContractTest { private String notSubdomain = "aion.bion"; // not a subdomain of domainName1 private static final Address AION = - AionAddress.wrap("0xa0eeaeabdbc92953b072afbd21f3e3fd8a4a4f5e6a6e22200db746ab75e9a99a"); + Address.wrap("0xa0eeaeabdbc92953b072afbd21f3e3fd8a4a4f5e6a6e22200db746ab75e9a99a"); private Address emptyAddress = - AionAddress.wrap("0000000000000000000000000000000000000000000000000000000000000000"); + Address.wrap("0000000000000000000000000000000000000000000000000000000000000000"); private Address domainAddress1 = - AionAddress.wrap("a011111111111111111111111111111101010101010101010101010101010101"); + Address.wrap("a011111111111111111111111111111101010101010101010101010101010101"); private Address domainAddress2 = - AionAddress.wrap("a022222222222222222222222222222202020202020202020202020202020202"); + Address.wrap("a022222222222222222222222222222202020202020202020202020202020202"); private Address domainAddress3 = - AionAddress.wrap("a033333333333333333333333333333303030303030303030303030303030303"); + Address.wrap("a033333333333333333333333333333303030303030303030303030303030303"); private Address domainAddress4 = - AionAddress.wrap("a044444444444444444444444444444404040404040404040404040404040404"); + Address.wrap("a044444444444444444444444444444404040404040404040404040404040404"); private Address domainAddress5 = - AionAddress.wrap("a055555555555555555555555555555505050505050050505050505050505050"); + Address.wrap("a055555555555555555555555555555505050505050050505050505050505050"); private Address domainAddress6 = - AionAddress.wrap("a066666666666666666666666666666606060606060606060606060606060060"); + Address.wrap("a066666666666666666666666666666606060606060606060606060606060060"); private Address invalidDomainAddress = - AionAddress.wrap("b066666666666666666666666666666606060606060606060606060606060606"); + Address.wrap("b066666666666666666666666666666606060606060606060606060606060606"); private Address newAddress1 = - AionAddress.wrap("1000000000000000000000000000000000000000000000000000000000000001"); + Address.wrap("1000000000000000000000000000000000000000000000000000000000000001"); private Address newAddress2 = - AionAddress.wrap("0100000000000000000000000000000000000000000000000000000000000010"); + Address.wrap("0100000000000000000000000000000000000000000000000000000000000010"); private Address newAddress3 = - AionAddress.wrap("0010000000000000000000000000000000000000000000000000000000000100"); + Address.wrap("0010000000000000000000000000000000000000000000000000000000000100"); private Address newAddress4 = - AionAddress.wrap("0001000000000000000000000000000000000000000000000000000000001000"); + Address.wrap("0001000000000000000000000000000000000000000000000000000000001000"); private Address newAddress5 = - AionAddress.wrap("0000100000000000000000000000000000000000000000000000000000010000"); + Address.wrap("0000100000000000000000000000000000000000000000000000000000010000"); private Address newAddress6 = - AionAddress.wrap("0000010000000000000000000000000000000000000000000000000000100000"); + Address.wrap("0000010000000000000000000000000000000000000000000000000000100000"); private Address newAddress7 = - AionAddress.wrap("0000001000000000000000000000000000000000000000000000000001000000"); + Address.wrap("0000001000000000000000000000000000000000000000000000000001000000"); private Address newAddress8 = - AionAddress.wrap("0000000100000000000000000000000000000000000000000000000010000000"); + Address.wrap("0000000100000000000000000000000000000000000000000000000010000000"); - private IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> repo; + private RepositoryCache<AccountState, IBlockStoreBase<?, ?>> repo; private ECKey defaultKey; private ECKey defaultKey2; private ECKey k; @@ -109,31 +109,31 @@ public void setup() { repo = populateRepo(); defaultKey = ECKeyFac.inst().create(); defaultKey2 = ECKeyFac.inst().create(); - repo.createAccount(AionAddress.wrap(defaultKey.getAddress())); - repo.createAccount(AionAddress.wrap(defaultKey2.getAddress())); - repo.addBalance(AionAddress.wrap(defaultKey.getAddress()), new BigInteger("10000")); - repo.addBalance(AionAddress.wrap(defaultKey2.getAddress()), new BigInteger("10000")); + repo.createAccount(Address.wrap(defaultKey.getAddress())); + repo.createAccount(Address.wrap(defaultKey2.getAddress())); + repo.addBalance(Address.wrap(defaultKey.getAddress()), new BigInteger("10000")); + repo.addBalance(Address.wrap(defaultKey2.getAddress()), new BigInteger("10000")); k = ECKeyFac.inst().create(); k2 = ECKeyFac.inst().create(); k3 = ECKeyFac.inst().create(); k4 = ECKeyFac.inst().create(); k5 = ECKeyFac.inst().create(); - repo.createAccount(AionAddress.wrap(k.getAddress())); - repo.createAccount(AionAddress.wrap(k2.getAddress())); - repo.createAccount(AionAddress.wrap(k3.getAddress())); - repo.createAccount(AionAddress.wrap(k4.getAddress())); - repo.createAccount(AionAddress.wrap(k5.getAddress())); - repo.addBalance(AionAddress.wrap(k.getAddress()), new BigInteger("10000")); - repo.addBalance(AionAddress.wrap(k2.getAddress()), new BigInteger("10000")); - repo.addBalance(AionAddress.wrap(k3.getAddress()), new BigInteger("10000")); - repo.addBalance(AionAddress.wrap(k4.getAddress()), new BigInteger("10000")); + repo.createAccount(Address.wrap(k.getAddress())); + repo.createAccount(Address.wrap(k2.getAddress())); + repo.createAccount(Address.wrap(k3.getAddress())); + repo.createAccount(Address.wrap(k4.getAddress())); + repo.createAccount(Address.wrap(k5.getAddress())); + repo.addBalance(Address.wrap(k.getAddress()), new BigInteger("10000")); + repo.addBalance(Address.wrap(k2.getAddress()), new BigInteger("10000")); + repo.addBalance(Address.wrap(k3.getAddress()), new BigInteger("10000")); + repo.addBalance(Address.wrap(k4.getAddress()), new BigInteger("10000")); BigInteger amount = new BigInteger("1000"); byte[] combined = setupInputs( domainName2, - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), amount.toByteArray(), defaultKey); AionAuctionContract aac = new AionAuctionContract(repo, AION, blockchain); @@ -149,7 +149,7 @@ public void setup() { byte[] combined2 = setupInputs( domainName3, - AionAddress.wrap(defaultKey2.getAddress()), + Address.wrap(defaultKey2.getAddress()), amount2.toByteArray(), defaultKey2); AionAuctionContract aac2 = new AionAuctionContract(repo, AION, blockchain); @@ -162,8 +162,8 @@ public void setup() { e.printStackTrace(); } - defaultAddress = AionAddress.wrap(result.getReturnData()); - defaultAddress2 = AionAddress.wrap(result2.getReturnData()); + defaultAddress = Address.wrap(result.getReturnData()); + defaultAddress2 = Address.wrap(result2.getReturnData()); } @After @@ -179,14 +179,14 @@ public void testInvalidValues() { AionNameServiceContract ansc = new AionNameServiceContract( - repo, defaultAddress, AionAddress.wrap(defaultKey.getAddress())); + repo, defaultAddress, Address.wrap(defaultKey.getAddress())); AionNameServiceContract ansc2 = new AionNameServiceContract( - repo, defaultAddress2, AionAddress.wrap(defaultKey2.getAddress())); + repo, defaultAddress2, Address.wrap(defaultKey2.getAddress())); byte[] combined = setupInputs( - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), newAddress1, (byte) 0x0, (byte) 0x4, @@ -196,7 +196,7 @@ public void testInvalidValues() { "aion.aion"); byte[] combined2 = setupInputs( - AionAddress.wrap(defaultKey2.getAddress()), + Address.wrap(defaultKey2.getAddress()), newAddress2, (byte) 0x0, (byte) 0x4, @@ -226,7 +226,7 @@ public void testConstructorInvalidDomainAddress() { // domain addresses must have aion prefix: 0xa0(66char) or a0(64char) AionNameServiceContract ansc = new AionNameServiceContract( - repo, invalidDomainAddress, AionAddress.wrap(k.getAddress())); + repo, invalidDomainAddress, Address.wrap(k.getAddress())); assertNull(ansc); } @@ -250,7 +250,7 @@ public void testConflictOwnerAddress() { // check that the given owner address is the same as the owner address in the repository. AionNameServiceContract ansc = - new AionNameServiceContract(repo, domainAddress2, AionAddress.wrap(k.getAddress())); + new AionNameServiceContract(repo, domainAddress2, Address.wrap(k.getAddress())); assertNull(ansc); } @@ -258,7 +258,7 @@ public void testConflictOwnerAddress() { public void testUnavailableDomain() { AionNameServiceContract ansc = new AionNameServiceContract( - repo, domainAddress6, AionAddress.wrap(defaultKey.getAddress())); + repo, domainAddress6, Address.wrap(defaultKey.getAddress())); assertNull(ansc); } @@ -266,10 +266,10 @@ public void testUnavailableDomain() { public void testGetNameAndAddress() { AionNameServiceContract ansc = new AionNameServiceContract( - repo, defaultAddress, AionAddress.wrap(defaultKey.getAddress())); + repo, defaultAddress, Address.wrap(defaultKey.getAddress())); AionNameServiceContract ansc2 = new AionNameServiceContract( - repo, defaultAddress2, AionAddress.wrap(defaultKey2.getAddress())); + repo, defaultAddress2, Address.wrap(defaultKey2.getAddress())); assertEquals(domainName2, ansc.getRegisteredDomainName(defaultAddress)); assertEquals(domainName3, ansc.getRegisteredDomainName(defaultAddress2)); @@ -291,11 +291,11 @@ public void testTransferSubdomainOwnership() { AionNameServiceContract ansc = new AionNameServiceContract( - repo, defaultAddress, AionAddress.wrap(defaultKey.getAddress())); + repo, defaultAddress, Address.wrap(defaultKey.getAddress())); byte[] combined = setupInputs( - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), newAddress5, (byte) 0x0, (byte) 0x4, @@ -321,11 +321,11 @@ public void testSetResolver() { // create ANS contract AionNameServiceContract ansc = new AionNameServiceContract( - repo, defaultAddress, AionAddress.wrap(defaultKey.getAddress())); + repo, defaultAddress, Address.wrap(defaultKey.getAddress())); byte[] combined = setupInputs( - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), newAddress1, (byte) 0x0, (byte) 0x1, @@ -351,11 +351,11 @@ public void testSetTTL() { // create ANS contract AionNameServiceContract ansc = new AionNameServiceContract( - repo, defaultAddress, AionAddress.wrap(defaultKey.getAddress())); + repo, defaultAddress, Address.wrap(defaultKey.getAddress())); byte[] combined = setupInputs( - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), newAddress1, (byte) 0x0, (byte) 0x2, @@ -381,7 +381,7 @@ public void testNotASubdomain() { AionNameServiceContract ansc = new AionNameServiceContract( - repo, defaultAddress, AionAddress.wrap(defaultKey.getAddress())); + repo, defaultAddress, Address.wrap(defaultKey.getAddress())); byte[] combined = setupInputs( @@ -408,11 +408,11 @@ public void incorrectInputLength() { // create ans contracts AionNameServiceContract ansc = new AionNameServiceContract( - repo, defaultAddress, AionAddress.wrap(defaultKey.getAddress())); + repo, defaultAddress, Address.wrap(defaultKey.getAddress())); byte[] combined = setupInputs( - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), newAddress1, (byte) 0x0, (byte) 0x1, @@ -440,11 +440,11 @@ public void testIncorrectSignature() { // create ANS contract AionNameServiceContract ansc = new AionNameServiceContract( - repo, defaultAddress, AionAddress.wrap(defaultKey.getAddress())); + repo, defaultAddress, Address.wrap(defaultKey.getAddress())); byte[] combined = setupInputs( - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), newAddress1, (byte) 0x0, (byte) 0x1, @@ -476,11 +476,11 @@ public void testUnsupportedOperation() { // create ANS contract AionNameServiceContract ansc = new AionNameServiceContract( - repo, defaultAddress, AionAddress.wrap(defaultKey.getAddress())); + repo, defaultAddress, Address.wrap(defaultKey.getAddress())); byte[] combined = setupInputs( - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), newAddress1, (byte) 0x0, (byte) 0x6, @@ -506,16 +506,16 @@ public void testIncorrectPublicKey() { // ECKey k = ECKeyFac.inst().create(); ECKey notk = ECKeyFac.inst().create(); - repo.createAccount(AionAddress.wrap(notk.getAddress())); + repo.createAccount(Address.wrap(notk.getAddress())); // create ANS contract AionNameServiceContract ansc = new AionNameServiceContract( - repo, defaultAddress, AionAddress.wrap(defaultKey.getAddress())); + repo, defaultAddress, Address.wrap(defaultKey.getAddress())); byte[] combined = setupInputs( - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), newAddress1, (byte) 0x0, (byte) 0x1, @@ -543,18 +543,18 @@ public void testTransferOwnership() { // create ANS contract AionNameServiceContract ansc = new AionNameServiceContract( - repo, defaultAddress, AionAddress.wrap(defaultKey.getAddress())); + repo, defaultAddress, Address.wrap(defaultKey.getAddress())); byte[] combined = setupInputs( - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), newAddress1, (byte) 0x0, (byte) 0x3, defaultKey); byte[] combined2 = setupInputs( - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), newAddress2, (byte) 0x0, (byte) 0x3, @@ -587,32 +587,32 @@ public void testInsufficientEnergy() { // create ANS contract AionNameServiceContract ansc = new AionNameServiceContract( - repo, defaultAddress, AionAddress.wrap(defaultKey.getAddress())); + repo, defaultAddress, Address.wrap(defaultKey.getAddress())); byte[] combined = setupInputs( - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), newAddress1, (byte) 0x0, (byte) 0x1, defaultKey); byte[] combined2 = setupInputs( - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), newAddress2, (byte) 0x0, (byte) 0x2, defaultKey); byte[] combined3 = setupInputs( - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), newAddress3, (byte) 0x0, (byte) 0x3, defaultKey); byte[] combined4 = setupInputs( - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), newAddress3, (byte) 0x0, (byte) 0x4, @@ -677,18 +677,18 @@ public void testSubdomainDoesNotExist() { // create ANS contract AionNameServiceContract ansc = new AionNameServiceContract( - repo, defaultAddress, AionAddress.wrap(defaultKey.getAddress())); + repo, defaultAddress, Address.wrap(defaultKey.getAddress())); byte[] combined = setupInputs( - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), newAddress1, (byte) 0x0, (byte) 0x3, defaultKey); byte[] combined2 = setupInputs( - AionAddress.wrap(defaultKey.getAddress()), + Address.wrap(defaultKey.getAddress()), newAddress2, (byte) 0x0, (byte) 0x4, @@ -726,23 +726,23 @@ public void testANSQuery() { byte[] combined = setupInputs( "cion.bion.aion", - AionAddress.wrap(k.getAddress()), + Address.wrap(k.getAddress()), amount.toByteArray(), k); AionAuctionContract aac = new AionAuctionContract(repo, AION, blockchain); PrecompiledTransactionResult result = aac.execute(combined, DEFAULT_INPUT_NRG); - Address addr = AionAddress.wrap(result.getReturnData()); + Address addr = Address.wrap(result.getReturnData()); byte[] combined2 = setupInputs( - "aaaa.aion", AionAddress.wrap(k.getAddress()), amount2.toByteArray(), k); + "aaaa.aion", Address.wrap(k.getAddress()), amount2.toByteArray(), k); AionAuctionContract aac2 = new AionAuctionContract(repo, AION, blockchain); aac2.execute(combined2, DEFAULT_INPUT_NRG); byte[] combined3 = setupInputs( "bbbb.aaaa.aion", - AionAddress.wrap(k2.getAddress()), + Address.wrap(k2.getAddress()), amount3.toByteArray(), k2); AionAuctionContract aac3 = new AionAuctionContract(repo, AION, blockchain); @@ -756,7 +756,7 @@ public void testANSQuery() { // register domain for name service AionNameServiceContract ansc = - new AionNameServiceContract(repo, addr, AionAddress.wrap(k.getAddress())); + new AionNameServiceContract(repo, addr, Address.wrap(k.getAddress())); System.out.print(""); @@ -774,7 +774,7 @@ public void testANSQuery() { byte[] combined4 = setupInputs( "cccc.aaaa.aion", - AionAddress.wrap(k.getAddress()), + Address.wrap(k.getAddress()), amount3.toByteArray(), k); AionAuctionContract aac4 = new AionAuctionContract(repo, AION, blockchain); @@ -783,7 +783,7 @@ public void testANSQuery() { byte[] combined5 = setupInputs( "cccc.aaaa.aion", - AionAddress.wrap(k2.getAddress()), + Address.wrap(k2.getAddress()), amount2.toByteArray(), k2); AionAuctionContract aac5 = new AionAuctionContract(repo, AION, blockchain); @@ -934,13 +934,13 @@ private void storeValueToRepo( byte[] value1, byte[] value2) { repo.addStorageRow( - domainAddress, new DataWord(hash1).toWrapper(), new DataWord(value1).toWrapper()); + domainAddress, new DataWordImpl(hash1).toWrapper(), new DataWordImpl(value1).toWrapper()); repo.addStorageRow( - domainAddress, new DataWord(hash2).toWrapper(), new DataWord(value2).toWrapper()); + domainAddress, new DataWordImpl(hash2).toWrapper(), new DataWordImpl(value2).toWrapper()); } private void createAccounts(DummyRepo repository, ECKey[] accountList) { - for (ECKey key : accountList) repository.createAccount(AionAddress.wrap(key.getAddress())); + for (ECKey key : accountList) repository.createAccount(Address.wrap(key.getAddress())); } private byte[] setupInputs(String domainName, Address ownerAddress, byte[] amount, ECKey k) { @@ -993,12 +993,12 @@ private static AionBlock createBundleAndCheck( StandaloneBlockchain bc, ECKey key, AionBlock parentBlock) { byte[] ZERO_BYTE = new byte[0]; - BigInteger accountNonce = bc.getRepository().getNonce(new AionAddress(key.getAddress())); + BigInteger accountNonce = bc.getRepository().getNonce(new Address(key.getAddress())); List<AionTransaction> transactions = new ArrayList<>(); // create 100 transactions per bundle for (int i = 0; i < 100; i++) { - Address destAddr = new AionAddress(HashUtil.h256(accountNonce.toByteArray())); + Address destAddr = new Address(HashUtil.h256(accountNonce.toByteArray())); AionTransaction sendTransaction = new AionTransaction( accountNonce.toByteArray(), diff --git a/modPrecompiled/test/org/aion/precompiled/contracts/BenchmarkTest.java b/modPrecompiled/test/org/aion/precompiled/contracts/BenchmarkTest.java index af79f2be07..71453da989 100644 --- a/modPrecompiled/test/org/aion/precompiled/contracts/BenchmarkTest.java +++ b/modPrecompiled/test/org/aion/precompiled/contracts/BenchmarkTest.java @@ -4,13 +4,13 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; -import org.aion.base.type.AionAddress; +import org.aion.types.Address; import org.aion.fastvm.ExecutionContext; import org.aion.mcf.config.CfgFork; -import org.aion.mcf.vm.types.DataWord; +import org.aion.mcf.vm.types.DataWordImpl; import org.aion.precompiled.ContractFactory; import org.aion.precompiled.type.PrecompiledContract; -import org.aion.vm.api.interfaces.Address; + import org.aion.zero.impl.config.CfgAion; import org.apache.commons.lang3.RandomUtils; import org.junit.After; @@ -25,7 +25,7 @@ public class BenchmarkTest { private byte[] txHash, callData; private Address origin, caller, blockCoinbase; private long blockNumber, blockTimestamp, blockNrgLimit, nrgLimit; - private DataWord blockDifficulty, nrgPrice, callValue; + private DataWordImpl blockDifficulty, nrgPrice, callValue; private int depth, kind, flags; private static int WARMUP = 2000; @@ -51,17 +51,17 @@ public void setup() throws IOException { cf = new ContractFactory(); CfgAion.inst(); txHash = RandomUtils.nextBytes(32); - origin = AionAddress.wrap(RandomUtils.nextBytes(32)); + origin = Address.wrap(RandomUtils.nextBytes(32)); caller = origin; - blockCoinbase = AionAddress.wrap(RandomUtils.nextBytes(32)); + blockCoinbase = Address.wrap(RandomUtils.nextBytes(32)); blockNumber = 2000001; blockTimestamp = System.currentTimeMillis() / 1000; blockNrgLimit = 5000000; - blockDifficulty = new DataWord(0x100000000L); + blockDifficulty = new DataWordImpl(0x100000000L); - nrgPrice = DataWord.ONE; + nrgPrice = DataWordImpl.ONE; nrgLimit = 20000; - callValue = DataWord.ZERO; + callValue = DataWordImpl.ZERO; callData = new byte[0]; depth = 0; diff --git a/modPrecompiled/test/org/aion/precompiled/contracts/DummyRepo.java b/modPrecompiled/test/org/aion/precompiled/contracts/DummyRepo.java index a2e20fec50..2946c446f9 100644 --- a/modPrecompiled/test/org/aion/precompiled/contracts/DummyRepo.java +++ b/modPrecompiled/test/org/aion/precompiled/contracts/DummyRepo.java @@ -6,18 +6,19 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.aion.base.db.IContractDetails; -import org.aion.base.db.IRepository; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; +import org.aion.interfaces.db.ContractDetails; +import org.aion.interfaces.db.Repository; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.mcf.core.AccountState; import org.aion.mcf.db.IBlockStoreBase; -import org.aion.mcf.vm.types.DataWord; import org.aion.mcf.vm.types.DoubleDataWord; -import org.aion.vm.api.interfaces.Address; +import org.aion.util.bytes.ByteUtil; -public class DummyRepo implements IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> { + +public class DummyRepo implements RepositoryCache<AccountState, IBlockStoreBase<?, ?>> { private Map<Address, AccountState> accounts = new HashMap<>(); private Map<Address, byte[]> contracts = new HashMap<>(); private Map<Address, Map<String, byte[]>> storage = new HashMap<>(); @@ -85,7 +86,7 @@ public BigInteger getNonce(Address addr) { } @Override - public IContractDetails getContractDetails(Address addr) { + public ContractDetails getContractDetails(Address addr) { throw new UnsupportedOperationException(); } @@ -131,8 +132,8 @@ public ByteArrayWrapper getStorageValue(Address addr, ByteArrayWrapper key) { if (res == null) { return null; } - if (res.length <= DataWord.BYTES) { - return new DataWord(res).toWrapper(); + if (res.length <= DataWordImpl.BYTES) { + return new DataWordImpl(res).toWrapper(); } else if (res.length == DoubleDataWord.BYTES) { return new DoubleDataWord(res).toWrapper(); } @@ -161,7 +162,7 @@ public BigInteger addBalance(Address addr, BigInteger value) { } @Override - public IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> startTracking() { + public RepositoryCache<AccountState, IBlockStoreBase<?, ?>> startTracking() { return new DummyRepo(this); } @@ -169,7 +170,7 @@ public BigInteger addBalance(Address addr, BigInteger value) { public void flush() {} @Override - public void flushTo(IRepository repo, boolean clearStateAfterFlush) {} + public void flushTo(Repository repo, boolean clearStateAfterFlush) {} @Override public void rollback() {} @@ -193,7 +194,7 @@ public boolean isValidRoot(byte[] root) { @Override public void updateBatch( Map<Address, AccountState> accountStates, - Map<Address, IContractDetails> contractDetailes) { + Map<Address, ContractDetails> contractDetailes) { throw new UnsupportedOperationException(); } @@ -206,12 +207,12 @@ public byte[] getRoot() { public void loadAccountState( Address addr, Map<Address, AccountState> cacheAccounts, - Map<Address, IContractDetails> cacheDetails) { + Map<Address, ContractDetails> cacheDetails) { throw new UnsupportedOperationException(); } @Override - public IRepository<AccountState, IBlockStoreBase<?, ?>> getSnapshotTo(byte[] root) { + public Repository<AccountState, IBlockStoreBase<?, ?>> getSnapshotTo(byte[] root) { throw new UnsupportedOperationException(); } diff --git a/modPrecompiled/test/org/aion/precompiled/contracts/EDVerifyContractTest.java b/modPrecompiled/test/org/aion/precompiled/contracts/EDVerifyContractTest.java index dee9fbcb32..c02b9722ee 100644 --- a/modPrecompiled/test/org/aion/precompiled/contracts/EDVerifyContractTest.java +++ b/modPrecompiled/test/org/aion/precompiled/contracts/EDVerifyContractTest.java @@ -11,18 +11,18 @@ import java.io.FileWriter; import java.io.IOException; import java.util.Arrays; -import org.aion.base.type.AionAddress; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; import org.aion.crypto.HashUtil; import org.aion.crypto.ISignature; import org.aion.fastvm.ExecutionContext; import org.aion.mcf.config.CfgFork; -import org.aion.mcf.vm.types.DataWord; import org.aion.precompiled.ContractFactory; import org.aion.precompiled.PrecompiledResultCode; import org.aion.precompiled.type.PrecompiledContract; -import org.aion.vm.api.interfaces.Address; + import org.aion.vm.api.interfaces.TransactionResult; import org.aion.zero.impl.config.CfgAion; import org.apache.commons.lang3.RandomUtils; @@ -34,18 +34,18 @@ public class EDVerifyContractTest { private byte[] txHash = RandomUtils.nextBytes(32); - private Address origin = AionAddress.wrap(RandomUtils.nextBytes(32)); + private Address origin = Address.wrap(RandomUtils.nextBytes(32)); private Address caller = origin; - private Address blockCoinbase = AionAddress.wrap(RandomUtils.nextBytes(32)); + private Address blockCoinbase = Address.wrap(RandomUtils.nextBytes(32)); private long blockNumber = 2000001; private long blockTimestamp = System.currentTimeMillis() / 1000; private long blockNrgLimit = 5000000; - private DataWord blockDifficulty = new DataWord(0x100000000L); + private DataWordImpl blockDifficulty = new DataWordImpl(0x100000000L); - private DataWord nrgPrice; + private DataWordImpl nrgPrice; private long nrgLimit; - private DataWord callValue; + private DataWordImpl callValue; private byte[] callData; private byte[] pubKey; @@ -57,9 +57,9 @@ public class EDVerifyContractTest { @Before public void setup() throws IOException { - nrgPrice = DataWord.ONE; + nrgPrice = DataWordImpl.ONE; nrgLimit = 20000; - callValue = DataWord.ZERO; + callValue = DataWordImpl.ZERO; callData = new byte[0]; new File(System.getProperty("user.dir") + "/mainnet/config").mkdirs(); @@ -178,12 +178,12 @@ public void incorrectInputTest() { assertNotNull(contract); TransactionResult result = contract.execute(input, 21000L); assertThat(result.getResultCode().isSuccess()); - assertThat(Arrays.equals(result.getReturnData(), AionAddress.ZERO_ADDRESS().toBytes())); + assertThat(Arrays.equals(result.getReturnData(), Address.ZERO_ADDRESS().toBytes())); } @Test public void shouldFailIfNotEnoughEnergy() { - nrgPrice = DataWord.ONE; + nrgPrice = DataWordImpl.ONE; byte[] input = setupInput(); ExecutionContext ctx = diff --git a/modPrecompiled/test/org/aion/precompiled/contracts/MultiSignatureContractTest.java b/modPrecompiled/test/org/aion/precompiled/contracts/MultiSignatureContractTest.java index 9d50ebb654..4e58621b73 100644 --- a/modPrecompiled/test/org/aion/precompiled/contracts/MultiSignatureContractTest.java +++ b/modPrecompiled/test/org/aion/precompiled/contracts/MultiSignatureContractTest.java @@ -12,18 +12,18 @@ import java.util.List; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.ByteUtil; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.crypto.ECKeyFac; import org.aion.crypto.ISignature; import org.aion.crypto.ed25519.ECKeyEd25519; -import org.aion.mcf.vm.types.DataWord; import org.aion.precompiled.PrecompiledResultCode; import org.aion.precompiled.PrecompiledTransactionResult; import org.aion.precompiled.type.StatefulPrecompiledContract; -import org.aion.vm.api.interfaces.Address; + +import org.aion.util.bytes.ByteUtil; import org.junit.After; import org.junit.Before; import org.junit.Ignore; @@ -40,7 +40,7 @@ public class MultiSignatureContractTest { private static final long NRG_LIMIT = 100000L; private static final long NRG_PRICE = 10000000000L; private Address to; - private IRepositoryCache repo; + private RepositoryCache repo; private List<Address> addrsToClean; @Before @@ -77,7 +77,7 @@ private PrecompiledTransactionResult execute( // Creates a new account with initial balance balance that will be deleted at test end. private Address getExistentAddress(BigInteger balance) { - Address addr = AionAddress.wrap(ECKeyFac.inst().create().getAddress()); + Address addr = Address.wrap(ECKeyFac.inst().create().getAddress()); repo.createAccount(addr); repo.addBalance(addr, balance); addrsToClean.add(addr); @@ -107,7 +107,7 @@ private List<Address> getExistentAddresses( // This is the constructMsg method provided by MSC class but here you can specify your nonce. private static byte[] customMsg( - IRepositoryCache repo, + RepositoryCache repo, BigInteger nonce, Address walletId, Address to, @@ -188,10 +188,10 @@ private byte[] toValidSendInput( // If walletId is not a multi-sig wallet this method fails. private List<Long> getWalletThresholdAndNumOwners(Address walletId) { List<Long> values = new ArrayList<>(); - byte[] metaKey = new byte[DataWord.BYTES]; + byte[] metaKey = new byte[DataWordImpl.BYTES]; metaKey[0] = (byte) 0x80; ByteArrayWrapper metaData = - repo.getStorageValue(walletId, new DataWord(metaKey).toWrapper()); + repo.getStorageValue(walletId, new DataWordImpl(metaKey).toWrapper()); if (metaData == null) { fail(); } @@ -201,7 +201,7 @@ private List<Long> getWalletThresholdAndNumOwners(Address walletId) { buffer.flip(); values.add(buffer.getLong()); buffer.flip(); - buffer.put(Arrays.copyOfRange(rawData, Long.BYTES, DataWord.BYTES)); + buffer.put(Arrays.copyOfRange(rawData, Long.BYTES, DataWordImpl.BYTES)); buffer.flip(); values.add(buffer.getLong()); return values; @@ -218,22 +218,22 @@ private Set<Address> getWalletOwners(Address walletId, long numOwners) { byte[] account = new byte[Address.SIZE]; buffer.putLong(i); buffer.flip(); - byte[] request = new byte[DataWord.BYTES]; - buffer.get(request, DataWord.BYTES - Long.BYTES, Long.BYTES); - portion = repo.getStorageValue(walletId, new DataWord(request).toWrapper()); + byte[] request = new byte[DataWordImpl.BYTES]; + buffer.get(request, DataWordImpl.BYTES - Long.BYTES, Long.BYTES); + portion = repo.getStorageValue(walletId, new DataWordImpl(request).toWrapper()); if (portion == null) { fail(); } - System.arraycopy(portion.getData(), 0, account, 0, DataWord.BYTES); + System.arraycopy(portion.getData(), 0, account, 0, DataWordImpl.BYTES); request[0] = (byte) 0x40; - portion = repo.getStorageValue(walletId, new DataWord(request).toWrapper()); + portion = repo.getStorageValue(walletId, new DataWordImpl(request).toWrapper()); if (portion == null) { fail(); } - System.arraycopy(portion.getData(), 0, account, DataWord.BYTES, DataWord.BYTES); + System.arraycopy(portion.getData(), 0, account, DataWordImpl.BYTES, DataWordImpl.BYTES); - Address address = new AionAddress(account); + Address address = new Address(account); if (owners.contains(address)) { fail(); } @@ -263,7 +263,7 @@ private Address createMultiSigWallet( List<Address> ownerAddrs = new ArrayList<>(); Address addr; for (ECKeyEd25519 key : owners) { - addr = new AionAddress(key.getAddress()); + addr = new Address(key.getAddress()); repo.createAccount(addr); addrsToClean.add(addr); ownerAddrs.add(addr); @@ -273,7 +273,7 @@ private Address createMultiSigWallet( MultiSignatureContract msc = new MultiSignatureContract(repo, ownerAddrs.get(0)); PrecompiledTransactionResult res = msc.execute(input, COST); assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); - Address wallet = new AionAddress(res.getReturnData()); + Address wallet = new Address(res.getReturnData()); repo.addBalance(wallet, balance); addrsToClean.add(wallet); repo.flush(); @@ -301,7 +301,7 @@ private List<ISignature> produceSignatures(List<ECKeyEd25519> owners, int numSig // threshold and consists of all the owners in owners and no more. private void checkCreateResult( PrecompiledTransactionResult res, long threshold, List<Address> owners) { - Address walletId = new AionAddress(res.getReturnData()); + Address walletId = new Address(res.getReturnData()); addrsToClean.add(walletId); assertEquals(BigInteger.ZERO, repo.getBalance(walletId)); assertEquals(BigInteger.ZERO, repo.getNonce(walletId)); @@ -327,7 +327,7 @@ private void checkAccountState(Address account, BigInteger nonce, BigInteger bal @Test(expected = IllegalArgumentException.class) public void testConstructWithNullTrack() { - new MultiSignatureContract(null, AionAddress.wrap(ECKeyFac.inst().create().getAddress())); + new MultiSignatureContract(null, Address.wrap(ECKeyFac.inst().create().getAddress())); } @Test(expected = IllegalArgumentException.class) @@ -353,7 +353,7 @@ public void testNrgBelowLegalLimit() { // Second test send-tx logic. // Test with min illegal cost. List<ECKeyEd25519> sendOwners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address sendCaller = new AionAddress(sendOwners.get(0).getAddress()); + Address sendCaller = new Address(sendOwners.get(0).getAddress()); Address wallet = createMultiSigWallet( sendOwners, MultiSignatureContract.MIN_OWNERS - 1, DEFAULT_BALANCE); @@ -409,7 +409,7 @@ public void testNrgAboveThanCost() { // Second test send-tx logic. // Test with min illegal cost. List<ECKeyEd25519> sendOwners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address sendCaller = new AionAddress(sendOwners.get(0).getAddress()); + Address sendCaller = new Address(sendOwners.get(0).getAddress()); Address wallet = createMultiSigWallet( sendOwners, MultiSignatureContract.MIN_OWNERS - 1, DEFAULT_BALANCE); @@ -631,7 +631,7 @@ public void testCreateWalletWithMultiSigWalletCaller() { PrecompiledTransactionResult res = execute(caller, input, NRG_LIMIT, PrecompiledResultCode.SUCCESS, NRG_LIMIT - COST); - Address walletCaller = new AionAddress(res.getReturnData()); + Address walletCaller = new Address(res.getReturnData()); addrsToClean.add(walletCaller); checkAccountState(walletCaller, BigInteger.ZERO, BigInteger.ZERO); @@ -659,7 +659,7 @@ public void testCreateWalletWithOwnerAsAMultiSigWallet() { PrecompiledTransactionResult res = execute(caller, input, NRG_LIMIT, PrecompiledResultCode.SUCCESS, NRG_LIMIT - COST); - Address wallet = new AionAddress(res.getReturnData()); + Address wallet = new Address(res.getReturnData()); addrsToClean.add(wallet); checkAccountState(wallet, BigInteger.ZERO, BigInteger.ZERO); @@ -689,7 +689,7 @@ public void testCreateWalletWithThresholdEqualToLegalNumOwners() { PrecompiledTransactionResult res = execute(caller, input, NRG_LIMIT, PrecompiledResultCode.SUCCESS, NRG_LIMIT - COST); checkCreateResult(res, threshold, owners); - checkAccountState(new AionAddress(res.getReturnData()), BigInteger.ZERO, BigInteger.ZERO); + checkAccountState(new Address(res.getReturnData()), BigInteger.ZERO, BigInteger.ZERO); // Test using max legal number of owners. owners = @@ -700,7 +700,7 @@ public void testCreateWalletWithThresholdEqualToLegalNumOwners() { res = execute(caller, input, NRG_LIMIT, PrecompiledResultCode.SUCCESS, NRG_LIMIT - COST); checkCreateResult(res, threshold, owners); - checkAccountState(new AionAddress(res.getReturnData()), BigInteger.ZERO, BigInteger.ZERO); + checkAccountState(new Address(res.getReturnData()), BigInteger.ZERO, BigInteger.ZERO); } @Test @@ -714,10 +714,10 @@ public void testCreateWalletAddressIsDeterministic() { PrecompiledTransactionResult res = execute(caller, input, NRG_LIMIT, PrecompiledResultCode.SUCCESS, NRG_LIMIT - COST); - Address wallet1 = new AionAddress(res.getReturnData()); + Address wallet1 = new Address(res.getReturnData()); res = execute(caller, input, NRG_LIMIT, PrecompiledResultCode.SUCCESS, NRG_LIMIT - COST); - Address wallet2 = new AionAddress(res.getReturnData()); + Address wallet2 = new Address(res.getReturnData()); assertEquals(wallet1, wallet2); } @@ -733,7 +733,7 @@ public void testWalletAddressStartsWithAionPrefix() { PrecompiledTransactionResult res = execute(caller, input, NRG_LIMIT, PrecompiledResultCode.SUCCESS, NRG_LIMIT - COST); - Address wallet = new AionAddress(res.getReturnData()); + Address wallet = new Address(res.getReturnData()); assertTrue(wallet.toString().startsWith("a0")); } @@ -750,7 +750,7 @@ public void testCreateWalletWithMinimumLegalThreshold() { PrecompiledTransactionResult res = execute(caller, input, NRG_LIMIT, PrecompiledResultCode.SUCCESS, NRG_LIMIT - COST); checkCreateResult(res, threshold, owners); - checkAccountState(new AionAddress(res.getReturnData()), BigInteger.ZERO, BigInteger.ZERO); + checkAccountState(new Address(res.getReturnData()), BigInteger.ZERO, BigInteger.ZERO); // Test using max legal number of owners. owners = @@ -761,7 +761,7 @@ public void testCreateWalletWithMinimumLegalThreshold() { res = execute(caller, input, NRG_LIMIT, PrecompiledResultCode.SUCCESS, NRG_LIMIT - COST); checkCreateResult(res, threshold, owners); - checkAccountState(new AionAddress(res.getReturnData()), BigInteger.ZERO, BigInteger.ZERO); + checkAccountState(new Address(res.getReturnData()), BigInteger.ZERO, BigInteger.ZERO); } // <----------------------------------SEND TRANSACTION TESTS-----------------------------------> @@ -769,7 +769,7 @@ public void testCreateWalletWithMinimumLegalThreshold() { @Test public void testSendTxWithZeroSignatures() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_THRESH, DEFAULT_BALANCE); @@ -791,7 +791,7 @@ public void testSendTxWithZeroSignatures() { public void testSendTxWithMoreThanMaxOwnersSignatures() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MAX_OWNERS + 1); ECKeyEd25519 extra = owners.remove(0); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MAX_OWNERS, DEFAULT_BALANCE); @@ -817,7 +817,7 @@ public void testSendTxWithMoreThanMaxOwnersSignatures() { public void testSendTxValidSignaturesMeetsThresholdPlusPhonySig() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS + 1); ECKeyEd25519 phony = owners.remove(0); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_OWNERS, DEFAULT_BALANCE); @@ -842,7 +842,7 @@ public void testSendTxValidSignaturesMeetsThresholdPlusPhonySig() { @Test public void testSendTxNegativeAmountWithZeroBalance() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_THRESH, BigInteger.ZERO); BigInteger amt = AMOUNT.negate(); @@ -865,7 +865,7 @@ public void testSendTxNegativeAmountWithZeroBalance() { @Test public void testSendTxNegativeAmountWithActualBalance() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_THRESH, DEFAULT_BALANCE); BigInteger amt = AMOUNT.negate(); @@ -889,7 +889,7 @@ public void testSendTxNegativeAmountWithActualBalance() { public void testSendTxFromRegularAddress() { // Our wallet is not a wallet... List<ECKeyEd25519> phonies = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address phonyWallet = new AionAddress(phonies.get(0).getAddress()); + Address phonyWallet = new Address(phonies.get(0).getAddress()); repo.addBalance(phonyWallet, DEFAULT_BALANCE); BigInteger amt = BigInteger.ONE; @@ -912,7 +912,7 @@ public void testSendTxFromRegularAddress() { @Test public void testSendTxNoSenderInInput() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_OWNERS, DEFAULT_BALANCE); @@ -934,7 +934,7 @@ public void testSendTxNoSenderInInput() { @Test public void testSendTxNoRecipient() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_OWNERS, DEFAULT_BALANCE); @@ -956,7 +956,7 @@ public void testSendTxNoRecipient() { @Test public void testSendTxNoAmount() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_OWNERS, DEFAULT_BALANCE); @@ -978,7 +978,7 @@ public void testSendTxNoAmount() { @Test public void testSendTxNoNrgPrice() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_OWNERS, DEFAULT_BALANCE); @@ -1010,7 +1010,7 @@ public void testSendTxNoNrgPrice() { @Test public void testSendTxWithSignatureUsingPreviousNonce() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_OWNERS, DEFAULT_BALANCE); @@ -1041,7 +1041,7 @@ public void testSendTxWithSignatureUsingPreviousNonce() { @Test public void testSendTxWhereSignedMessagesDifferInNonce() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_OWNERS, DEFAULT_BALANCE); @@ -1076,7 +1076,7 @@ public void testSendTxWhereSignedMessagesDifferInNonce() { @Test public void testSendTxWhereSignedMessagesDifferInRecipient() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_OWNERS, DEFAULT_BALANCE); @@ -1106,7 +1106,7 @@ public void testSendTxWhereSignedMessagesDifferInRecipient() { @Test public void testSendTxWhereSignedMessagesDifferInAmount() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_OWNERS, DEFAULT_BALANCE); @@ -1139,7 +1139,7 @@ public void testSendTxWhereSignedMessagesDifferInAmount() { @Test public void testSendTxWhereSignedMessagesDifferInNrgPrice() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_OWNERS, DEFAULT_BALANCE); @@ -1169,7 +1169,7 @@ public void testSendTxWhereSignedMessagesDifferInNrgPrice() { @Test public void testSendTxAllSignWrongRecipient() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_OWNERS, DEFAULT_BALANCE); Address stranger = getExistentAddress(BigInteger.ZERO); @@ -1196,7 +1196,7 @@ public void testSendTxAllSignWrongRecipient() { @Test public void testSendTxAllSignWrongAmount() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_OWNERS, DEFAULT_BALANCE); @@ -1222,7 +1222,7 @@ public void testSendTxAllSignWrongAmount() { @Test public void testSendTxAllSignWrongNonce() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_OWNERS, DEFAULT_BALANCE); Address stranger = getExistentAddress(BigInteger.ZERO); @@ -1249,7 +1249,7 @@ public void testSendTxAllSignWrongNonce() { @Test public void testSendTxAllSignWrongNrgPrice() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_OWNERS, DEFAULT_BALANCE); @@ -1276,7 +1276,7 @@ public void testSendTxAllSignWrongNrgPrice() { public void testSendTxInsufficientBalance() { // Create account with zero balance. List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_THRESH, BigInteger.ZERO); @@ -1300,7 +1300,7 @@ public void testSendTxInsufficientBalance() { public void testWalletAbleToSendTxToDiffWallet() { List<ECKeyEd25519> owners1 = produceKeys(MultiSignatureContract.MIN_OWNERS); List<ECKeyEd25519> owners2 = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners1.get(0).getAddress()); + Address caller = new Address(owners1.get(0).getAddress()); Address wallet1 = createMultiSigWallet(owners1, MultiSignatureContract.MIN_THRESH, DEFAULT_BALANCE); Address wallet2 = @@ -1327,7 +1327,7 @@ public void testWalletAbleToSendTxToDiffWallet() { @Test public void testSendTxLessSignaturesThanThresholdMinOwners() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_OWNERS, DEFAULT_BALANCE); @@ -1352,7 +1352,7 @@ public void testSendTxLessSignaturesThanThresholdMinOwners() { @Test public void testSendTxLessSignaturesThanThresholdMaxOwners() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MAX_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MAX_OWNERS, DEFAULT_BALANCE); @@ -1377,7 +1377,7 @@ public void testSendTxLessSignaturesThanThresholdMaxOwners() { @Test public void testSendTxSameSignaturesAsThresholdMinOwners() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_THRESH, DEFAULT_BALANCE); @@ -1401,7 +1401,7 @@ public void testSendTxSameSignaturesAsThresholdMinOwners() { @Test public void testSendTxSameSignaturesAsThresholdMaxOwners() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MAX_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MAX_OWNERS, DEFAULT_BALANCE); @@ -1425,7 +1425,7 @@ public void testSendTxSameSignaturesAsThresholdMaxOwners() { @Test public void testSendTxMoreSignaturesThanThresholdMinOwners() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet( owners, MultiSignatureContract.MIN_OWNERS - 1, DEFAULT_BALANCE); @@ -1450,7 +1450,7 @@ public void testSendTxMoreSignaturesThanThresholdMinOwners() { @Test public void testSendTxMoreSignaturesThanThresholdMaxOwners() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MAX_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_THRESH, DEFAULT_BALANCE); @@ -1474,7 +1474,7 @@ public void testSendTxMoreSignaturesThanThresholdMaxOwners() { @Test public void testSendTxDuplicateSignee() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MAX_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MAX_OWNERS, DEFAULT_BALANCE); @@ -1501,7 +1501,7 @@ public void testSendTxDuplicateSignee() { public void testSendTxSignatureOneSigneeIsNonOwner() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MAX_OWNERS); ECKeyEd25519 phony = produceKeys(1).get(0); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MAX_OWNERS, DEFAULT_BALANCE); @@ -1528,7 +1528,7 @@ public void testSendTxSignatureOneSigneeIsNonOwner() { @Test public void testSendTxSignedProperlyButNotSignedByOwnerCaller() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MAX_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_THRESH, DEFAULT_BALANCE); @@ -1574,7 +1574,7 @@ public void testSendTxSignedProperlyButCallerIsNotOwner() { checkAccountState(wallet, BigInteger.ZERO, DEFAULT_BALANCE); checkAccountState(to, BigInteger.ZERO, BigInteger.ZERO); execute( - new AionAddress(phony.getAddress()), + new Address(phony.getAddress()), input, NRG_LIMIT, PrecompiledResultCode.FAILURE, @@ -1586,7 +1586,7 @@ public void testSendTxSignedProperlyButCallerIsNotOwner() { @Test public void testPartialSignature() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_OWNERS, DEFAULT_BALANCE); @@ -1614,7 +1614,7 @@ public void testPartialSignature() { @Test public void testPartialWalletAddress() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_OWNERS, DEFAULT_BALANCE); @@ -1642,7 +1642,7 @@ public void testPartialWalletAddress() { @Test public void testPartialRecipientAddress() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_OWNERS, DEFAULT_BALANCE); @@ -1670,7 +1670,7 @@ public void testPartialRecipientAddress() { @Test public void testPartialAmount() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_OWNERS, DEFAULT_BALANCE); @@ -1698,7 +1698,7 @@ public void testPartialAmount() { @Test public void testPartialNrgPrice() { List<ECKeyEd25519> owners = produceKeys(MultiSignatureContract.MIN_OWNERS); - Address caller = new AionAddress(owners.get(0).getAddress()); + Address caller = new Address(owners.get(0).getAddress()); Address wallet = createMultiSigWallet(owners, MultiSignatureContract.MIN_OWNERS, DEFAULT_BALANCE); diff --git a/modPrecompiled/test/org/aion/precompiled/contracts/TotalCurrencyContractTest.java b/modPrecompiled/test/org/aion/precompiled/contracts/TotalCurrencyContractTest.java index 2f73848d83..9d1258336e 100644 --- a/modPrecompiled/test/org/aion/precompiled/contracts/TotalCurrencyContractTest.java +++ b/modPrecompiled/test/org/aion/precompiled/contracts/TotalCurrencyContractTest.java @@ -5,15 +5,15 @@ import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.Arrays; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.AionAddress; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; -import org.aion.mcf.vm.types.DataWord; import org.aion.precompiled.ContractFactory; import org.aion.precompiled.PrecompiledResultCode; import org.aion.precompiled.PrecompiledTransactionResult; -import org.aion.vm.api.interfaces.Address; + import org.junit.After; import org.junit.Before; import org.junit.Ignore; @@ -25,14 +25,14 @@ public class TotalCurrencyContractTest { private static final long COST = 21000L; private static final BigInteger AMT = BigInteger.valueOf(1000); private TotalCurrencyContract tcc; - private IRepositoryCache repo; + private RepositoryCache repo; private ECKey ownerKey; @Before public void setup() { repo = new DummyRepo(); ownerKey = ECKeyFac.inst().create(); - tcc = new TotalCurrencyContract(repo, ADDR, AionAddress.wrap(ownerKey.getAddress())); + tcc = new TotalCurrencyContract(repo, ADDR, Address.wrap(ownerKey.getAddress())); } @After @@ -52,7 +52,7 @@ public void tearDown() { */ private byte[] constructUpdateInput(byte chainID, byte signum) { ByteBuffer buffer = ByteBuffer.allocate(18); - buffer.put(chainID).put(signum).put(new DataWord(AMT.toByteArray()).getData()); + buffer.put(chainID).put(signum).put(new DataWordImpl(AMT.toByteArray()).getData()); byte[] payload = buffer.array(); buffer = ByteBuffer.allocate(18 + 96); @@ -117,7 +117,7 @@ public void TestUpdateAndGetTotalAmount() { assertEquals(PrecompiledResultCode.SUCCESS, res.getResultCode()); assertEquals(0L, res.getEnergyRemaining()); - tcc = new TotalCurrencyContract(repo, ADDR, AionAddress.wrap(ownerKey.getAddress())); + tcc = new TotalCurrencyContract(repo, ADDR, Address.wrap(ownerKey.getAddress())); input = new byte[] {(byte) 0x0}; res = tcc.execute(input, COST); @@ -186,7 +186,7 @@ public void TestUpdateTotalNotOwner() { new TotalCurrencyContract( repo, ADDR, - AionAddress.wrap(ECKeyFac.inst().create().getAddress())); // diff owner. + Address.wrap(ECKeyFac.inst().create().getAddress())); // diff owner. byte[] input = constructUpdateInput((byte) 0x0, (byte) 0x0); PrecompiledTransactionResult res = contract.execute(input, COST); diff --git a/modPrecompiled/test/org/aion/precompiled/encoding/AbiEncoder.java b/modPrecompiled/test/org/aion/precompiled/encoding/AbiEncoder.java index 9d2dbf89c4..fc2d91db95 100644 --- a/modPrecompiled/test/org/aion/precompiled/encoding/AbiEncoder.java +++ b/modPrecompiled/test/org/aion/precompiled/encoding/AbiEncoder.java @@ -6,9 +6,9 @@ import java.util.List; import javax.annotation.Nonnull; import javax.annotation.concurrent.ThreadSafe; -import org.aion.base.util.ByteUtil; import org.aion.crypto.HashUtil; import org.aion.precompiled.PrecompiledUtilities; +import org.aion.util.bytes.ByteUtil; @ThreadSafe public class AbiEncoder { diff --git a/modPrecompiled/test/org/aion/precompiled/encoding/AddressFVM.java b/modPrecompiled/test/org/aion/precompiled/encoding/AddressFVM.java index 03f7919e6f..a120400f8d 100644 --- a/modPrecompiled/test/org/aion/precompiled/encoding/AddressFVM.java +++ b/modPrecompiled/test/org/aion/precompiled/encoding/AddressFVM.java @@ -3,7 +3,7 @@ import java.util.List; import java.util.Optional; import javax.annotation.Nonnull; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; public class AddressFVM extends BaseTypeFVM { diff --git a/modPrecompiled/test/org/aion/precompiled/encoding/Bytes32FVM.java b/modPrecompiled/test/org/aion/precompiled/encoding/Bytes32FVM.java index 9aa9a24786..3d382be695 100644 --- a/modPrecompiled/test/org/aion/precompiled/encoding/Bytes32FVM.java +++ b/modPrecompiled/test/org/aion/precompiled/encoding/Bytes32FVM.java @@ -3,7 +3,7 @@ import java.util.List; import java.util.Optional; import javax.annotation.Nonnull; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; public class Bytes32FVM extends BaseTypeFVM { private ByteArrayWrapper word; diff --git a/modPrecompiled/test/org/aion/precompiled/encoding/Uint128FVM.java b/modPrecompiled/test/org/aion/precompiled/encoding/Uint128FVM.java index 51fc56b82f..718875bd36 100644 --- a/modPrecompiled/test/org/aion/precompiled/encoding/Uint128FVM.java +++ b/modPrecompiled/test/org/aion/precompiled/encoding/Uint128FVM.java @@ -3,7 +3,7 @@ import java.util.List; import java.util.Optional; import javax.annotation.Nonnull; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; public class Uint128FVM extends BaseTypeFVM { diff --git a/modRlp/build.gradle b/modRlp/build.gradle index b0b0c5dee4..f22aa34a16 100644 --- a/modRlp/build.gradle +++ b/modRlp/build.gradle @@ -1,8 +1,8 @@ ext.moduleName = 'aion.rlp' dependencies { - compile project(':modUtil') - //compile files('../lib/libnsc.jar') + compile 'network.aion:util4j:0.4.0' + compile 'com.madgag.spongycastle:prov:1.58.0.0' compile 'com.madgag.spongycastle:core:1.58.0.0' compile 'com.google.guava:guava:25.1-jre' diff --git a/modTxPool/build.gradle b/modTxPool/build.gradle index db8e6579fd..f613d17647 100644 --- a/modTxPool/build.gradle +++ b/modTxPool/build.gradle @@ -1,6 +1,5 @@ ext.moduleName = 'aion.txpool' dependencies { - compile project(':modAionBase') -// runtime project(':modTxPoolImpl') + compile 'network.aion:vm-api4j:0.4.0' } diff --git a/modTxPool/src/module-info.java b/modTxPool/src/module-info.java index 974e1fcddb..bb47c23c84 100644 --- a/modTxPool/src/module-info.java +++ b/modTxPool/src/module-info.java @@ -1,5 +1,4 @@ module aion.txpool { - requires aion.base; requires aion.vm.api; exports org.aion.txpool; diff --git a/modTxPool/src/org/aion/txpool/ITxPool.java b/modTxPool/src/org/aion/txpool/ITxPool.java index 02a7b30008..ffad7a66b7 100644 --- a/modTxPool/src/org/aion/txpool/ITxPool.java +++ b/modTxPool/src/org/aion/txpool/ITxPool.java @@ -3,15 +3,15 @@ import java.math.BigInteger; import java.util.List; import java.util.Map; -import org.aion.base.type.ITransaction; -import org.aion.vm.api.interfaces.Address; +import org.aion.interfaces.tx.Transaction; +import org.aion.types.Address; /** * Aion pending state should be the only user of transaction pool. * * @param <TX> */ -public interface ITxPool<TX extends ITransaction> { +public interface ITxPool<TX extends Transaction> { String PROP_TX_TIMEOUT = "tx-timeout"; String PROP_BLOCK_SIZE_LIMIT = "blk-size-limit"; diff --git a/modTxPool/src/org/aion/txpool/TxPoolModule.java b/modTxPool/src/org/aion/txpool/TxPoolModule.java index 48d1b3296c..6902ef6643 100644 --- a/modTxPool/src/org/aion/txpool/TxPoolModule.java +++ b/modTxPool/src/org/aion/txpool/TxPoolModule.java @@ -1,20 +1,20 @@ package org.aion.txpool; import java.util.Properties; -import org.aion.base.type.ITransaction; +import org.aion.interfaces.tx.Transaction; public final class TxPoolModule { private static TxPoolModule singleton = null; public static final String MODULENAME = "module_name"; - private static ITxPool<ITransaction> TXPOOL; + private static ITxPool<Transaction> TXPOOL; @SuppressWarnings("unchecked") private TxPoolModule(Properties config) throws Exception { String moduleName = (String) config.get(MODULENAME); if (moduleName != null) { TXPOOL = - (ITxPool<ITransaction>) + (ITxPool<Transaction>) getClass() .getClassLoader() .loadClass(moduleName) diff --git a/modTxPoolImpl/build.gradle b/modTxPoolImpl/build.gradle index 3c9a90466f..f9acecf4b2 100644 --- a/modTxPoolImpl/build.gradle +++ b/modTxPoolImpl/build.gradle @@ -3,7 +3,9 @@ test.dependsOn copyNativeLibsForModuleTests clean.dependsOn deleteNativeLibs dependencies { - compile project(':modAionBase') + compile 'network.aion:vm-api4j:0.4.0' + compile 'network.aion:util4j:0.4.0' + compile project(':modTxPool') compile project(':modLogger') compile 'com.madgag.spongycastle:prov:1.58.0.0' diff --git a/modTxPoolImpl/src/module-info.java b/modTxPoolImpl/src/module-info.java index e586f36175..7053f7db3e 100644 --- a/modTxPoolImpl/src/module-info.java +++ b/modTxPoolImpl/src/module-info.java @@ -1,7 +1,7 @@ module aion.txpool.impl { requires aion.log; requires slf4j.api; - requires aion.base; + requires aion.util; requires aion.txpool; requires aion.vm.api; diff --git a/modTxPoolImpl/src/org/aion/txpool/common/AbstractTxPool.java b/modTxPoolImpl/src/org/aion/txpool/common/AbstractTxPool.java index 29f2db5d34..b1b8da86a2 100644 --- a/modTxPoolImpl/src/org/aion/txpool/common/AbstractTxPool.java +++ b/modTxPoolImpl/src/org/aion/txpool/common/AbstractTxPool.java @@ -17,16 +17,17 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantReadWriteLock; -import org.aion.base.Constant; -import org.aion.base.type.ITransaction; -import org.aion.base.util.ByteArrayWrapper; + +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; +import org.aion.util.bytes.ByteUtil; import org.slf4j.Logger; -import org.spongycastle.pqc.math.linearalgebra.ByteUtils; -import org.aion.vm.api.interfaces.Address; +import org.aion.interfaces.tx.Transaction; +import org.aion.interfaces.block.Constant; -public abstract class AbstractTxPool<TX extends ITransaction> { +public abstract class AbstractTxPool<TX extends Transaction> { protected static final Logger LOG = AionLoggerFactory.getLogger(LogEnum.TXPOOL.toString()); @@ -146,7 +147,7 @@ protected void sortTxn() { SortedMap<Long, LinkedHashSet<ByteArrayWrapper>> timeMap = Collections.synchronizedSortedMap(new TreeMap<>()); - Map<ITransaction, Long> updatedTx = new HashMap<>(); + Map<Transaction, Long> updatedTx = new HashMap<>(); this.mainMap .entrySet() .parallelStream() @@ -157,13 +158,13 @@ protected void sortTxn() { return; } - ITransaction tx = ts.getTx(); + Transaction tx = ts.getTx(); // Gen temp timeMap long timestamp = tx.getTimeStampBI().longValue() / multiplyM; Map<BigInteger, SimpleEntry<ByteArrayWrapper, BigInteger>> nonceMap; - ITransaction replacedTx = null; + Transaction replacedTx = null; synchronized (accMap) { if (accMap.get(tx.getSenderAddress()) != null) { nonceMap = accMap.get(tx.getSenderAddress()); @@ -182,7 +183,7 @@ protected void sortTxn() { LOG.trace( "AbstractTxPool.sortTxn Put tx into nonceMap: nonce:[{}] ts:[{}] nrgCharge:[{}]", nonce, - ByteUtils.toHexString(e.getKey().getData()), + ByteUtil.toHexString(e.getKey().getData()), nrgCharge.toString()); } @@ -258,7 +259,7 @@ protected void sortTxn() { }); if (!updatedTx.isEmpty()) { - for (Map.Entry<ITransaction, Long> en : updatedTx.entrySet()) { + for (Map.Entry<Transaction, Long> en : updatedTx.entrySet()) { ByteArrayWrapper bw = ByteArrayWrapper.wrap(en.getKey().getTransactionHash()); if (this.timeView.get(en.getValue()) != null) { this.timeView.get(en.getValue()).remove(bw); diff --git a/modTxPoolImpl/src/org/aion/txpool/common/AccountState.java b/modTxPoolImpl/src/org/aion/txpool/common/AccountState.java index 298c325f86..74ad34db2f 100644 --- a/modTxPoolImpl/src/org/aion/txpool/common/AccountState.java +++ b/modTxPoolImpl/src/org/aion/txpool/common/AccountState.java @@ -7,7 +7,7 @@ import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.atomic.AtomicBoolean; -import org.aion.base.util.ByteArrayWrapper; +import org.aion.types.ByteArrayWrapper; public class AccountState { private final SortedMap<BigInteger, AbstractMap.SimpleEntry<ByteArrayWrapper, BigInteger>> diff --git a/modTxPoolImpl/src/org/aion/txpool/common/TxDependList.java b/modTxPoolImpl/src/org/aion/txpool/common/TxDependList.java index 28b342132b..2c0823cf38 100644 --- a/modTxPoolImpl/src/org/aion/txpool/common/TxDependList.java +++ b/modTxPoolImpl/src/org/aion/txpool/common/TxDependList.java @@ -3,7 +3,8 @@ import java.math.BigInteger; import java.util.ArrayList; import java.util.List; -import org.aion.vm.api.interfaces.Address; + +import org.aion.types.Address; public class TxDependList<BW> { private final List<BW> txList; diff --git a/modTxPoolImpl/src/org/aion/txpool/zero/TxPoolA0.java b/modTxPoolImpl/src/org/aion/txpool/zero/TxPoolA0.java index 4e71de9712..f4ee0061b0 100644 --- a/modTxPoolImpl/src/org/aion/txpool/zero/TxPoolA0.java +++ b/modTxPoolImpl/src/org/aion/txpool/zero/TxPoolA0.java @@ -16,18 +16,18 @@ import java.util.SortedMap; import java.util.TreeMap; import java.util.stream.Collectors; -import org.aion.base.type.ITransaction; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.util.TimeInstant; +import org.aion.interfaces.tx.Transaction; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.txpool.ITxPool; import org.aion.txpool.common.AbstractTxPool; import org.aion.txpool.common.AccountState; import org.aion.txpool.common.TxDependList; -import org.spongycastle.pqc.math.linearalgebra.ByteUtils; -import org.aion.vm.api.interfaces.Address; +import org.aion.util.bytes.ByteUtil; +import org.aion.util.time.TimeInstant; @SuppressWarnings("unchecked") -public class TxPoolA0<TX extends ITransaction> extends AbstractTxPool<TX> implements ITxPool<TX> { +public class TxPoolA0<TX extends Transaction> extends AbstractTxPool<TX> implements ITxPool<TX> { public TxPoolA0() { super(); @@ -120,7 +120,7 @@ public List<TX> add(List<TX> txl) { if (LOG.isWarnEnabled()) { LOG.warn( "The tx hash existed in the pool! [{}]", - ByteUtils.toHexString(bw.getData())); + ByteUtil.toHexString(bw.getData())); } continue; } @@ -128,7 +128,7 @@ public List<TX> add(List<TX> txl) { if (LOG.isTraceEnabled()) { LOG.trace( "Put tx into mainMap: hash:[{}] tx:[{}]", - ByteUtils.toHexString(bw.getData()), + ByteUtil.toHexString(bw.getData()), tx.toString()); } @@ -242,7 +242,7 @@ public List<TX> remove(Map<Address, BigInteger> accNonce) { .forEach( bw -> { if (this.getMainMap().get(bw) != null) { - ITransaction tx = this.getMainMap().get(bw).getTx().clone(); + Transaction tx = this.getMainMap().get(bw).getTx().clone(); removedTxl.add((TX) tx); long timestamp = tx.getTimeStampBI().longValue() / multiplyM; @@ -300,7 +300,7 @@ public List<TX> remove(List<TX> txs) { if (LOG.isTraceEnabled()) { LOG.trace( "TxPoolA0.remove:[{}] nonce:[{}]", - ByteUtils.toHexString(tx.getTransactionHash()), + ByteUtil.toHexString(tx.getTransactionHash()), tx.getNonceBI().toString()); } @@ -477,7 +477,7 @@ public List<TX> snapshot() { if (dependTx == null || snapshotSet.contains(dependTx)) { boolean firstTx = true; for (ByteArrayWrapper bw : pair.getValue().getTxList()) { - ITransaction itx = this.getMainMap().get(bw).getTx(); + Transaction itx = this.getMainMap().get(bw).getTx(); cnt_txSz += itx.getEncoded().length; cnt_nrg += itx.getNrgConsume(); @@ -524,7 +524,7 @@ public List<TX> snapshot() { firstTx = true; for (ByteArrayWrapper bw : nonPickedTx.get(ancestor).getValue().getTxList()) { - ITransaction itx = this.getMainMap().get(bw).getTx(); + Transaction itx = this.getMainMap().get(bw).getTx(); cnt_txSz += itx.getEncoded().length; cnt_nrg += itx.getNrgConsume(); diff --git a/modTxPoolImpl/test/org/aion/txpool/test/TxnPoolTest.java b/modTxPoolImpl/test/org/aion/txpool/test/TxnPoolTest.java index 7bccb76400..aec6a7d886 100644 --- a/modTxPoolImpl/test/org/aion/txpool/test/TxnPoolTest.java +++ b/modTxPoolImpl/test/org/aion/txpool/test/TxnPoolTest.java @@ -12,19 +12,20 @@ import java.util.Map; import java.util.Properties; import java.util.Random; -import org.aion.base.type.AionAddress; -import org.aion.base.type.Hash256; -import org.aion.base.type.ITransaction; + +import org.aion.interfaces.tx.Transaction; +import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.crypto.ECKeyFac; import org.aion.txpool.ITxPool; import org.aion.txpool.zero.TxPoolA0; +import org.aion.types.Hash256; import org.aion.zero.types.AionTransaction; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.spongycastle.pqc.math.linearalgebra.ByteUtils; -import org.aion.vm.api.interfaces.Address; + public class TxnPoolTest { @@ -67,8 +68,8 @@ public void add1() { Properties config = new Properties(); config.put("tx-timeout", "100"); - ITxPool<ITransaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = getMockTransaction(); + ITxPool<Transaction> tp = new TxPoolA0<>(config); + List<Transaction> txnl = getMockTransaction(); ((AionTransaction) txnl.get(0)).sign(key.get(0)); tp.add(txnl); @@ -76,12 +77,12 @@ public void add1() { assertTrue(tp.size() == 1); } - private List<ITransaction> getMockTransaction() { + private List<Transaction> getMockTransaction() { return Collections.singletonList( new AionTransaction( ByteUtils.fromHexString("0000000000000001"), - AionAddress.wrap(key.get(0).getAddress()), - AionAddress.wrap( + Address.wrap(key.get(0).getAddress()), + Address.wrap( "0000000000000000000000000000000000000000000000000000000000000001"), ByteUtils.fromHexString("1"), ByteUtils.fromHexString("1"), @@ -94,8 +95,8 @@ public void remove() { Properties config = new Properties(); config.put("tx-timeout", "100"); - ITxPool<ITransaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = getMockTransaction(); + ITxPool<Transaction> tp = new TxPoolA0<>(config); + List<Transaction> txnl = getMockTransaction(); ((AionTransaction) txnl.get(0)).sign(key.get(0)); tp.add(txnl); assertTrue(tp.size() == 1); @@ -112,9 +113,9 @@ public void remove2() { Properties config = new Properties(); config.put("tx-timeout", "100"); // 100 sec - ITxPool<ITransaction> tp = new TxPoolA0<>(config); - List<ITransaction> txl = new ArrayList<>(); - List<ITransaction> txlrm = new ArrayList<>(); + ITxPool<Transaction> tp = new TxPoolA0<>(config); + List<Transaction> txl = new ArrayList<>(); + List<Transaction> txlrm = new ArrayList<>(); int cnt = 20; for (int i = 0; i < cnt; i++) { AionTransaction tx = @@ -143,9 +144,9 @@ public void remove3() { Properties config = new Properties(); config.put("tx-timeout", "100"); // 100 sec - ITxPool<ITransaction> tp = new TxPoolA0<>(config); - List<ITransaction> txl = new ArrayList<>(); - List<ITransaction> txlrm = new ArrayList<>(); + ITxPool<Transaction> tp = new TxPoolA0<>(config); + List<Transaction> txl = new ArrayList<>(); + List<Transaction> txlrm = new ArrayList<>(); int cnt = 20; for (int i = 0; i < cnt; i++) { AionTransaction tx = @@ -171,33 +172,33 @@ public void remove3() { assertTrue(tp.size() == 10); } - private ITransaction genTransaction(byte[] nonce) { + private Transaction genTransaction(byte[] nonce) { return new AionTransaction( nonce, - AionAddress.wrap(key.get(0).getAddress()), - AionAddress.wrap("0000000000000000000000000000000000000000000000000000000000000001"), + Address.wrap(key.get(0).getAddress()), + Address.wrap("0000000000000000000000000000000000000000000000000000000000000001"), ByteUtils.fromHexString("1"), ByteUtils.fromHexString("1"), 10000L, 1L); } - private ITransaction genTransaction(byte[] nonce, int _index) { + private Transaction genTransaction(byte[] nonce, int _index) { return new AionTransaction( nonce, - AionAddress.wrap(key.get(_index).getAddress()), - AionAddress.wrap("0000000000000000000000000000000000000000000000000000000000000001"), + Address.wrap(key.get(_index).getAddress()), + Address.wrap("0000000000000000000000000000000000000000000000000000000000000001"), ByteUtils.fromHexString("1"), ByteUtils.fromHexString("1"), 10000L, 1L); } - private ITransaction genTransactionRandomPrice(byte[] nonce, long price) { + private Transaction genTransactionRandomPrice(byte[] nonce, long price) { return new AionTransaction( nonce, - AionAddress.wrap(key.get(0).getAddress()), - AionAddress.wrap("0000000000000000000000000000000000000000000000000000000000000001"), + Address.wrap(key.get(0).getAddress()), + Address.wrap("0000000000000000000000000000000000000000000000000000000000000001"), ByteUtils.fromHexString("1"), ByteUtils.fromHexString("1"), 10000L, @@ -209,8 +210,8 @@ public void timeout1() throws Exception { Properties config = new Properties(); config.put("tx-timeout", "10"); // 10 sec - ITxPool<ITransaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = getMockTransaction(); + ITxPool<Transaction> tp = new TxPoolA0<>(config); + List<Transaction> txnl = getMockTransaction(); ((AionTransaction) txnl.get(0)).sign(key.get(0)); txnl.get(0).setNrgConsume(30000L); tp.add(txnl); @@ -226,8 +227,8 @@ public void timeout2() throws Exception { Properties config = new Properties(); config.put("tx-timeout", "1"); // 10 sec - ITxPool<ITransaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = getMockTransaction(); + ITxPool<Transaction> tp = new TxPoolA0<>(config); + List<Transaction> txnl = getMockTransaction(); ((AionTransaction) txnl.get(0)).sign(key.get(0)); txnl.get(0).setNrgConsume(30000L); tp.add(txnl); @@ -243,8 +244,8 @@ public void snapshot() { Properties config = new Properties(); config.put("tx-timeout", "10"); // 10 sec - ITxPool<ITransaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = getMockTransaction(); + ITxPool<Transaction> tp = new TxPoolA0<>(config); + List<Transaction> txnl = getMockTransaction(); ((AionTransaction) txnl.get(0)).sign(key.get(0)); tp.add(txnl); @@ -257,8 +258,8 @@ public void snapshot2() { Properties config = new Properties(); config.put("tx-timeout", "100"); // 100 sec - ITxPool<ITransaction> tp = new TxPoolA0<>(config); - List<ITransaction> txl = new ArrayList<>(); + ITxPool<Transaction> tp = new TxPoolA0<>(config); + List<Transaction> txl = new ArrayList<>(); int cnt = 26; for (int i = 0; i < cnt; i++) { AionTransaction txe = @@ -281,15 +282,15 @@ public void snapshot3() { Properties config = new Properties(); config.put("tx-timeout", "100"); - TxPoolA0<ITransaction> tp = new TxPoolA0<>(config); + TxPoolA0<Transaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); int cnt = 26; for (int i = 0; i < cnt; i++) { byte[] nonce = new byte[Long.BYTES]; nonce[Long.BYTES - 1] = (byte) i; - ITransaction txn = genTransaction(nonce); + Transaction txn = genTransaction(nonce); ((AionTransaction) txn).sign(key.get(0)); txn.setNrgConsume(i + 1); txnl.add(txn); @@ -298,10 +299,10 @@ public void snapshot3() { assertTrue(tp.size() == cnt); // sort the inserted txs - List<ITransaction> txl = tp.snapshot(); + List<Transaction> txl = tp.snapshot(); long nonce = 0; - for (ITransaction tx : txl) { + for (Transaction tx : txl) { assertTrue((new BigInteger(tx.getNonce())).longValue() == nonce++); } } @@ -311,15 +312,15 @@ public void snapshot4() { Properties config = new Properties(); config.put("tx-timeout", "100"); - TxPoolA0<ITransaction> tp = new TxPoolA0<>(config); + TxPoolA0<Transaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); int cnt = 26; for (int i = 0; i < cnt; i++) { byte[] nonce = new byte[Long.BYTES]; nonce[Long.BYTES - 1] = (byte) i; - ITransaction txn = genTransaction(nonce); + Transaction txn = genTransaction(nonce); ((AionTransaction) txn).sign(key.get(0)); txn.setNrgConsume(50 - i); txnl.add(txn); @@ -328,10 +329,10 @@ public void snapshot4() { assertTrue(tp.size() == cnt); // sort the inserted txs - List<ITransaction> txl = tp.snapshot(); + List<Transaction> txl = tp.snapshot(); long nonce = 0; - for (ITransaction tx : txl) { + for (Transaction tx : txl) { assertTrue((new BigInteger(tx.getNonce())).longValue() == nonce++); } } @@ -341,16 +342,16 @@ public void snapshot5() { Properties config = new Properties(); config.put("tx-timeout", "100"); - TxPoolA0<ITransaction> tp = new TxPoolA0<>(config); + TxPoolA0<Transaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); int cnt = 100; Random r = new Random(); for (int i = 0; i < cnt; i++) { byte[] nonce = new byte[Long.BYTES]; nonce[Long.BYTES - 1] = (byte) i; - ITransaction txn = genTransaction(nonce); + Transaction txn = genTransaction(nonce); ((AionTransaction) txn).sign(key.get(0)); txn.setNrgConsume(r.nextInt(1000)); txnl.add(txn); @@ -359,12 +360,12 @@ public void snapshot5() { assertTrue(tp.size() == cnt); // sort the inserted txs - List<ITransaction> txl = tp.snapshot(); + List<Transaction> txl = tp.snapshot(); assertTrue(tp.size() == txl.size()); assertTrue(tp.snapshotAll().size() == txl.size()); long nonce = 0; - for (ITransaction tx : txl) { + for (Transaction tx : txl) { assertTrue((new BigInteger(tx.getNonce())).longValue() == nonce++); } } @@ -374,16 +375,16 @@ public void snapshot6() { Properties config = new Properties(); config.put("tx-timeout", "100"); - TxPoolA0<ITransaction> tp = new TxPoolA0<>(config); + TxPoolA0<Transaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); int cnt = 200; Random r = new Random(); for (int i = 0; i < cnt; i++) { byte[] nonce = new byte[Long.BYTES]; nonce[Long.BYTES - 1] = (byte) i; - ITransaction txn = genTransactionRandomPrice(nonce, r.nextInt(1000)); + Transaction txn = genTransactionRandomPrice(nonce, r.nextInt(1000)); ((AionTransaction) txn).sign(key.get(0)); txn.setNrgConsume(r.nextInt(1000)); txnl.add(txn); @@ -392,12 +393,12 @@ public void snapshot6() { assertTrue(tp.size() == cnt); // sort the inserted txs - List<ITransaction> txl = tp.snapshot(); + List<Transaction> txl = tp.snapshot(); assertTrue(tp.size() == txl.size()); assertTrue(tp.snapshotAll().size() == txl.size()); long nonce = 0; - for (ITransaction tx : txl) { + for (Transaction tx : txl) { assertTrue((new BigInteger(tx.getNonce())).longValue() == nonce++); } } @@ -407,16 +408,16 @@ public void snapshot7() { Properties config = new Properties(); config.put("tx-timeout", "100"); - TxPoolA0<ITransaction> tp = new TxPoolA0<>(config); + TxPoolA0<Transaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); int cnt = 200; Random r = new Random(); for (int i = 0; i < cnt; i++) { byte[] nonce = new byte[Long.BYTES]; nonce[Long.BYTES - 1] = (byte) i; - ITransaction txn = genTransactionRandomPrice(nonce, r.nextInt(1000)); + Transaction txn = genTransactionRandomPrice(nonce, r.nextInt(1000)); ((AionTransaction) txn).sign(key.get(i < 100 ? 0 : 1)); txn.setNrgConsume(r.nextInt(1000)); @@ -426,12 +427,12 @@ public void snapshot7() { assertTrue(tp.size() == cnt); // sort the inserted txs - List<ITransaction> txl = tp.snapshot(); + List<Transaction> txl = tp.snapshot(); assertTrue(tp.size() == txl.size()); assertTrue(tp.snapshotAll().size() == txl.size()); long nonce = 0; - for (ITransaction tx : txl) { + for (Transaction tx : txl) { assertTrue((new BigInteger(tx.getNonce())).longValue() == nonce++); } } @@ -441,16 +442,16 @@ public void snapshot8() { Properties config = new Properties(); config.put("tx-timeout", "100"); - TxPoolA0<ITransaction> tp = new TxPoolA0<>(config); + TxPoolA0<Transaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); int cnt = 200; Random r = new Random(); for (int i = 0; i < cnt; i++) { byte[] nonce = new byte[Long.BYTES]; nonce[Long.BYTES - 1] = (byte) i; - ITransaction txn = genTransactionRandomPrice(nonce, r.nextInt(1000)); + Transaction txn = genTransactionRandomPrice(nonce, r.nextInt(1000)); ((AionTransaction) txn).sign(key.get(r.nextInt(10))); txn.setNrgConsume(r.nextInt(1000)); @@ -460,12 +461,12 @@ public void snapshot8() { assertTrue(tp.size() == cnt); // sort the inserted txs - List<ITransaction> txl = tp.snapshot(); + List<Transaction> txl = tp.snapshot(); assertTrue(tp.size() == txl.size()); assertTrue(tp.snapshotAll().size() == txl.size()); long nonce = 0; - for (ITransaction tx : txl) { + for (Transaction tx : txl) { assertTrue((new BigInteger(tx.getNonce())).longValue() == nonce++); } } @@ -475,15 +476,15 @@ public void snapshot9() { Properties config = new Properties(); config.put("tx-timeout", "100"); - TxPoolA0<ITransaction> tp = new TxPoolA0<>(config); + TxPoolA0<Transaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); int cnt = 25; // Random r = new Random(); for (int i = 0; i < cnt; i++) { byte[] nonce = new byte[Long.BYTES]; nonce[Long.BYTES - 1] = (byte) i; - ITransaction txn = genTransaction(nonce, 0); + Transaction txn = genTransaction(nonce, 0); ((AionTransaction) txn).sign(key.get(0)); txn.setNrgConsume(1); @@ -492,11 +493,11 @@ public void snapshot9() { tp.add(txnl); assertTrue(tp.size() == cnt); - List<ITransaction> txnl2 = new ArrayList<>(); + List<Transaction> txnl2 = new ArrayList<>(); for (int i = 0; i < cnt; i++) { byte[] nonce = new byte[Long.BYTES]; nonce[Long.BYTES - 1] = (byte) i; - ITransaction txn = genTransaction(nonce, 1); + Transaction txn = genTransaction(nonce, 1); ((AionTransaction) txn).sign(key.get(1)); txn.setNrgConsume(1); @@ -506,12 +507,12 @@ public void snapshot9() { assertTrue(tp.size() == cnt * 2); // sort the inserted txs - List<ITransaction> txl = tp.snapshot(); + List<Transaction> txl = tp.snapshot(); assertTrue(tp.size() == txl.size()); assertTrue(tp.snapshotAll().size() == txl.size()); int check = 0; - for (ITransaction tx : txl) { + for (Transaction tx : txl) { if (check < 25) { assertTrue( Hash256.wrap(txnl.get(check).getTransactionHash()) @@ -533,15 +534,15 @@ public void snapshot10() { Properties config = new Properties(); config.put("tx-timeout", "100"); - TxPoolA0<ITransaction> tp = new TxPoolA0<>(config); + TxPoolA0<Transaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); int cnt = 16; // Random r = new Random(); for (int i = 0; i < cnt; i++) { byte[] nonce = new byte[Long.BYTES]; nonce[Long.BYTES - 1] = (byte) i; - ITransaction txn = genTransaction(nonce, 0); + Transaction txn = genTransaction(nonce, 0); ((AionTransaction) txn).sign(key.get(0)); txn.setNrgConsume(1); @@ -551,7 +552,7 @@ public void snapshot10() { for (int i = 0; i < cnt; i++) { byte[] nonce = new byte[Long.BYTES]; nonce[Long.BYTES - 1] = (byte) i; - ITransaction txn = genTransaction(nonce, 1); + Transaction txn = genTransaction(nonce, 1); ((AionTransaction) txn).sign(key.get(1)); txn.setNrgConsume(1); @@ -561,7 +562,7 @@ public void snapshot10() { for (int i = 16; i < 16 + cnt; i++) { byte[] nonce = new byte[Long.BYTES]; nonce[Long.BYTES - 1] = (byte) i; - ITransaction txn = genTransaction(nonce, 0); + Transaction txn = genTransaction(nonce, 0); ((AionTransaction) txn).sign(key.get(0)); txn.setNrgConsume(1); @@ -571,7 +572,7 @@ public void snapshot10() { for (int i = 16; i < 16 + cnt; i++) { byte[] nonce = new byte[Long.BYTES]; nonce[Long.BYTES - 1] = (byte) i; - ITransaction txn = genTransaction(nonce, 1); + Transaction txn = genTransaction(nonce, 1); ((AionTransaction) txn).sign(key.get(1)); txn.setNrgConsume(1); @@ -582,12 +583,12 @@ public void snapshot10() { assertTrue(tp.size() == cnt * 4); // sort the inserted txs - List<ITransaction> txl = tp.snapshot(); + List<Transaction> txl = tp.snapshot(); assertTrue(tp.size() == txl.size()); assertTrue(tp.snapshotAll().size() == txl.size()); int check = 0; - for (ITransaction tx : txl) { + for (Transaction tx : txl) { assertTrue( Hash256.wrap(txnl.get(check).getTransactionHash()) .toString() @@ -601,12 +602,12 @@ public void addRepeatedTxn() { Properties config = new Properties(); config.put("tx-timeout", "10"); - ITxPool<ITransaction> tp = new TxPoolA0<>(config); - ITransaction txn = + ITxPool<Transaction> tp = new TxPoolA0<>(config); + Transaction txn = new AionTransaction( ByteUtils.fromHexString("0000000000000001"), - AionAddress.wrap(key.get(0).getAddress()), - AionAddress.wrap(key.get(0).getAddress()), + Address.wrap(key.get(0).getAddress()), + Address.wrap(key.get(0).getAddress()), ByteUtils.fromHexString("1"), ByteUtils.fromHexString("1"), 10000L, @@ -614,7 +615,7 @@ public void addRepeatedTxn() { ((AionTransaction) txn).sign(key.get(0)); - List<ITransaction> txnl = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); txnl.add(txn); txnl.add(txn); tp.add(txnl); @@ -627,15 +628,15 @@ public void addRepeatedTxn2() { Properties config = new Properties(); config.put("tx-timeout", "10"); - ITxPool<ITransaction> tp = new TxPoolA0<>(config); + ITxPool<Transaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); int cnt = 10; for (int i = 0; i < cnt; i++) { byte[] nonce = new byte[Long.BYTES]; nonce[Long.BYTES - 1] = (byte) i; - ITransaction txn = genTransaction(nonce); + Transaction txn = genTransaction(nonce); ((AionTransaction) txn).sign(key.get(0)); txn.setNrgConsume(50); txnl.add(txn); @@ -646,12 +647,12 @@ public void addRepeatedTxn2() { byte[] nonce = new byte[Long.BYTES]; nonce[Long.BYTES - 1] = (byte) 5; - ITransaction txn = genTransaction(nonce); + Transaction txn = genTransaction(nonce); ((AionTransaction) txn).sign(key.get(0)); txn.setNrgConsume(500); tp.add(txn); - List<ITransaction> snapshot = tp.snapshot(); + List<Transaction> snapshot = tp.snapshot(); assertTrue(snapshot.size() == cnt); assertTrue(snapshot.get(5).equals(txn)); @@ -662,15 +663,15 @@ public void addRepeatedTxn3() { Properties config = new Properties(); config.put("tx-timeout", "10"); - ITxPool<ITransaction> tp = new TxPoolA0<>(config); + ITxPool<Transaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); int cnt = 10; for (int i = 0; i < cnt; i++) { byte[] nonce = new byte[Long.BYTES]; nonce[Long.BYTES - 1] = (byte) i; - ITransaction txn = genTransaction(nonce); + Transaction txn = genTransaction(nonce); ((AionTransaction) txn).sign(key.get(0)); txn.setNrgConsume(50); txnl.add(txn); @@ -682,12 +683,12 @@ public void addRepeatedTxn3() { byte[] nonce = new byte[Long.BYTES]; nonce[Long.BYTES - 1] = (byte) 5; - ITransaction txn = genTransaction(nonce); + Transaction txn = genTransaction(nonce); ((AionTransaction) txn).sign(key.get(0)); txn.setNrgConsume(500); tp.add(txn); - List<ITransaction> snapshot = tp.snapshot(); + List<Transaction> snapshot = tp.snapshot(); assertTrue(snapshot.size() == cnt); assertTrue(snapshot.get(5).equals(txn)); @@ -698,12 +699,12 @@ public void addTxWithSameNonce() { Properties config = new Properties(); config.put("tx-timeout", "10"); - ITxPool<ITransaction> tp = new TxPoolA0<>(config); + ITxPool<Transaction> tp = new TxPoolA0<>(config); - ITransaction txn = genTransaction(ByteUtils.fromHexString("0000000000000001")); + Transaction txn = genTransaction(ByteUtils.fromHexString("0000000000000001")); txn.setNrgConsume(100); - List<ITransaction> txnl = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); ((AionTransaction) txn).sign(key.get(0)); txnl.add(txn); @@ -716,7 +717,7 @@ public void addTxWithSameNonce() { assertTrue(tp.size() == 1); - List<ITransaction> txl = tp.snapshot(); + List<Transaction> txl = tp.snapshot(); assertTrue(txl.size() == 1); assertTrue(new BigInteger(txl.get(0).getTimestamp()).longValue() == t); } @@ -726,20 +727,20 @@ public void noncebyAccountTest() { Properties config = new Properties(); config.put("tx-timeout", "10"); - TxPoolA0<ITransaction> tp = new TxPoolA0<>(config); + TxPoolA0<Transaction> tp = new TxPoolA0<>(config); - Address acc = AionAddress.wrap(key.get(0).getAddress()); + Address acc = Address.wrap(key.get(0).getAddress()); - List<ITransaction> txnl = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); int cnt = 100; for (int i = 0; i < cnt; i++) { byte[] nonce = new byte[Long.BYTES]; nonce[Long.BYTES - 1] = (byte) (i + 1); - ITransaction txn = + Transaction txn = new AionTransaction( nonce, acc, - AionAddress.wrap( + Address.wrap( "0000000000000000000000000000000000000000000000000000000000000001"), ByteUtils.fromHexString("1"), ByteUtils.fromHexString("1"), @@ -768,20 +769,20 @@ public void noncebyAccountTest2() { Properties config = new Properties(); config.put("tx-timeout", "10"); - TxPoolA0<ITransaction> tp = new TxPoolA0<>(config); + TxPoolA0<Transaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); int cnt = 100; for (ECKey aKey1 : key) { - Address acc = AionAddress.wrap(aKey1.getAddress()); + Address acc = Address.wrap(aKey1.getAddress()); for (int i = 0; i < cnt; i++) { byte[] nonce = new byte[Long.BYTES]; nonce[Long.BYTES - 1] = (byte) (i + 1); - ITransaction txn = + Transaction txn = new AionTransaction( nonce, acc, - AionAddress.wrap( + Address.wrap( "0000000000000000000000000000000000000000000000000000000000000001"), ByteUtils.fromHexString("1"), ByteUtils.fromHexString("1"), @@ -801,7 +802,7 @@ public void noncebyAccountTest2() { tp.snapshot(); for (ECKey aKey : key) { - List<BigInteger> nl = tp.getNonceList(AionAddress.wrap(aKey.getAddress())); + List<BigInteger> nl = tp.getNonceList(Address.wrap(aKey.getAddress())); for (int i = 0; i < cnt; i++) { assertTrue(nl.get(i).equals(BigInteger.valueOf(i + 1))); } @@ -813,20 +814,20 @@ public void feemapTest() { Properties config = new Properties(); config.put("tx-timeout", "10"); - TxPoolA0<ITransaction> tp = new TxPoolA0<>(config); + TxPoolA0<Transaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); int cnt = 100; byte[] nonce = new byte[Long.BYTES]; for (int i = 0; i < cnt; i++) { nonce[Long.BYTES - 1] = 1; - Address addr = AionAddress.wrap(key2.get(i).getAddress()); - ITransaction txn = + Address addr = Address.wrap(key2.get(i).getAddress()); + Transaction txn = new AionTransaction( nonce, addr, - AionAddress.wrap( + Address.wrap( "0000000000000000000000000000000000000000000000000000000000000001"), ByteUtils.fromHexString("1"), ByteUtils.fromHexString("1"), @@ -857,15 +858,15 @@ public void TxnfeeCombineTest() { Properties config = new Properties(); config.put("tx-timeout", "100"); - TxPoolA0<ITransaction> tp = new TxPoolA0<>(config); + TxPoolA0<Transaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); int cnt = 10; for (int i = 0; i < cnt; i++) { byte[] nonce = new byte[Long.BYTES]; nonce[Long.BYTES - 1] = (byte) (i + 1); - ITransaction txn = genTransaction(nonce); + Transaction txn = genTransaction(nonce); ((AionTransaction) txn).sign(key.get(0)); txn.setNrgConsume(i + 1); @@ -888,15 +889,15 @@ public void TxnfeeCombineTest2() { Properties config = new Properties(); config.put("tx-timeout", "100"); - TxPoolA0<ITransaction> tp = new TxPoolA0<>(config); + TxPoolA0<Transaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); int cnt = 17; for (int i = 0; i < cnt; i++) { byte[] nonce = new byte[Long.BYTES]; nonce[Long.BYTES - 1] = (byte) (i + 1); - ITransaction txn = genTransaction(nonce); + Transaction txn = genTransaction(nonce); ((AionTransaction) txn).sign(key.get(0)); txn.setNrgConsume(i + 1); txnl.add(txn); @@ -921,18 +922,18 @@ public void TxnfeeCombineTest2() { Properties config = new Properties(); config.put("tx-timeout", "100"); - TxPoolA0<ITransaction> tp = new TxPoolA0<>(config); + TxPoolA0<Transaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); int cnt = 10000; for (ECKey aKey1 : key) { - Address acc = AionAddress.wrap(aKey1.getAddress()); + Address acc = Address.wrap(aKey1.getAddress()); for (int i = 0; i < cnt; i++) { - ITransaction txn = + Transaction txn = new AionTransaction( BigInteger.valueOf(i).toByteArray(), acc, - AionAddress.wrap( + Address.wrap( "0000000000000000000000000000000000000000000000000000000000000001"), ByteUtils.fromHexString("1"), ByteUtils.fromHexString("1"), @@ -953,7 +954,7 @@ public void TxnfeeCombineTest2() { System.out.println("time spent: " + (System.currentTimeMillis() - start) + " ms."); for (ECKey aKey : key) { - List<BigInteger> nl = tp.getNonceList(AionAddress.wrap(aKey.getAddress())); + List<BigInteger> nl = tp.getNonceList(Address.wrap(aKey.getAddress())); for (int i = 0; i < cnt; i++) { assertTrue(nl.get(i).equals(BigInteger.valueOf(i))); } @@ -968,18 +969,18 @@ public void TxnfeeCombineTest2() { Properties config = new Properties(); config.put("tx-timeout", "100"); - TxPoolA0<ITransaction> tp = new TxPoolA0<>(config); + TxPoolA0<Transaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); int cnt = 10000; for (ECKey aKey2 : key) { - Address acc = AionAddress.wrap(aKey2.getAddress()); + Address acc = Address.wrap(aKey2.getAddress()); for (int i = 0; i < cnt; i++) { - ITransaction txn = + Transaction txn = new AionTransaction( BigInteger.valueOf(i).toByteArray(), acc, - AionAddress.wrap( + Address.wrap( "0000000000000000000000000000000000000000000000000000000000000001"), ByteUtils.fromHexString("1"), ByteUtils.fromHexString("1"), @@ -1003,11 +1004,11 @@ public void TxnfeeCombineTest2() { txnl.clear(); for (ECKey aKey1 : key) { for (int i = 0; i < cnt2; i++) { - ITransaction txn = + Transaction txn = new AionTransaction( BigInteger.valueOf(cnt + i).toByteArray(), - AionAddress.wrap(aKey1.getAddress()), - AionAddress.wrap( + Address.wrap(aKey1.getAddress()), + Address.wrap( "0000000000000000000000000000000000000000000000000000000000000001"), ByteUtils.fromHexString("1"), ByteUtils.fromHexString("1"), @@ -1027,7 +1028,7 @@ public void TxnfeeCombineTest2() { System.out.println("2nd time spent: " + (System.currentTimeMillis() - start) + " ms."); for (ECKey aKey : key) { - List<BigInteger> nl = tp.getNonceList(AionAddress.wrap(aKey.getAddress())); + List<BigInteger> nl = tp.getNonceList(Address.wrap(aKey.getAddress())); for (int i = 0; i < cnt + cnt2; i++) { assertTrue(nl.get(i).equals(BigInteger.valueOf(i))); } @@ -1044,20 +1045,20 @@ public void TxnfeeCombineTest2() { Properties config = new Properties(); config.put("tx-timeout", "100"); - TxPoolA0<ITransaction> tp = new TxPoolA0<>(config); + TxPoolA0<Transaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); int cnt = 100; System.out.println("Gen new transactions --"); long start = System.currentTimeMillis(); for (ECKey aKey21 : key2) { - Address acc = AionAddress.wrap(aKey21.getAddress()); + Address acc = Address.wrap(aKey21.getAddress()); for (int i = 0; i < cnt; i++) { - ITransaction txn = + Transaction txn = new AionTransaction( BigInteger.valueOf(i).toByteArray(), acc, - AionAddress.wrap( + Address.wrap( "0000000000000000000000000000000000000000000000000000000000000001"), ByteUtils.fromHexString("1"), ByteUtils.fromHexString("1"), @@ -1084,7 +1085,7 @@ public void TxnfeeCombineTest2() { System.out.println("time spent: " + (System.currentTimeMillis() - start) + " ms."); for (ECKey aKey2 : key2) { - List<BigInteger> nl = tp.getNonceList(AionAddress.wrap(aKey2.getAddress())); + List<BigInteger> nl = tp.getNonceList(Address.wrap(aKey2.getAddress())); for (int i = 0; i < cnt; i++) { assertTrue(nl.get(i).equals(BigInteger.valueOf(i))); } @@ -1097,21 +1098,21 @@ public void TxnfeeCombineTest2() { Properties config = new Properties(); config.put("tx-timeout", "100"); - TxPoolA0<ITransaction> tp = new TxPoolA0<>(config); + TxPoolA0<Transaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = new ArrayList<>(); - List<ITransaction> txnlrm = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); + List<Transaction> txnlrm = new ArrayList<>(); int cnt = 100000; int rmCnt = 10; - Address acc = AionAddress.wrap(key.get(0).getAddress()); + Address acc = Address.wrap(key.get(0).getAddress()); System.out.println("gen new transactions..."); long start = System.currentTimeMillis(); for (int i = 0; i < cnt; i++) { - ITransaction txn = + Transaction txn = new AionTransaction( BigInteger.valueOf(i).toByteArray(), acc, - AionAddress.wrap( + Address.wrap( "0000000000000000000000000000000000000000000000000000000000000001"), ByteUtils.fromHexString("1"), ByteUtils.fromHexString("1"), @@ -1151,7 +1152,7 @@ public void TxnfeeCombineTest2() { tp.snapshot(); System.out.println("time spent: " + (System.currentTimeMillis() - start) + " ms."); - List<BigInteger> nl = tp.getNonceList(AionAddress.wrap(key.get(0).getAddress())); + List<BigInteger> nl = tp.getNonceList(Address.wrap(key.get(0).getAddress())); for (int i = 0; i < nl.size(); i++) { assertTrue(nl.get(i).equals(BigInteger.valueOf(i).add(BigInteger.valueOf(rmCnt)))); } @@ -1165,18 +1166,18 @@ public void TxnfeeCombineTest2() { Properties config = new Properties(); config.put("tx-timeout", "100"); - TxPoolA0<ITransaction> tp = new TxPoolA0<>(config); + TxPoolA0<Transaction> tp = new TxPoolA0<>(config); - List<ITransaction> txnl = new ArrayList<>(); + List<Transaction> txnl = new ArrayList<>(); int cnt = 10000; for (ECKey aKey1 : key) { - Address acc = AionAddress.wrap(aKey1.getAddress()); + Address acc = Address.wrap(aKey1.getAddress()); for (int i = 0; i < cnt; i++) { - ITransaction txn = + Transaction txn = new AionTransaction( BigInteger.valueOf(i).toByteArray(), acc, - AionAddress.wrap( + Address.wrap( "0000000000000000000000000000000000000000000000000000000000000001"), ByteUtils.fromHexString("1"), ByteUtils.fromHexString("1"), @@ -1203,7 +1204,7 @@ public void TxnfeeCombineTest2() { System.out.println("2nd time spent: " + (System.currentTimeMillis() - start) + " ms."); for (ECKey aKey : key) { - List<BigInteger> nl = tp.getNonceList(AionAddress.wrap(aKey.getAddress())); + List<BigInteger> nl = tp.getNonceList(Address.wrap(aKey.getAddress())); for (int i = 0; i < cnt; i++) { assertTrue(nl.get(i).equals(BigInteger.valueOf(i))); } @@ -1220,8 +1221,8 @@ public void testSnapshotAll() { AionTransaction tx = new AionTransaction( BigInteger.valueOf(i).toByteArray(), - AionAddress.wrap(key.getAddress()), - AionAddress.wrap( + Address.wrap(key.getAddress()), + Address.wrap( "0000000000000000000000000000000000000000000000000000000000000001"), ByteUtils.fromHexString("1"), ByteUtils.fromHexString("1"), @@ -1254,8 +1255,8 @@ public void testSnapshotAll2() { AionTransaction tx = new AionTransaction( BigInteger.valueOf(i).toByteArray(), - AionAddress.wrap(key.getAddress()), - AionAddress.wrap( + Address.wrap(key.getAddress()), + Address.wrap( "0000000000000000000000000000000000000000000000000000000000000001"), ByteUtils.fromHexString("1"), ByteUtils.fromHexString("1"), @@ -1284,8 +1285,8 @@ public void testRemove2() { AionTransaction tx = new AionTransaction( BigInteger.valueOf(i).toByteArray(), - AionAddress.wrap(key.getAddress()), - AionAddress.wrap( + Address.wrap(key.getAddress()), + Address.wrap( "0000000000000000000000000000000000000000000000000000000000000001"), ByteUtils.fromHexString("1"), ByteUtils.fromHexString("1"), diff --git a/modUtil/build.gradle b/modUtil/build.gradle index ddf19dc71f..e2785e9fcb 100644 --- a/modUtil/build.gradle +++ b/modUtil/build.gradle @@ -1,6 +1,117 @@ ext.moduleName = 'aion.util' +// set the publish to true when the code ready to push the lib to the maven repo +def publish = false; + +apply plugin: 'maven' +apply plugin: 'signing' + +group = "network.aion" +archivesBaseName = "util4j" + +def getCommitHash = { -> + def hashStdOut = new ByteArrayOutputStream() + exec { + commandLine "sh", "-c", "git log --pretty=format:%h | head -1" + standardOutput = hashStdOut + } + + return hashStdOut.toString().trim() +} + dependencies { testCompile 'junit:junit:4.12' testCompile 'pl.pragmatists:JUnitParams:1.1.1' + testCompile 'com.google.truth:truth:0.42' + testCompile 'com.madgag.spongycastle:core:1.58.0.0' +} + +sourceSets { + + if (publish) { + version = "0.4.0" + } else { + jar.baseName = 'util4j-' + getCommitHash() + } + + main { + java.srcDirs = ['src/main/java'] + } + test { + java.srcDirs = ['src/test/java'] + } +} + +signing { + sign configurations.archives +} +signArchives.enabled = publish + + +task sourcesJar(type: Jar) { + classifier = 'sources' + from sourceSets.main.allSource +} +sourcesJar.enabled = publish + +javadoc { + source = sourceSets.main.allJava + classpath = configurations.compile + options.tags = ["implSpec"] +} + +task javadocJar(type: Jar) { + classifier = 'javadoc' + from javadoc +} +javadocJar.enabled = publish + +artifacts { + archives sourcesJar, javadocJar +} + +uploadArchives { + repositories { + mavenDeployer { + + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + pom.project { + name 'util4j' + packaging 'jar' + // optionally artifactId can be defined here + description 'a util module for the aion java kernel.' + url 'https://github.com/aionnetwork/aion/tree/master-pre-merge/modUtil' + + scm { + connection 'scm:git:https://github.com/aionnetwork/aion.git' + developerConnection 'git:https://github.com/aionnetwork/aion.git' + url 'https://github.com/aionnetwork/aion/tree/master' + } + + licenses { + license { + name 'MIT' + url 'https://opensource.org/licenses/MIT' + } + } + + developers { + developer { + id 'aion foundation dev' + name 'aion foundation dev' + email 'toengineering@aion.network' + } + } + } + } + } } +uploadArchives.enabled = publish + + + diff --git a/modUtil/src/main/java/module-info.java b/modUtil/src/main/java/module-info.java new file mode 100644 index 0000000000..6944582e10 --- /dev/null +++ b/modUtil/src/main/java/module-info.java @@ -0,0 +1,10 @@ +module aion.util { + exports org.aion.util.bytes; + exports org.aion.util.conversions; + exports org.aion.util.others; + exports org.aion.util.time; + exports org.aion.util.string; + exports org.aion.util.file; + exports org.aion.util.biginteger; + exports org.aion.util.map; +} diff --git a/modAionBase/src/org/aion/base/util/BIUtil.java b/modUtil/src/main/java/org/aion/util/biginteger/BIUtil.java similarity index 98% rename from modAionBase/src/org/aion/base/util/BIUtil.java rename to modUtil/src/main/java/org/aion/util/biginteger/BIUtil.java index 7ad7e5796e..d07c7b7240 100644 --- a/modAionBase/src/org/aion/base/util/BIUtil.java +++ b/modUtil/src/main/java/org/aion/util/biginteger/BIUtil.java @@ -1,4 +1,4 @@ -package org.aion.base.util; +package org.aion.util.biginteger; import java.math.BigInteger; diff --git a/modUtil/src/org/aion/util/bytes/ByteUtil.java b/modUtil/src/main/java/org/aion/util/bytes/ByteUtil.java similarity index 99% rename from modUtil/src/org/aion/util/bytes/ByteUtil.java rename to modUtil/src/main/java/org/aion/util/bytes/ByteUtil.java index 598863b3c6..15a32d8193 100644 --- a/modUtil/src/org/aion/util/bytes/ByteUtil.java +++ b/modUtil/src/main/java/org/aion/util/bytes/ByteUtil.java @@ -648,7 +648,7 @@ public static byte[] shortToBytes(short n) { /** * Converts string hex representation to data bytes Accepts following hex: - with or without 0x - * prefix - with no leading 0, like 0xabc -> 0x0abc + * prefix - with no leading 0, like 0xabc v.s. 0x0abc * * @param data String like '0xa5e..' or just 'a5e..' * @return decoded bytes array diff --git a/modUtil/src/org/aion/util/conversions/Hex.java b/modUtil/src/main/java/org/aion/util/conversions/Hex.java similarity index 93% rename from modUtil/src/org/aion/util/conversions/Hex.java rename to modUtil/src/main/java/org/aion/util/conversions/Hex.java index 858763f18d..88312ca5ad 100644 --- a/modUtil/src/org/aion/util/conversions/Hex.java +++ b/modUtil/src/main/java/org/aion/util/conversions/Hex.java @@ -3,6 +3,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.util.Arrays; /** Utility class for converting hex data to bytes and back again. */ public class Hex { @@ -45,7 +46,7 @@ public static byte[] encode(byte[] data, int off, int length) { * * @return the number of bytes produced. */ - public static int encode(byte[] data, OutputStream out) throws IOException { + public static int encode(byte[] data, OutputStream out) { return encoder.encode(data, 0, data.length, out); } @@ -54,8 +55,7 @@ public static int encode(byte[] data, OutputStream out) throws IOException { * * @return the number of bytes produced. */ - public static int encode(byte[] data, int off, int length, OutputStream out) - throws IOException { + public static int encode(byte[] data, int off, int length, OutputStream out) { return encoder.encode(data, off, length, out); } @@ -69,7 +69,7 @@ public static byte[] decode(byte[] data) { try { encoder.decode(data, 0, data.length, bOut); } catch (IOException e) { - System.err.println("Hex decode failed! " + data); + System.err.println("Hex decode failed! " + Arrays.toString(data)); return null; } diff --git a/modUtil/src/org/aion/util/conversions/HexEncoder.java b/modUtil/src/main/java/org/aion/util/conversions/HexEncoder.java similarity index 84% rename from modUtil/src/org/aion/util/conversions/HexEncoder.java rename to modUtil/src/main/java/org/aion/util/conversions/HexEncoder.java index 9abfd7d715..171e6fbf5e 100644 --- a/modUtil/src/org/aion/util/conversions/HexEncoder.java +++ b/modUtil/src/main/java/org/aion/util/conversions/HexEncoder.java @@ -6,7 +6,7 @@ /** A streaming Hex encoder. */ public class HexEncoder { - protected final byte[] encodingTable = { + private final static byte[] encodingTable = { (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', @@ -16,9 +16,9 @@ public class HexEncoder { /* * set up the decoding table. */ - protected final byte[] decodingTable = new byte[128]; + private final static byte[] decodingTable = new byte[128]; - protected void initialiseDecodingTable() { + private void initialiseDecodingTable() { for (int i = 0; i < decodingTable.length; i++) { decodingTable[i] = (byte) 0xff; } @@ -44,13 +44,13 @@ public HexEncoder() { * * @return the number of bytes produced. */ - public int encode(byte[] data, int off, int length, OutputStream out) { + public static int encode(byte[] data, int off, int length, OutputStream out) { for (int i = off; i < (off + length); i++) { int v = data[i] & 0xff; try { out.write(encodingTable[(v >>> 4)]); out.write(encodingTable[v & 0xf]); - } catch (Exception e) { + } catch (Exception ignored) { } } @@ -67,7 +67,7 @@ private static boolean ignore(char c) { * * @return the number of bytes produced. */ - public int decode(byte[] data, int off, int length, OutputStream out) throws IOException { + public static int decode(byte[] data, int off, int length, OutputStream out) throws IOException { byte b1, b2; int outLen = 0; @@ -78,7 +78,7 @@ public int decode(byte[] data, int off, int length, OutputStream out) throws IOE break; } - end--; + --end; } int i = off; @@ -101,11 +101,11 @@ public int decode(byte[] data, int off, int length, OutputStream out) throws IOE try { out.write((b1 << 4) | b2); - } catch (Exception e) { + } catch (Exception ignored) { } - outLen++; + ++outLen; } return outLen; @@ -117,7 +117,7 @@ public int decode(byte[] data, int off, int length, OutputStream out) throws IOE * * @return the number of bytes produced. */ - public int decode(String data, OutputStream out) throws IOException { + public static int decode(String data, OutputStream out) throws IOException { byte b1, b2; int length = 0; @@ -128,19 +128,19 @@ public int decode(String data, OutputStream out) throws IOException { break; } - end--; + --end; } int i = 0; while (i < end) { while (i < end && ignore(data.charAt(i))) { - i++; + ++i; } b1 = decodingTable[data.charAt(i++)]; while (i < end && ignore(data.charAt(i))) { - i++; + ++i; } b2 = decodingTable[data.charAt(i++)]; @@ -151,7 +151,7 @@ public int decode(String data, OutputStream out) throws IOException { out.write((b1 << 4) | b2); - length++; + ++length; } return length; diff --git a/modAionBase/src/org/aion/base/io/File.java b/modUtil/src/main/java/org/aion/util/file/File.java similarity index 96% rename from modAionBase/src/org/aion/base/io/File.java rename to modUtil/src/main/java/org/aion/util/file/File.java index 37e4b949cf..046795d2ca 100644 --- a/modAionBase/src/org/aion/base/io/File.java +++ b/modUtil/src/main/java/org/aion/util/file/File.java @@ -1,4 +1,4 @@ -package org.aion.base.io; +package org.aion.util.file; import java.nio.file.Path; import java.util.Arrays; diff --git a/modUtil/src/main/java/org/aion/util/file/NativeLoader.java b/modUtil/src/main/java/org/aion/util/file/NativeLoader.java new file mode 100644 index 0000000000..45c080676f --- /dev/null +++ b/modUtil/src/main/java/org/aion/util/file/NativeLoader.java @@ -0,0 +1,138 @@ +package org.aion.util.file; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Scanner; + +/** + * Native library loader. + * + * @author jin + */ +public class NativeLoader { + + /** + * Returns the current OS name. + * + * @return current system OS name. + */ + private static String getOS() { + String osName = System.getProperty("os.name").toLowerCase(); + if (osName.contains("win")) { + return "win"; + } else if (osName.contains("linux")) { + return "linux"; + } else if (osName.contains("mac")) { + return "mac"; + } else { + throw new RuntimeException("Unrecognized OS: " + osName); + } + } + + /** + * Builds a file path given a list of folder names. + * + * @param args list of folder names + * @return file object + */ + private static File buildPath(String... args) { + StringBuilder sb = new StringBuilder(); + for (String arg : args) { + sb.append(File.separator); + sb.append(arg); + } + + return sb.length() > 0 ? new File(sb.substring(1)) : new File("."); + } + + /** + * Loads library based on the file list in the given module folder. + * + * @param module module name + */ + public static void loadLibrary(String module) { + File dir = buildPath("native", getOS(), module); + + try (Scanner s = new Scanner(new File(dir, "file.list"))) { + while (s.hasNextLine()) { + String line = s.nextLine(); + + if (line.startsWith("/") || line.startsWith(".")) { // for debug + // purpose + // mainly + System.load(line); + } else { + System.load(new File(dir, line).getCanonicalPath()); + } + } + } catch (IOException e) { + throw new RuntimeException("Failed to load libraries for " + module, e); + } + } + + public static void loadLibraryFromJar(@SuppressWarnings("rawtypes") Class clz, String path) + throws IOException { + + if (!path.startsWith("/")) { + throw new IllegalArgumentException("The path has to be absolute (start with '/')."); + } + + // Obtain filename from path + String[] parts = path.split("/"); + String filename = (parts.length > 1) ? parts[parts.length - 1] : null; + + // Split filename to prexif and suffix (extension) + String prefix = ""; + String suffix = null; + if (filename != null) { + parts = filename.split("\\.", 2); + prefix = parts[0]; + suffix = (parts.length > 1) ? "." + parts[parts.length - 1] : null; + } + + // Check if the filename is okay + if (filename == null || prefix.length() < 3) { + throw new IllegalArgumentException( + "The filename has to be at least 3 characters long."); + } + + // Prepare temporary file + File temp = File.createTempFile(prefix, suffix); + temp.deleteOnExit(); + + if (!temp.exists()) { + throw new FileNotFoundException("File " + temp.getAbsolutePath() + " does not exist."); + } + + // Prepare buffer for data copying + byte[] buffer = new byte[1024]; + int readBytes; + + // Open and check input stream + InputStream is = clz.getResourceAsStream(path); + if (is == null) { + throw new FileNotFoundException("File " + path + " was not found inside JAR."); + } + + // Open output stream and copy data between source file in JAR and the + // temporary file + OutputStream os = new FileOutputStream(temp); + try { + while ((readBytes = is.read(buffer)) != -1) { + os.write(buffer, 0, readBytes); + } + } finally { + // If read/write fails, close streams safely before throwing an + // exception + os.close(); + is.close(); + } + + // Finally, load the library + System.load(temp.getAbsolutePath()); + } +} diff --git a/modAionBase/src/org/aion/base/util/AbstractMap.java b/modUtil/src/main/java/org/aion/util/map/AbstractMap.java similarity index 99% rename from modAionBase/src/org/aion/base/util/AbstractMap.java rename to modUtil/src/main/java/org/aion/util/map/AbstractMap.java index 6874fd1c2c..50d99117ff 100644 --- a/modAionBase/src/org/aion/base/util/AbstractMap.java +++ b/modUtil/src/main/java/org/aion/util/map/AbstractMap.java @@ -1,4 +1,4 @@ -package org.aion.base.util; +package org.aion.util.map; import java.util.AbstractCollection; import java.util.AbstractSet; diff --git a/modAionBase/src/org/aion/base/util/HashMap.java b/modUtil/src/main/java/org/aion/util/map/HashMap.java similarity index 99% rename from modAionBase/src/org/aion/base/util/HashMap.java rename to modUtil/src/main/java/org/aion/util/map/HashMap.java index a3f6547012..bc0923038b 100644 --- a/modAionBase/src/org/aion/base/util/HashMap.java +++ b/modUtil/src/main/java/org/aion/util/map/HashMap.java @@ -1,4 +1,4 @@ -package org.aion.base.util; +package org.aion.util.map; import java.io.IOException; import java.io.InvalidObjectException; @@ -51,7 +51,7 @@ public abstract class HashMap<K, V> extends AbstractMap<K, V> * ordered primarily by hashCode, but in the case of ties, if two * elements are of the same "class C implements Comparable<C>", * type then their compareTo method is used for ordering. (We - * conservatively check generic types via reflection to validate + * conservatively check generic type via reflection to validate * this -- see method comparableClassFor). The added complexity * of tree bins is worthwhile in providing worst-case O(log n) * operations when keys either have distinct hashes or are diff --git a/modAionBase/src/org/aion/base/util/MAF.java b/modUtil/src/main/java/org/aion/util/others/MAF.java similarity index 88% rename from modAionBase/src/org/aion/base/util/MAF.java rename to modUtil/src/main/java/org/aion/util/others/MAF.java index efdc5787f7..a7abf22605 100644 --- a/modAionBase/src/org/aion/base/util/MAF.java +++ b/modUtil/src/main/java/org/aion/util/others/MAF.java @@ -1,8 +1,8 @@ -package org.aion.base.util; +package org.aion.util.others; import java.util.ArrayDeque; import java.util.Queue; -/** @author: ali sharif Implements a simple Moving Average Filter This class is thread safe */ +/** @author ali sharif Implements a simple Moving Average Filter This class is thread safe */ public class MAF { // rationale for using ArrayDeque - doc: 'This class is likely to be faster than Stack when used // as a stack, @@ -38,7 +38,8 @@ public void add(double value) { count++; } if (count > window) { - total -= myQ.poll(); + Double d = myQ.poll(); + total -= d == null ? 0 : d; count--; } movingAvg = total / count; diff --git a/modUtil/src/main/java/org/aion/util/others/Utils.java b/modUtil/src/main/java/org/aion/util/others/Utils.java new file mode 100644 index 0000000000..2b869823b6 --- /dev/null +++ b/modUtil/src/main/java/org/aion/util/others/Utils.java @@ -0,0 +1,70 @@ +package org.aion.util.others; + +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Utils { + + public static final Object dummy = new Object(); + + private static final Pattern matchPattern = Pattern.compile("^([0-9]+)([a-zA-Z]+)$"); + public static final long KILO_BYTE = 1024; + public static final long MEGA_BYTE = 1048576; + public static final long GIGA_BYTE = 1073741824; + /** + * Matches file sizes based on fileSize string, in the format: [numericalValue][sizeDescriptor] + * + * <p>Examples of acceptable formats: + * 10b,10B,10K,10KB,10kB,10M,10mB,10MB,10G,10gB,10GB + * + * <p>Commas are <b>not</b> accepted by the parser, and are considered invalid. + * + * <p>Note: Anything beyond {@code gigaByte (GB, G, gB)} is not considered valid, and will be + * treated as a parse exception. + * + * <p>Note: this function assumes the binary representation of magnitudes, therefore 1kB + * (kiloByte) is not {@code 1000 bytes} but rather {@code 1024 bytes}. + * + * @param fileSize file size string + * @return {@code Optional.of(fileSizeInt)} if we were able to successfully decode the filesize + * string, otherwise outputs {@code Optional.empty()} indicating that we were unable to + * decode the file size string, this usually refers to some sort of syntactic error made by + * the user. + */ + public static Optional<Long> parseSize(String fileSize) { + Matcher m = matchPattern.matcher(fileSize); + // if anything does not match + if (!m.find()) { + return Optional.empty(); + } + + String numerical = m.group(1); + String sizeSuffix = m.group(2); + + long size = Integer.parseInt(numerical); + switch (sizeSuffix) { + case "B": + break; + case "K": + case "kB": + case "KB": + // process kiloByte (1024 * byte) here + size = size * KILO_BYTE; + break; + case "M": + case "mB": + case "MB": + size = size * MEGA_BYTE; + break; + case "G": + case "gB": + case "GB": + size = size * GIGA_BYTE; + break; + default: + return Optional.empty(); + } + return Optional.of(size); + } +} diff --git a/modUtil/src/main/java/org/aion/util/string/StringUtils.java b/modUtil/src/main/java/org/aion/util/string/StringUtils.java new file mode 100644 index 0000000000..61381db1a2 --- /dev/null +++ b/modUtil/src/main/java/org/aion/util/string/StringUtils.java @@ -0,0 +1,95 @@ +package org.aion.util.string; + +import java.math.BigInteger; +import java.util.Arrays; +import org.aion.util.conversions.Hex; + +public class StringUtils { + + /** Validate a passed hex string is a valid address */ + public static boolean isValidAddress(String address) { + if (address == null || address.isEmpty() || address.length() < 64) { + return false; + } + + if (address.startsWith("0x")) { + address = address.substring(2); + } + + // Will need to change this for a1, a2.... + if (address.startsWith("a0")) { + return address.length() == 64 && address.substring(2).matches("^[0-9A-Fa-f]+$"); + } else { + return false; + } + } + + public static String getNodeIdShort(String nodeId) { + return nodeId == null ? "<null>" : nodeId.substring(0, 8); + } + + public static String align(String s, char fillChar, int targetLen, boolean alignRight) { + if (targetLen <= s.length()) { + return s; + } + String alignString = repeat("" + fillChar, targetLen - s.length()); + return alignRight ? alignString + s : s + alignString; + } + + public static String repeat(String s, int n) { + if (s.length() == 1) { + byte[] bb = new byte[n]; + Arrays.fill(bb, s.getBytes()[0]); + return new String(bb); + } else { + StringBuilder ret = new StringBuilder(); + for (int i = 0; i < n; i++) { + ret.append(s); + } + return ret.toString(); + } + } + + public static BigInteger StringNumberAsBigInt(String input) { + if (input.startsWith("0x")) { + return StringHexToBigInteger(input); + } else { + return StringDecimalToBigInteger(input); + } + } + + public static BigInteger StringHexToBigInteger(String input) { + String hexa = input.startsWith("0x") ? input.substring(2) : input; + return new BigInteger(hexa, 16); + } + + private static BigInteger StringDecimalToBigInteger(String input) { + return new BigInteger(input); + } + + public static byte[] StringHexToByteArray(String x) { + if (x.startsWith("0x")) { + x = x.substring(2); + } + if (x.length() % 2 != 0) { + x = "0" + x; + } + return Hex.decode(x); + } + + public static String toJsonHex(byte[] x) { + return "0x" + Hex.toHexString(x); + } + + public static String toJsonHex(String x) { + return x.startsWith("0x") ? x : "0x" + x; + } + + public static String toJsonHex(long n) { + return "0x" + Long.toHexString(n); + } + + public static String toJsonHex(BigInteger n) { + return "0x" + n.toString(16); + } +} diff --git a/modAionBase/src/org/aion/base/util/TimeInstant.java b/modUtil/src/main/java/org/aion/util/time/TimeInstant.java similarity index 96% rename from modAionBase/src/org/aion/base/util/TimeInstant.java rename to modUtil/src/main/java/org/aion/util/time/TimeInstant.java index f1b63fdd5a..1c9b2c5771 100644 --- a/modAionBase/src/org/aion/base/util/TimeInstant.java +++ b/modUtil/src/main/java/org/aion/util/time/TimeInstant.java @@ -1,8 +1,8 @@ -package org.aion.base.util; +package org.aion.util.time; import java.time.Instant; -public final class TimeInstant { +public final class TimeInstant { private static Instant instant; public static final TimeInstant EPOCH = new TimeInstant(); diff --git a/modAionBase/src/org/aion/base/util/TimeUtils.java b/modUtil/src/main/java/org/aion/util/time/TimeUtils.java similarity index 68% rename from modAionBase/src/org/aion/base/util/TimeUtils.java rename to modUtil/src/main/java/org/aion/util/time/TimeUtils.java index cbfa2bc6b2..53fe3eb6f3 100644 --- a/modAionBase/src/org/aion/base/util/TimeUtils.java +++ b/modUtil/src/main/java/org/aion/util/time/TimeUtils.java @@ -1,8 +1,12 @@ -package org.aion.base.util; +package org.aion.util.time; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; public class TimeUtils { - /** + /* * Converts minutes to millis * * @param minutes time in minutes @@ -23,6 +27,18 @@ public static long secondsToMillis(long seconds) { } /** + * Return formatted Date String: yyyy.MM.dd HH:mm:ss Based on Unix's time() input in seconds + * + * @param timestamp seconds since start of Unix-time + * @return String formatted as - yyyy.MM.dd HH:mm:ss + */ + public static String longToDateTime(long timestamp) { + Date date = new Date(timestamp * 1000); + DateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); + return formatter.format(date); + } + + /* * Converts millis to minutes * * @param millis time in millis @@ -32,7 +48,7 @@ public static long secondsToMillis(long seconds) { // return Math.round(millis / 60.0 / 1000.0); // } - /** + /* * Converts millis to seconds * * @param millis time in millis @@ -42,7 +58,7 @@ public static long secondsToMillis(long seconds) { // return Math.round(millis / 1000.0); // } - /** + /* * Returns timestamp in the future after some millis passed from now * * @param millis millis count diff --git a/modUtil/src/module-info.java b/modUtil/src/module-info.java deleted file mode 100644 index 83b2a379e0..0000000000 --- a/modUtil/src/module-info.java +++ /dev/null @@ -1,5 +0,0 @@ -module aion.util { - exports org.aion.util; - exports org.aion.util.bytes; - exports org.aion.util.conversions; -} diff --git a/modUtil/src/org/aion/util/NativeLoader.java b/modUtil/src/org/aion/util/NativeLoader.java deleted file mode 100644 index 58e61d5505..0000000000 --- a/modUtil/src/org/aion/util/NativeLoader.java +++ /dev/null @@ -1,142 +0,0 @@ -package org.aion.util; - -import java.io.File; -import java.io.IOException; -import java.util.Scanner; - -// import java.io.FileNotFoundException; -// import java.io.FileOutputStream; -// import java.io.InputStream; -// import java.io.OutputStream; - -/** - * Native library loader. - * - * @author jin - */ -public class NativeLoader { - - /** - * Returns the current OS name. - * - * @return - */ - public static String getOS() { - String osName = System.getProperty("os.name").toLowerCase(); - if (osName.contains("win")) { - return "win"; - } else if (osName.contains("linux")) { - return "linux"; - } else if (osName.contains("mac")) { - return "mac"; - } else { - throw new RuntimeException("Unrecognized OS: " + osName); - } - } - - /** - * Builds a file path given a list of folder names. - * - * @param args - * @return - */ - public static File buildPath(String... args) { - StringBuilder sb = new StringBuilder(); - for (String arg : args) { - sb.append(File.separator); - sb.append(arg); - } - - return sb.length() > 0 ? new File(sb.substring(1)) : new File("."); - } - - /** - * Loads library based on the file list in the given module folder. - * - * @param module - */ - public static void loadLibrary(String module) { - File dir = buildPath("native", getOS(), module); - - try (Scanner s = new Scanner(new File(dir, "file.list"))) { - while (s.hasNextLine()) { - String line = s.nextLine(); - - if (line.startsWith("/") || line.startsWith(".")) { // for debug - // purpose - // mainly - System.load(line); - } else { - System.load(new File(dir, line).getCanonicalPath()); - } - } - } catch (IOException e) { - throw new RuntimeException("Failed to load libraries for " + module, e); - } - } - - // public static void loadLibraryFromJar(@SuppressWarnings("rawtypes") Class clz, String - // path) - // throws IOException { - // - // if (!path.startsWith("/")) { - // throw new IllegalArgumentException("The path has to be absolute (start with - // '/')."); - // } - // - // // Obtain filename from path - // String[] parts = path.split("/"); - // String filename = (parts.length > 1) ? parts[parts.length - 1] : null; - // - // // Split filename to prexif and suffix (extension) - // String prefix = ""; - // String suffix = null; - // if (filename != null) { - // parts = filename.split("\\.", 2); - // prefix = parts[0]; - // suffix = (parts.length > 1) ? "." + parts[parts.length - 1] : null; - // } - // - // // Check if the filename is okay - // if (filename == null || prefix.length() < 3) { - // throw new IllegalArgumentException( - // "The filename has to be at least 3 characters long."); - // } - // - // // Prepare temporary file - // File temp = File.createTempFile(prefix, suffix); - // temp.deleteOnExit(); - // - // if (!temp.exists()) { - // throw new FileNotFoundException("File " + temp.getAbsolutePath() + " does not - // exist."); - // } - // - // // Prepare buffer for data copying - // byte[] buffer = new byte[1024]; - // int readBytes; - // - // // Open and check input stream - // InputStream is = clz.getResourceAsStream(path); - // if (is == null) { - // throw new FileNotFoundException("File " + path + " was not found inside JAR."); - // } - // - // // Open output stream and copy data between source file in JAR and the - // // temporary file - // OutputStream os = new FileOutputStream(temp); - // try { - // while ((readBytes = is.read(buffer)) != -1) { - // os.write(buffer, 0, readBytes); - // } - // } finally { - // // If read/write fails, close streams safely before throwing an - // // exception - // os.close(); - // is.close(); - // } - // - // // Finally, load the library - // System.load(temp.getAbsolutePath()); - // } -} diff --git a/modAionBase/test/org/aion/base/util/AddressValidationTest.java b/modUtil/src/test/java/org/aion/util/bytes/AddressValidationTest.java similarity index 67% rename from modAionBase/test/org/aion/base/util/AddressValidationTest.java rename to modUtil/src/test/java/org/aion/util/bytes/AddressValidationTest.java index 601d6ff532..59a6498751 100644 --- a/modAionBase/test/org/aion/base/util/AddressValidationTest.java +++ b/modUtil/src/test/java/org/aion/util/bytes/AddressValidationTest.java @@ -1,7 +1,7 @@ -package org.aion.base.util; - -import static org.junit.Assert.assertEquals; +package org.aion.util.bytes; +import org.aion.util.string.StringUtils; +import org.junit.Assert; import org.junit.Test; public class AddressValidationTest { @@ -10,45 +10,45 @@ public class AddressValidationTest { public void allZeroPrefix() { String addr = "0x0000000000000000000000000000000000000000000000000000000000000000"; - assertEquals(false, Utils.isValidAddress(addr)); + Assert.assertFalse(StringUtils.isValidAddress(addr)); } @Test public void allZeroNoPrefix() { String addr = "0000000000000000000000000000000000000000000000000000000000000000"; - assertEquals(false, Utils.isValidAddress(addr)); + Assert.assertFalse(StringUtils.isValidAddress(addr)); } @Test public void burnPrefix() { String addr = "0xa000000000000000000000000000000000000000000000000000000000000000"; - assertEquals(true, Utils.isValidAddress(addr)); + Assert.assertTrue(StringUtils.isValidAddress(addr)); } @Test public void burnNoPrefix() { String addr = "a000000000000000000000000000000000000000000000000000000000000000"; - assertEquals(true, Utils.isValidAddress(addr)); + Assert.assertTrue(StringUtils.isValidAddress(addr)); } @Test public void previouslyGeneratedPrefix() { String addr = "0xa0ad207b4ae29a4e6219a8a8a1a82310de491194a33bd95907515a3c2196f357"; - assertEquals(true, Utils.isValidAddress(addr)); + Assert.assertTrue(StringUtils.isValidAddress(addr)); } @Test public void previouslyGeneratedNoPrefix() { String addr = "a0ad207b4ae29a4e6219a8a8a1a82310de491194a33bd95907515a3c2196f357"; - assertEquals(true, Utils.isValidAddress(addr)); + Assert.assertTrue(StringUtils.isValidAddress(addr)); } @Test public void incorrectLength() { String addr = "a0ad207b4ae29a4e6219a8a8a1a82310de491194a33bd95907515a3c2196f3"; - assertEquals(false, Utils.isValidAddress(addr)); + Assert.assertFalse(StringUtils.isValidAddress(addr)); } } diff --git a/modUtil/src/test/java/org/aion/util/bytes/BIUtilTest.java b/modUtil/src/test/java/org/aion/util/bytes/BIUtilTest.java new file mode 100644 index 0000000000..3683efc340 --- /dev/null +++ b/modUtil/src/test/java/org/aion/util/bytes/BIUtilTest.java @@ -0,0 +1,155 @@ +package org.aion.util.bytes; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.math.BigInteger; +import org.aion.util.biginteger.BIUtil; +import org.junit.Assert; +import org.junit.Test; + +public class BIUtilTest { + + private final BigInteger[][] bigInt = { + {new BigInteger("-00000000000000000000"), new BigInteger("00000000000000000000")}, + {new BigInteger("-00000000000000000001"), new BigInteger("00000000000000000001")}, + {new BigInteger("-10000000000000000000"), new BigInteger("10000000000000000000")}, + {new BigInteger("-20000000000000000000"), new BigInteger("20000000000000000000")}, + {new BigInteger("-30000000000000000000"), new BigInteger("30000000000000000000")}, + {new BigInteger("-99999999999999999999"), new BigInteger("99999999999999999999")}, + }; + + @Test + public void testIntegrity() { + + // isZero && isPositive + assertTrue(BIUtil.isZero(bigInt[0][0])); + assertTrue(BIUtil.isZero(bigInt[0][1])); + + assertFalse(BIUtil.isPositive(bigInt[0][0])); + assertFalse(BIUtil.isPositive(bigInt[0][1])); + + // isEqual && isNotEqual + assertTrue(BIUtil.isEqual(bigInt[0][0], bigInt[0][1])); + assertFalse(BIUtil.isNotEqual(bigInt[0][0], bigInt[0][1])); + + // isLessThan && isMoreThan + assertFalse(BIUtil.isLessThan(bigInt[0][0], bigInt[0][1])); + assertFalse(BIUtil.isMoreThan(bigInt[0][0], bigInt[0][1])); + + for (int a = 1; a < bigInt.length; a++) { + + assertFalse(BIUtil.isPositive(bigInt[a][0])); + assertTrue(BIUtil.isPositive(bigInt[a][1])); + + assertFalse(BIUtil.isEqual(bigInt[a][0], bigInt[a][1])); + assertTrue(BIUtil.isNotEqual(bigInt[a][0], bigInt[a][1])); + + assertTrue(BIUtil.isLessThan(bigInt[a][0], bigInt[a][1])); + assertFalse(BIUtil.isMoreThan(bigInt[a][0], bigInt[a][1])); + } + + // isCovers && isNotCovers + for (int a = 1; a < bigInt.length; a++) { + assertTrue(BIUtil.isNotCovers(bigInt[a - 1][1], bigInt[a][1])); + } + for (int a = 1; a < bigInt.length; a++) { + assertTrue(BIUtil.isNotCovers(bigInt[a][0], bigInt[a - 1][0])); + } + for (int a = bigInt.length - 1; a > 0; a--) { + assertTrue(BIUtil.isCovers(bigInt[a][1], bigInt[a - 1][1])); + } + + for (int a = bigInt.length - 1; a > 0; a--) { + assertTrue(BIUtil.isCovers(bigInt[a - 1][0], bigInt[a][0])); + } + + // isIn20PercentRange + } + + @Test + public void testType() { + + // toBI(byte), toBI(long) + final long[] testLong = { + 0L, 1L, 1000000000000000000L, 9223372036854775807L, + }; + + final byte[][] testByte = { + ByteUtil.longToBytes(testLong[0]), + ByteUtil.longToBytes(testLong[1]), + ByteUtil.longToBytes(testLong[2]), + ByteUtil.longToBytes(testLong[3]), + }; + + for (int i = 0; i < 4; i++) { + Assert.assertEquals(BIUtil.toBI(testLong[i]), BIUtil.toBI(testByte[i])); + } + + // exitLong + } + + @Test + public void testSum() { + + // sum + for (BigInteger[] bigIntegers : bigInt) { + Assert.assertEquals(new BigInteger("0"), BIUtil.sum(bigIntegers[0], bigIntegers[1])); + } + + for (int b = 0; b < 2; b++) { + for (BigInteger[] bigIntegers : bigInt) { + Assert.assertEquals(bigIntegers[b], BIUtil.sum(bigInt[0][b], bigIntegers[b])); + } + } + + Assert.assertEquals( + new BigInteger("-160000000000000000000"), + BIUtil.sum( + bigInt[0][0], + BIUtil.sum( + bigInt[1][0], + BIUtil.sum( + bigInt[2][0], + BIUtil.sum(bigInt[3][0], BIUtil.sum(bigInt[4][0], bigInt[5][0])))))); + + Assert.assertEquals( + new BigInteger("160000000000000000000"), + BIUtil.sum( + bigInt[0][1], + BIUtil.sum( + bigInt[1][1], + BIUtil.sum( + bigInt[2][1], + BIUtil.sum(bigInt[3][1], BIUtil.sum(bigInt[4][1], bigInt[5][1])))))); + } + + @Test + public void testMinMax() { + // min && max + for (BigInteger[] bigIntegers : bigInt) { + Assert.assertEquals(bigIntegers[0], BIUtil.min(bigIntegers[0], bigIntegers[1])); + Assert.assertEquals(bigIntegers[1], BIUtil.max(bigIntegers[0], bigIntegers[1])); + } + + Assert.assertEquals( + bigInt[bigInt.length - 1][0], + BIUtil.min( + bigInt[0][0], + BIUtil.min( + bigInt[1][0], + BIUtil.min( + bigInt[2][0], + BIUtil.min(bigInt[3][0], BIUtil.min(bigInt[4][0], bigInt[5][0])))))); + + Assert.assertEquals( + bigInt[bigInt.length - 1][1], + BIUtil.max( + bigInt[0][1], + BIUtil.max( + bigInt[1][1], + BIUtil.max( + bigInt[2][1], + BIUtil.max(bigInt[3][1], BIUtil.max(bigInt[4][1], bigInt[5][1])))))); + } +} diff --git a/modUtil/src/test/java/org/aion/util/bytes/ByteUtilExtendTest.java b/modUtil/src/test/java/org/aion/util/bytes/ByteUtilExtendTest.java new file mode 100644 index 0000000000..8ce1a29bf8 --- /dev/null +++ b/modUtil/src/test/java/org/aion/util/bytes/ByteUtilExtendTest.java @@ -0,0 +1,483 @@ +package org.aion.util.bytes; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.aion.util.conversions.Hex; +import org.junit.Assert; +import org.junit.Test; +import org.spongycastle.util.BigIntegers; + +/** TODO: functionality should be moved to modUtil */ +public class ByteUtilExtendTest { + + @Test + public void testAppendByte() { + byte[] bytes = "tes".getBytes(); + byte b = 0x74; + Assert.assertArrayEquals("test".getBytes(), ByteUtil.appendByte(bytes, b)); + } + + @Test + public void testBigIntegerToBytes() { + byte[] expecteds = new byte[] {(byte) 0xff, (byte) 0xec, 0x78}; + BigInteger b = BigInteger.valueOf(16772216); + byte[] actuals = ByteUtil.bigIntegerToBytes(b); + assertArrayEquals(expecteds, actuals); + } + + @Test + public void testBigIntegerToBytesSign() { + { + BigInteger b = BigInteger.valueOf(-2); + byte[] actuals = ByteUtil.bigIntegerToBytesSigned(b, 8); + assertArrayEquals(Hex.decode("fffffffffffffffe"), actuals); + } + { + BigInteger b = BigInteger.valueOf(2); + byte[] actuals = ByteUtil.bigIntegerToBytesSigned(b, 8); + assertArrayEquals(Hex.decode("0000000000000002"), actuals); + } + { + BigInteger b = BigInteger.valueOf(0); + byte[] actuals = ByteUtil.bigIntegerToBytesSigned(b, 8); + assertArrayEquals(Hex.decode("0000000000000000"), actuals); + } + { + BigInteger b = new BigInteger("eeeeeeeeeeeeee", 16); + byte[] actuals = ByteUtil.bigIntegerToBytesSigned(b, 8); + assertArrayEquals(Hex.decode("00eeeeeeeeeeeeee"), actuals); + } + { + BigInteger b = new BigInteger("eeeeeeeeeeeeeeee", 16); + byte[] actuals = ByteUtil.bigIntegerToBytesSigned(b, 8); + assertArrayEquals(Hex.decode("eeeeeeeeeeeeeeee"), actuals); + } + } + + @Test + public void testBigIntegerToBytesNegative() { + byte[] expecteds = new byte[] {(byte) 0xff, 0x0, 0x13, (byte) 0x88}; + BigInteger b = BigInteger.valueOf(-16772216); + byte[] actuals = ByteUtil.bigIntegerToBytes(b); + assertArrayEquals(expecteds, actuals); + } + + @Test + public void testBigIntegerToBytesZero() { + byte[] expecteds = new byte[] {0x00}; + BigInteger b = BigInteger.ZERO; + byte[] actuals = ByteUtil.bigIntegerToBytes(b); + assertArrayEquals(expecteds, actuals); + } + + @Test + public void testToHexString() { + assertEquals("", ByteUtil.toHexString(null)); + } + + @Test + public void testCalcPacketLength() { + byte[] test = new byte[] {0x0f, 0x10, 0x43}; + byte[] expected = new byte[] {0x00, 0x00, 0x00, 0x03}; + assertArrayEquals(expected, ByteUtil.calcPacketLength(test)); + } + + @Test + public void testByteArrayToInt() { + assertEquals(0, ByteUtil.byteArrayToInt(null)); + assertEquals(0, ByteUtil.byteArrayToInt(new byte[0])); + + // byte[] x = new byte[] { 5,1,7,0,8 }; + // long start = System.currentTimeMillis(); + // for (int i = 0; i < 100000000; i++) { + // ByteArray.read32bit(x, 0); + // } + // long end = System.currentTimeMillis(); + // System.out.println(end - start + "ms"); + // + // long start1 = System.currentTimeMillis(); + // for (int i = 0; i < 100000000; i++) { + // new BigInteger(1, x).intValue(); + // } + // long end1 = System.currentTimeMillis(); + // System.out.println(end1 - start1 + "ms"); + } + + @Test + public void testNumBytes() { + String test1 = "0"; + String test2 = "1"; + String test3 = "1000000000"; // 3B9ACA00 + int expected1 = 1; + int expected2 = 1; + int expected3 = 4; + assertEquals(expected1, ByteUtil.numBytes(test1)); + assertEquals(expected2, ByteUtil.numBytes(test2)); + assertEquals(expected3, ByteUtil.numBytes(test3)); + } + + @Test + public void testStripLeadingZeroes() { + byte[] test1 = null; + byte[] test2 = new byte[] {}; + byte[] test3 = new byte[] {0x00}; + byte[] test4 = new byte[] {0x00, 0x01}; + byte[] test5 = new byte[] {0x00, 0x00, 0x01}; + byte[] expected1 = null; + byte[] expected2 = new byte[] {0}; + byte[] expected3 = new byte[] {0}; + byte[] expected4 = new byte[] {0x01}; + byte[] expected5 = new byte[] {0x01}; + assertArrayEquals(expected1, ByteUtil.stripLeadingZeroes(test1)); + assertArrayEquals(expected2, ByteUtil.stripLeadingZeroes(test2)); + assertArrayEquals(expected3, ByteUtil.stripLeadingZeroes(test3)); + assertArrayEquals(expected4, ByteUtil.stripLeadingZeroes(test4)); + assertArrayEquals(expected5, ByteUtil.stripLeadingZeroes(test5)); + } + + @Test + public void testMatchingNibbleLength1() { + // a larger than b + byte[] a = new byte[] {0x00, 0x01}; + byte[] b = new byte[] {0x00}; + int result = ByteUtil.matchingNibbleLength(a, b); + assertEquals(1, result); + } + + @Test + public void testMatchingNibbleLength2() { + // b larger than a + byte[] a = new byte[] {0x00}; + byte[] b = new byte[] {0x00, 0x01}; + int result = ByteUtil.matchingNibbleLength(a, b); + assertEquals(1, result); + } + + @Test + public void testMatchingNibbleLength3() { + // a and b the same length equal + byte[] a = new byte[] {0x00}; + byte[] b = new byte[] {0x00}; + int result = ByteUtil.matchingNibbleLength(a, b); + assertEquals(1, result); + } + + @Test + public void testMatchingNibbleLength4() { + // a and b the same length not equal + byte[] a = new byte[] {0x01}; + byte[] b = new byte[] {0x00}; + int result = ByteUtil.matchingNibbleLength(a, b); + assertEquals(0, result); + } + + @Test + public void testNiceNiblesOutput_1() { + byte[] test = {7, 0, 7, 5, 7, 0, 7, 0, 7, 9}; + String result = "\\x07\\x00\\x07\\x05\\x07\\x00\\x07\\x00\\x07\\x09"; + assertEquals(result, ByteUtil.nibblesToPrettyString(test)); + } + + @Test + public void testNiceNiblesOutput_2() { + byte[] test = {7, 0, 7, 0xf, 7, 0, 0xa, 0, 7, 9}; + String result = "\\x07\\x00\\x07\\x0f\\x07\\x00\\x0a\\x00\\x07\\x09"; + assertEquals(result, ByteUtil.nibblesToPrettyString(test)); + } + + @Test(expected = NullPointerException.class) + public void testMatchingNibbleLength5() { + // a == null + byte[] a = null; + byte[] b = new byte[] {0x00}; + ByteUtil.matchingNibbleLength(a, b); + } + + @Test(expected = NullPointerException.class) + public void testMatchingNibbleLength6() { + // b == null + byte[] a = new byte[] {0x00}; + byte[] b = null; + ByteUtil.matchingNibbleLength(a, b); + } + + @Test + public void testMatchingNibbleLength7() { + // a or b is empty + byte[] a = new byte[0]; + byte[] b = new byte[] {0x00}; + int result = ByteUtil.matchingNibbleLength(a, b); + assertEquals(0, result); + } + + /** + * This test shows the difference between iterating over, and comparing byte[] vs BigInteger + * value. + * + * <p>Results indicate that the former has ~15x better performance. Therefore this is used in + * the Miner.mine() method. + */ + @Test + public void testIncrementPerformance() { + boolean testEnabled = false; + + if (testEnabled) { + byte[] counter1 = new byte[4]; + byte[] max = ByteBuffer.allocate(4).putInt(Integer.MAX_VALUE).array(); + long start1 = System.currentTimeMillis(); + while (ByteUtil.increment(counter1)) { + if (compareTo(counter1, 0, 4, max, 0, 4) == 0) { + break; + } + } + System.out.println( + System.currentTimeMillis() + - start1 + + "ms to reach: " + + Hex.toHexString(counter1)); + + BigInteger counter2 = BigInteger.ZERO; + long start2 = System.currentTimeMillis(); + while (true) { + if (counter2.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) == 0) { + break; + } + counter2 = counter2.add(BigInteger.ONE); + } + System.out.println( + System.currentTimeMillis() + - start2 + + "ms to reach: " + + Hex.toHexString(BigIntegers.asUnsignedByteArray(4, counter2))); + } + } + + /** Compares two regions of byte array. */ + public static int compareTo( + byte[] array1, int offset1, int size1, byte[] array2, int offset2, int size2) { + byte[] b1 = Arrays.copyOfRange(array1, offset1, offset1 + size1); + byte[] b2 = Arrays.copyOfRange(array2, offset2, offset2 + size2); + + return Arrays.compare(b1, b2); + } + + @Test + public void firstNonZeroByte_1() { + + byte[] data = + Hex.decode("0000000000000000000000000000000000000000000000000000000000000000"); + int result = ByteUtil.firstNonZeroByte(data); + + assertEquals(-1, result); + } + + @Test + public void firstNonZeroByte_2() { + + byte[] data = + Hex.decode("0000000000000000000000000000000000000000000000000000000000332211"); + int result = ByteUtil.firstNonZeroByte(data); + + assertEquals(29, result); + } + + @Test + public void firstNonZeroByte_3() { + + byte[] data = + Hex.decode("2211009988776655443322110099887766554433221100998877665544332211"); + int result = ByteUtil.firstNonZeroByte(data); + + assertEquals(0, result); + } + + @Test + public void setBitTest() { + /* + Set on + */ + byte[] data = ByteBuffer.allocate(4).putInt(0).array(); + int posBit = 24; + int expected = 16777216; + int result = -1; + byte[] ret = ByteUtil.setBit(data, posBit, 1); + result = ByteUtil.byteArrayToInt(ret); + assertTrue(expected == result); + + posBit = 25; + expected = 50331648; + ret = ByteUtil.setBit(data, posBit, 1); + result = ByteUtil.byteArrayToInt(ret); + assertTrue(expected == result); + + posBit = 2; + expected = 50331652; + ret = ByteUtil.setBit(data, posBit, 1); + result = ByteUtil.byteArrayToInt(ret); + assertTrue(expected == result); + + /* + Set off + */ + posBit = 24; + expected = 33554436; + ret = ByteUtil.setBit(data, posBit, 0); + result = ByteUtil.byteArrayToInt(ret); + assertTrue(expected == result); + + posBit = 25; + expected = 4; + ret = ByteUtil.setBit(data, posBit, 0); + result = ByteUtil.byteArrayToInt(ret); + assertTrue(expected == result); + + posBit = 2; + expected = 0; + ret = ByteUtil.setBit(data, posBit, 0); + result = ByteUtil.byteArrayToInt(ret); + assertTrue(expected == result); + } + + @Test + public void getBitTest() { + byte[] data = ByteBuffer.allocate(4).putInt(0).array(); + ByteUtil.setBit(data, 24, 1); + ByteUtil.setBit(data, 25, 1); + ByteUtil.setBit(data, 2, 1); + + List<Integer> found = new ArrayList<>(); + for (int i = 0; i < (data.length * 8); i++) { + int res = ByteUtil.getBit(data, i); + if (res == 1) { + if (i != 24 && i != 25 && i != 2) { + assertTrue(false); + } else { + found.add(i); + } + } else { + if (i == 24 || i == 25 || i == 2) { + assertTrue(false); + } + } + } + + if (found.size() != 3) { + assertTrue(false); + } + assertTrue(found.get(0) == 2); + assertTrue(found.get(1) == 24); + assertTrue(found.get(2) == 25); + } + + @Test + public void numToBytesTest() { + byte[] bytes = ByteUtil.intToBytesNoLeadZeroes(-1); + assertArrayEquals(bytes, Hex.decode("ffffffff")); + bytes = ByteUtil.intToBytesNoLeadZeroes(1); + assertArrayEquals(bytes, Hex.decode("01")); + bytes = ByteUtil.intToBytesNoLeadZeroes(255); + assertArrayEquals(bytes, Hex.decode("ff")); + bytes = ByteUtil.intToBytesNoLeadZeroes(256); + assertArrayEquals(bytes, Hex.decode("0100")); + bytes = ByteUtil.intToBytesNoLeadZeroes(0); + assertArrayEquals(bytes, new byte[0]); + + bytes = ByteUtil.intToBytes(-1); + assertArrayEquals(bytes, Hex.decode("ffffffff")); + bytes = ByteUtil.intToBytes(1); + assertArrayEquals(bytes, Hex.decode("00000001")); + bytes = ByteUtil.intToBytes(255); + assertArrayEquals(bytes, Hex.decode("000000ff")); + bytes = ByteUtil.intToBytes(256); + assertArrayEquals(bytes, Hex.decode("00000100")); + bytes = ByteUtil.intToBytes(0); + assertArrayEquals(bytes, Hex.decode("00000000")); + + bytes = ByteUtil.longToBytesNoLeadZeroes(-1); + assertArrayEquals(bytes, Hex.decode("ffffffffffffffff")); + bytes = ByteUtil.longToBytesNoLeadZeroes(1); + assertArrayEquals(bytes, Hex.decode("01")); + bytes = ByteUtil.longToBytesNoLeadZeroes(255); + assertArrayEquals(bytes, Hex.decode("ff")); + bytes = ByteUtil.longToBytesNoLeadZeroes(1L << 32); + assertArrayEquals(bytes, Hex.decode("0100000000")); + bytes = ByteUtil.longToBytesNoLeadZeroes(0); + assertArrayEquals(bytes, new byte[0]); + + bytes = ByteUtil.longToBytes(-1); + assertArrayEquals(bytes, Hex.decode("ffffffffffffffff")); + bytes = ByteUtil.longToBytes(1); + assertArrayEquals(bytes, Hex.decode("0000000000000001")); + bytes = ByteUtil.longToBytes(255); + assertArrayEquals(bytes, Hex.decode("00000000000000ff")); + bytes = ByteUtil.longToBytes(256); + assertArrayEquals(bytes, Hex.decode("0000000000000100")); + bytes = ByteUtil.longToBytes(0); + assertArrayEquals(bytes, Hex.decode("0000000000000000")); + } + + @Test + public void testHexStringToBytes() { + { + String str = "0000"; + byte[] actuals = ByteUtil.hexStringToBytes(str); + byte[] expected = new byte[] {0, 0}; + assertArrayEquals(expected, actuals); + } + { + String str = "0x0000"; + byte[] actuals = ByteUtil.hexStringToBytes(str); + byte[] expected = new byte[] {0, 0}; + assertArrayEquals(expected, actuals); + } + { + String str = "0x45a6"; + byte[] actuals = ByteUtil.hexStringToBytes(str); + byte[] expected = new byte[] {69, -90}; + assertArrayEquals(expected, actuals); + } + { + String str = "1963093cee500c081443e1045c40264b670517af"; + byte[] actuals = ByteUtil.hexStringToBytes(str); + byte[] expected = Hex.decode(str); + assertArrayEquals(expected, actuals); + } + { + String str = "0x"; // Empty + byte[] actuals = ByteUtil.hexStringToBytes(str); + byte[] expected = new byte[] {}; + assertArrayEquals(expected, actuals); + } + { + String str = "0"; // Same as 0x00 + byte[] actuals = ByteUtil.hexStringToBytes(str); + byte[] expected = new byte[] {0}; + assertArrayEquals(expected, actuals); + } + { + String str = "0x00"; // This case shouldn't be empty array + byte[] actuals = ByteUtil.hexStringToBytes(str); + byte[] expected = new byte[] {0}; + assertArrayEquals(expected, actuals); + } + { + String str = "0xd"; // Should work with odd length, adding leading 0 + byte[] actuals = ByteUtil.hexStringToBytes(str); + byte[] expected = new byte[] {13}; + assertArrayEquals(expected, actuals); + } + { + String str = "0xd0d"; // Should work with odd length, adding leading 0 + byte[] actuals = ByteUtil.hexStringToBytes(str); + byte[] expected = new byte[] {13, 13}; + assertArrayEquals(expected, actuals); + } + } +} diff --git a/modUtil/test/org/aion/util/bytes/ByteUtilTest.java b/modUtil/src/test/java/org/aion/util/bytes/ByteUtilTest.java similarity index 98% rename from modUtil/test/org/aion/util/bytes/ByteUtilTest.java rename to modUtil/src/test/java/org/aion/util/bytes/ByteUtilTest.java index 8e1b8ab7b1..95ed19b587 100644 --- a/modUtil/test/org/aion/util/bytes/ByteUtilTest.java +++ b/modUtil/src/test/java/org/aion/util/bytes/ByteUtilTest.java @@ -138,11 +138,9 @@ private Object intValues() { @SuppressWarnings("unused") private Object shortValues() { - Short[] temp = { + return new Short[] { 0, 1, 10, 15, 20, (short) random.nextInt(Integer.MAX_VALUE), (short) Integer.MAX_VALUE }; - - return temp; } /** @@ -176,9 +174,9 @@ public void bigIntegerTest() { // encodeDataList(Object) @Test public void objectTest() { - for (int a = 0; a < testNum.length; a++) { + for (String[] strings : testNum) { try { - byte[] temp1 = encodeValFor32Bits(testNum[a][1]); + encodeValFor32Bits(strings[1]); } catch (NullPointerException e) { System.out.println("\nNull Object Test!"); } @@ -277,12 +275,14 @@ public void byteTest() { // Empty Array byte[] temp1 = stripLeadingZeroes(testByte[1]); + assert temp1 != null; assertEquals(-1, firstNonZeroByte(temp1)); assertEquals(-1, firstNonZeroByte(testByte[1])); assertTrue(isNullOrZeroArray(testByte[1])); // Leading Non-zero byte[] temp2 = stripLeadingZeroes(testByte[2]); + assert temp2 != null; assertEquals(0, firstNonZeroByte(temp2)); assertEquals(0, firstNonZeroByte(testByte[2])); assertEquals(0, firstNonZeroByte(testByte[3])); @@ -295,6 +295,7 @@ public void byteTest() { // Leading Zeroes byte[] temp3 = stripLeadingZeroes(testByte[6]); + assert temp3 != null; assertEquals(0, firstNonZeroByte(temp3)); assertEquals(31, firstNonZeroByte(testByte[6])); @@ -312,13 +313,14 @@ public void byteTest() { for (int a = 1; a < testBigInt.length; a++) { byte[] temp4 = bigIntegerToBytes(testBigInt[a][1]); String temp5 = toHexString(temp4); + assert temp4 != null; assertEquals(temp5.length() / 2, temp4.length); assertEquals(temp5.length() / 2, numBytes(testNum[a][1])); } // Append - for (int a = 0; a < testHex.length; a++) { - byte[] temp6 = hexStringToBytes(testHex[a]); + for (String testHex1 : testHex) { + byte[] temp6 = hexStringToBytes(testHex1); byte temp7 = toByte(testByte[2]); byte[] temp8 = appendByte(temp6, temp7); String temp9 = oneByteToHexString(temp7); diff --git a/modAionBase/test/org/aion/base/util/MAFTest.java b/modUtil/src/test/java/org/aion/util/bytes/MAFTest.java similarity index 52% rename from modAionBase/test/org/aion/base/util/MAFTest.java rename to modUtil/src/test/java/org/aion/util/bytes/MAFTest.java index 75c84632e1..e37272e29e 100644 --- a/modAionBase/test/org/aion/base/util/MAFTest.java +++ b/modUtil/src/test/java/org/aion/util/bytes/MAFTest.java @@ -1,7 +1,8 @@ -package org.aion.base.util; +package org.aion.util.bytes; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicBoolean; +import org.aion.util.others.MAF; import org.junit.Test; public class MAFTest { @@ -37,47 +38,41 @@ public void testMAFMultiThread() { MAF ma = new MAF(5); Thread producerA = - new Thread() { - public void run() { - try { - for (int i = 0; i < itrCount; i++) { - ma.add(ThreadLocalRandom.current().nextDouble(min, max)); - } - System.out.println("producerA done --------------"); - } catch (Exception e) { - e.printStackTrace(); - } + new Thread(() -> { + try { + for (int i = 0; i < itrCount; i++) { + ma.add(ThreadLocalRandom.current().nextDouble(min, max)); } - }; + System.out.println("producerA done --------------"); + } catch (Exception e) { + e.printStackTrace(); + } + }); Thread producerB = - new Thread() { - public void run() { - try { - for (int i = 0; i < itrCount; i++) { - ma.add(ThreadLocalRandom.current().nextDouble(min, max)); - } - System.out.println("producerB done --------------"); - } catch (Exception e) { - e.printStackTrace(); - } + new Thread(() -> { + try { + for (int i = 0; i < itrCount; i++) { + ma.add(ThreadLocalRandom.current().nextDouble(min, max)); } - }; + System.out.println("producerB done --------------"); + } catch (Exception e) { + e.printStackTrace(); + } + }); AtomicBoolean shutdown = new AtomicBoolean(false); Thread consumer = - new Thread() { - public void run() { - try { - while (!shutdown.get()) { - System.out.println(ma.getAverage()); - // Thread.sleep(1); - } - System.out.println("consumer done --------------"); - } catch (Exception e) { - e.printStackTrace(); - } + new Thread(() -> { + try { + while (!shutdown.get()) { + System.out.println(ma.getAverage()); + // Thread.sleep(1); } - }; + System.out.println("consumer done --------------"); + } catch (Exception e) { + e.printStackTrace(); + } + }); try { producerA.start(); diff --git a/modAionBase/test/org/aion/base/util/SizeParseTest.java b/modUtil/src/test/java/org/aion/util/bytes/SizeParseTest.java similarity index 78% rename from modAionBase/test/org/aion/base/util/SizeParseTest.java rename to modUtil/src/test/java/org/aion/util/bytes/SizeParseTest.java index 680ddebb6d..2fe8d55b00 100644 --- a/modAionBase/test/org/aion/base/util/SizeParseTest.java +++ b/modUtil/src/test/java/org/aion/util/bytes/SizeParseTest.java @@ -1,10 +1,10 @@ -package org.aion.base.util; - -import static com.google.common.truth.Truth.assertThat; +package org.aion.util.bytes; +import com.google.common.truth.Truth; import java.util.Arrays; import java.util.Collection; import java.util.Optional; +import org.aion.util.others.Utils; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -35,11 +35,11 @@ public static Collection<Object[]> data() { } private static class Bundle { - public final String input; - public final boolean expectedResult; - public final long expectedSize; + final String input; + final boolean expectedResult; + final long expectedSize; - public Bundle(String input, boolean expectedResult, long expectedSize) { + Bundle(String input, boolean expectedResult, long expectedSize) { this.input = input; this.expectedResult = expectedResult; this.expectedSize = expectedSize; @@ -55,8 +55,8 @@ public SizeParseTest(Bundle bundle) { @Test public void test() { Optional<Long> maybeSize = Utils.parseSize(bundle.input); - assertThat(maybeSize.isPresent()).isEqualTo(bundle.expectedResult); + Truth.assertThat(maybeSize.isPresent()).isEqualTo(bundle.expectedResult); - maybeSize.ifPresent((p) -> assertThat(p).isEqualTo(bundle.expectedSize)); + maybeSize.ifPresent((p) -> Truth.assertThat(p).isEqualTo(bundle.expectedSize)); } } diff --git a/modVM/build.gradle b/modVM/build.gradle index 1afbcab536..4a5969ea0e 100644 --- a/modVM/build.gradle +++ b/modVM/build.gradle @@ -1,20 +1,29 @@ ext.moduleName = 'aion.vm' dependencies { - compile project(':modAionBase') + compile 'network.aion:vm-api4j:0.4.0' + compile 'network.aion:util4j:0.4.0' + compile project(':modAion') compile project(':modMcf') compile project(':modCrypto') - compile project(':aion_vm_api') compile project(':aion_fastvm') compile files('../lib/org-aion-avm-core.jar') - compile files('../lib/org-aion-avm-rt.jar') compile 'com.google.code.findbugs:jsr305:3.0.2' compile 'org.slf4j:slf4j-api:1.7.25' compile group: 'org.apache.commons', name: 'commons-collections4', version: '4.0' compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.4' compile group: 'commons-codec', name: 'commons-codec', version: '1.10' + runtimeOnly group: 'org.ow2.asm', name: 'asm', version: '6.2.1' + runtimeOnly group: 'org.ow2.asm', name: 'asm-analysis', version: '6.2.1' + runtimeOnly group: 'org.ow2.asm', name: 'asm-commons', version: '6.2.1' + runtimeOnly group: 'org.ow2.asm', name: 'asm-tree', version: '6.2.1' + runtimeOnly group: 'org.ow2.asm', name: 'asm-util', version: '6.2.1' + runtimeOnly files('../lib/org-aion-avm-api.jar') + runtimeOnly files('../lib/org-aion-avm-rt.jar') + runtimeOnly files('../lib/org-aion-avm-userlib.jar') + testCompile 'junit:junit:4.12' testCompile 'org.hamcrest:hamcrest-all:1.3' } diff --git a/modVM/src/module-info.java b/modVM/src/module-info.java index a867da95c9..79c93986fa 100644 --- a/modVM/src/module-info.java +++ b/modVM/src/module-info.java @@ -1,5 +1,4 @@ module aion.vm { - requires aion.base; requires aion.mcf; requires transitive slf4j.api; requires aion.zero; @@ -10,6 +9,7 @@ requires org.aion.avm.core; requires aion.precompiled; requires com.google.common; + requires aion.crypto; exports org.aion.vm; } diff --git a/modVM/src/org/aion/vm/AionCapabilities.java b/modVM/src/org/aion/vm/AionCapabilities.java new file mode 100644 index 0000000000..d9dd2efd75 --- /dev/null +++ b/modVM/src/org/aion/vm/AionCapabilities.java @@ -0,0 +1,43 @@ +package org.aion.vm; + +import java.math.BigInteger; +import java.nio.ByteBuffer; +import org.aion.avm.core.IExternalCapabilities; +import org.aion.avm.core.NodeEnvironment; +import org.aion.crypto.HashUtil; +import org.aion.types.Address; +import org.aion.vm.api.interfaces.TransactionInterface; + +public class AionCapabilities implements IExternalCapabilities { + + @Override + public byte[] sha256(byte[] bytes) { + return HashUtil.sha256(bytes); + } + + @Override + public byte[] blake2b(byte[] bytes) { + return org.aion.crypto.hash.Blake2bNative.blake256(bytes); + } + + @Override + public byte[] keccak256(byte[] bytes) { + return HashUtil.keccak256(bytes); + } + + @Override + public boolean verifyEdDSA(byte[] bytes, byte[] bytes1, byte[] bytes2) { + return org.aion.crypto.ed25519.ECKeyEd25519.verify(bytes, bytes1, bytes2); + } + + @Override + public Address generateContractAddress(TransactionInterface tx) { + Address sender = tx.getSenderAddress(); + long nonce = new BigInteger(tx.getNonce()).longValue(); + ByteBuffer buffer = + ByteBuffer.allocate(Address.SIZE + Long.BYTES).put(sender.toBytes()).putLong(nonce); + byte[] hash = sha256(buffer.array()); + hash[0] = NodeEnvironment.CONTRACT_PREFIX; + return Address.wrap(hash); + } +} \ No newline at end of file diff --git a/modVM/src/org/aion/vm/BulkExecutor.java b/modVM/src/org/aion/vm/BulkExecutor.java index 87b93416d7..bf06cafafa 100644 --- a/modVM/src/org/aion/vm/BulkExecutor.java +++ b/modVM/src/org/aion/vm/BulkExecutor.java @@ -3,20 +3,20 @@ import java.util.ArrayList; import java.util.List; import org.aion.avm.core.NodeEnvironment; -import org.aion.base.db.IRepository; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.type.ITxExecSummary; -import org.aion.base.vm.VirtualMachineSpecs; +import org.aion.interfaces.db.Repository; +import org.aion.interfaces.db.RepositoryCache; import org.aion.fastvm.FastVirtualMachine; import org.aion.fastvm.FastVmResultCode; import org.aion.fastvm.SideEffects; +import org.aion.interfaces.tx.TxExecSummary; +import org.aion.interfaces.vm.VirtualMachineSpecs; import org.aion.kernel.AvmTransactionResult; import org.aion.mcf.core.AccountState; import org.aion.mcf.db.IBlockStoreBase; import org.aion.mcf.vm.types.KernelInterfaceForFastVM; import org.aion.mcf.vm.types.Log; +import org.aion.types.Address; import org.aion.vm.VmFactoryImplementation.VM; -import org.aion.vm.api.interfaces.Address; import org.aion.vm.api.interfaces.IExecutionLog; import org.aion.vm.api.interfaces.KernelInterface; import org.aion.vm.api.interfaces.ResultCode; @@ -61,8 +61,8 @@ public static void enabledAvmCheck(boolean isEnabled) { avmEnabled = isEnabled; } - private IRepository repository; - private IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> repositoryChild; + private Repository repository; + private RepositoryCache<AccountState, IBlockStoreBase<?, ?>> repositoryChild; private PostExecutionWork postExecutionWork; private ExecutionBatch executionBatch; private Logger logger; @@ -90,8 +90,8 @@ public static void enabledAvmCheck(boolean isEnabled) { */ public BulkExecutor( ExecutionBatch executionBatch, - IRepository repository, - IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> repositoryChild, + Repository repository, + RepositoryCache<AccountState, IBlockStoreBase<?, ?>> repositoryChild, boolean isLocalCall, boolean allowNonceIncrement, long blockRemainingEnergy, @@ -125,7 +125,7 @@ public BulkExecutor( */ public BulkExecutor( ExecutionBatch executionBatch, - IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> repositoryChild, + RepositoryCache<AccountState, IBlockStoreBase<?, ?>> repositoryChild, boolean isLocalCall, boolean allowNonceIncrement, long blockRemainingEnergy, @@ -155,31 +155,32 @@ public List<AionTxExecSummary> execute() { AionTransaction firstTransactionInNextBatch = this.executionBatch.getTransactions().get(currentIndex); + KernelInterface vmKernel; if (transactionIsForFastVirtualMachine(firstTransactionInNextBatch)) { - KernelInterfaceForFastVM fvmKernel = + vmKernel = new KernelInterfaceForFastVM( this.repositoryChild.startTracking(), this.allowNonceIncrement, this.isLocalCall); virtualMachineForNextBatch = - VirtualMachineProvider.getVirtualMachineInstance(VM.FVM, fvmKernel); + VirtualMachineProvider.getVirtualMachineInstance(VM.FVM, vmKernel); nextBatchToExecute = fetchNextBatchOfTransactionsForFastVirtualMachine(currentIndex); } else { - KernelInterfaceForAVM avmKernel = + vmKernel = new KernelInterfaceForAVM( this.repositoryChild.startTracking(), this.allowNonceIncrement, this.isLocalCall); virtualMachineForNextBatch = - VirtualMachineProvider.getVirtualMachineInstance(VM.AVM, avmKernel); + VirtualMachineProvider.getVirtualMachineInstance(VM.AVM, vmKernel); nextBatchToExecute = fetchNextBatchOfTransactionsForAionVirtualMachine(currentIndex); } // Execute the next batch of transactions using the specified virtual machine. summaries.addAll( - executeTransactions(virtualMachineForNextBatch, nextBatchToExecute)); + executeTransactions(virtualMachineForNextBatch, nextBatchToExecute, vmKernel)); currentIndex += nextBatchToExecute.size(); } @@ -188,12 +189,12 @@ public List<AionTxExecSummary> execute() { } private List<AionTxExecSummary> executeTransactions( - VirtualMachine virtualMachine, ExecutionBatch details) { + VirtualMachine virtualMachine, ExecutionBatch details, KernelInterface kernel) { List<AionTxExecSummary> summaries = new ArrayList<>(); // Run the transactions. SimpleFuture<TransactionResult>[] resultsAsFutures = - virtualMachine.run(details.getExecutionContexts()); + virtualMachine.run(kernel, details.getExecutionContexts()); // Process the results of the transactions. List<AionTransaction> transactions = details.getTransactions(); @@ -326,14 +327,14 @@ private AionTxReceipt makeReceipt( } private void updateRepository( - ITxExecSummary summary, + TxExecSummary summary, AionTransaction tx, Address coinbase, List<Address> deleteAccounts, TransactionResult result) { if (!isLocalCall && !summary.isRejected()) { - IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> track = + RepositoryCache<AccountState, IBlockStoreBase<?, ?>> track = this.repositoryChild.startTracking(); // Refund energy if transaction was successfully or reverted. diff --git a/modVM/src/org/aion/vm/ExecutionBatch.java b/modVM/src/org/aion/vm/ExecutionBatch.java index a7510fdf12..3858e3cadf 100644 --- a/modVM/src/org/aion/vm/ExecutionBatch.java +++ b/modVM/src/org/aion/vm/ExecutionBatch.java @@ -4,9 +4,8 @@ import java.util.Arrays; import java.util.List; import org.aion.fastvm.ExecutionContext; -import org.aion.mcf.vm.types.DataWord; -import org.aion.util.bytes.ByteUtil; -import org.aion.vm.api.interfaces.Address; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; import org.aion.zero.types.AionTransaction; import org.aion.zero.types.IAionBlock; import org.apache.commons.lang3.ArrayUtils; @@ -123,10 +122,10 @@ private KernelTransactionContext constructTransactionContext( Address origin = transaction.getSenderAddress(); Address caller = transaction.getSenderAddress(); - DataWord nrgPrice = transaction.nrgPrice(); + DataWordImpl nrgPrice = transaction.nrgPrice(); long energyLimit = transaction.nrgLimit(); long nrg = transaction.nrgLimit() - transaction.transactionCost(block.getNumber()); - DataWord callValue = new DataWord(ArrayUtils.nullToEmpty(transaction.getValue())); + DataWordImpl callValue = new DataWordImpl(ArrayUtils.nullToEmpty(transaction.getValue())); byte[] callData = ArrayUtils.nullToEmpty(transaction.getData()); int depth = 0; @@ -146,7 +145,7 @@ private KernelTransactionContext constructTransactionContext( if (diff.length > 16) { diff = Arrays.copyOfRange(diff, diff.length - 16, diff.length); } - DataWord blockDifficulty = new DataWord(diff); + DataWordImpl blockDifficulty = new DataWordImpl(diff); return new KernelTransactionContext( transaction, diff --git a/modVM/src/org/aion/vm/KernelInterfaceForAVM.java b/modVM/src/org/aion/vm/KernelInterfaceForAVM.java index 2598ebbcfb..5ba20123b9 100644 --- a/modVM/src/org/aion/vm/KernelInterfaceForAVM.java +++ b/modVM/src/org/aion/vm/KernelInterfaceForAVM.java @@ -2,23 +2,22 @@ import java.math.BigInteger; import org.aion.avm.core.NodeEnvironment; -import org.aion.base.db.IRepositoryCache; -import org.aion.base.util.ByteArrayWrapper; -import org.aion.base.vm.VirtualMachineSpecs; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.types.Address; +import org.aion.types.ByteArrayWrapper; import org.aion.mcf.core.AccountState; import org.aion.mcf.db.IBlockStoreBase; import org.aion.mcf.valid.TxNrgRule; import org.aion.mcf.vm.types.KernelInterfaceForFastVM; import org.aion.precompiled.ContractFactory; -import org.aion.vm.api.interfaces.Address; import org.aion.vm.api.interfaces.KernelInterface; public class KernelInterfaceForAVM implements KernelInterface { - private IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> repositoryCache; + private RepositoryCache<AccountState, IBlockStoreBase<?, ?>> repositoryCache; private boolean allowNonceIncrement, isLocalCall; public KernelInterfaceForAVM( - IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> repositoryCache, + RepositoryCache<AccountState, IBlockStoreBase<?, ?>> repositoryCache, boolean allowNonceIncrement, boolean isLocalCall) { @@ -51,7 +50,7 @@ public void rollback() { this.repositoryCache.rollback(); } - public IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> getRepositoryCache() { + public RepositoryCache<AccountState, IBlockStoreBase<?, ?>> getRepositoryCache() { return this.repositoryCache; } diff --git a/modVM/src/org/aion/vm/KernelTransactionContext.java b/modVM/src/org/aion/vm/KernelTransactionContext.java index 9ca4f273ec..887ddbb39d 100644 --- a/modVM/src/org/aion/vm/KernelTransactionContext.java +++ b/modVM/src/org/aion/vm/KernelTransactionContext.java @@ -4,13 +4,12 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import org.aion.avm.core.NodeEnvironment; -import org.aion.base.type.AionAddress; -import org.aion.base.vm.IDataWord; +import org.aion.interfaces.vm.DataWord; +import org.aion.mcf.vm.types.DataWordImpl; +import org.aion.types.Address; import org.aion.fastvm.SideEffects; -import org.aion.mcf.vm.types.DataWord; import org.aion.mcf.vm.types.DoubleDataWord; import org.aion.util.bytes.ByteUtil; -import org.aion.vm.api.interfaces.Address; import org.aion.vm.api.interfaces.TransactionContext; import org.aion.vm.api.interfaces.TransactionSideEffects; import org.aion.zero.types.AionTransaction; @@ -18,7 +17,7 @@ public class KernelTransactionContext implements TransactionContext { private static final int ENCODE_BASE_LEN = - (Address.SIZE * 4) + (DataWord.BYTES * 3) + (Long.BYTES * 4) + (Integer.BYTES * 4); + (Address.SIZE * 4) + (DataWordImpl.BYTES * 3) + (Long.BYTES * 4) + (Integer.BYTES * 4); public static int CALL = 0; public static int DELEGATECALL = 1; public static int CALLCODE = 2; @@ -33,9 +32,9 @@ public class KernelTransactionContext implements TransactionContext { public Address address; public Address sender; private Address blockCoinbase; - private IDataWord nrgPrice; - private IDataWord callValue; - private IDataWord blockDifficulty; + private DataWord nrgPrice; + private DataWord callValue; + private DataWord blockDifficulty; private byte[] callData; private byte[] txHash; private long nrg; // NOTE: nrg = tx_nrg_limit - tx_basic_cost @@ -74,9 +73,9 @@ public KernelTransactionContext( Address destination, Address origin, Address sender, - IDataWord nrgPrice, + DataWordImpl nrgPrice, long nrg, - IDataWord callValue, + DataWordImpl callValue, byte[] callData, int depth, int kind, @@ -85,7 +84,7 @@ public KernelTransactionContext( long blockNumber, long blockTimestamp, long blockNrgLimit, - IDataWord blockDifficulty) { + DataWord blockDifficulty) { this.transaction = transaction; this.address = destination; @@ -166,11 +165,10 @@ public Address getDestinationAddress() { return address; } - @Override public Address getContractAddress() { byte[] rawBytes = this.transaction.getContractAddress().toBytes(); rawBytes[0] = NodeEnvironment.CONTRACT_PREFIX; - return AionAddress.wrap(rawBytes); + return Address.wrap(rawBytes); } /** @return the origination address, which is the sender of original transaction. */ @@ -188,8 +186,8 @@ public Address getSenderAddress() { /** @return the nrg price in current environment. */ @Override public long getTransactionEnergyPrice() { - if (this.nrgPrice instanceof DataWord) { - return ((DataWord) this.nrgPrice).longValue(); + if (this.nrgPrice instanceof DataWordImpl) { + return ((DataWordImpl) this.nrgPrice).longValue(); } else { return ((DoubleDataWord) this.nrgPrice).longValue(); } @@ -257,8 +255,8 @@ public long getBlockEnergyLimit() { /** @return the block difficulty. */ @Override public long getBlockDifficulty() { - if (blockDifficulty instanceof DataWord) { - return ((DataWord) blockDifficulty).longValue(); + if (blockDifficulty instanceof DataWordImpl) { + return ((DataWordImpl) blockDifficulty).longValue(); } else { return ((DoubleDataWord) blockDifficulty).longValue(); } diff --git a/modVM/src/org/aion/vm/PostExecutionWork.java b/modVM/src/org/aion/vm/PostExecutionWork.java index 0a1d4f1345..ce142178c1 100644 --- a/modVM/src/org/aion/vm/PostExecutionWork.java +++ b/modVM/src/org/aion/vm/PostExecutionWork.java @@ -1,7 +1,7 @@ package org.aion.vm; -import org.aion.base.db.IRepository; -import org.aion.base.db.IRepositoryCache; +import org.aion.interfaces.db.Repository; +import org.aion.interfaces.db.RepositoryCache; import org.aion.mcf.core.AccountState; import org.aion.mcf.db.IBlockStoreBase; import org.aion.zero.types.AionTransaction; @@ -39,8 +39,8 @@ public interface PostExecutionWork { * @return The amount of energy that this transaction uses in its block. */ long doPostExecutionWork( - IRepository repository, - IRepositoryCache<AccountState, IBlockStoreBase<?, ?>> repositoryChild, + Repository repository, + RepositoryCache<AccountState, IBlockStoreBase<?, ?>> repositoryChild, AionTxExecSummary summary, AionTransaction transaction, long blockEnergyRemaining); diff --git a/modVM/src/org/aion/vm/VmFactoryImplementation.java b/modVM/src/org/aion/vm/VmFactoryImplementation.java index 3ab34178cf..d787536a5f 100644 --- a/modVM/src/org/aion/vm/VmFactoryImplementation.java +++ b/modVM/src/org/aion/vm/VmFactoryImplementation.java @@ -1,5 +1,6 @@ package org.aion.vm; +import org.aion.avm.core.AvmConfiguration; import org.aion.avm.core.AvmImpl; import org.aion.avm.core.CommonAvmFactory; import org.aion.fastvm.FastVirtualMachine; @@ -66,7 +67,7 @@ public void initializeAllVirtualMachines() { } // initialize the Avm. This buildAvmInstance method already calls start() for us. - this.aionVirtualMachine = CommonAvmFactory.buildAvmInstance(null); + this.aionVirtualMachine = CommonAvmFactory.buildAvmInstanceForConfiguration(new AionCapabilities(), new AvmConfiguration()); this.state = MachineState.ALL_MACHINES_LIVE; } @@ -95,11 +96,8 @@ public VirtualMachine getVirtualMachineInstance(VM request, KernelInterface kern switch (request) { case FVM: - FastVirtualMachine fvm = new FastVirtualMachine(); - fvm.setKernelInterface(kernel); - return fvm; + return new FastVirtualMachine(); case AVM: - this.aionVirtualMachine.setKernelInterface(kernel); return this.aionVirtualMachine; default: throw new UnsupportedOperationException("Unsupported VM request."); diff --git a/settings.gradle b/settings.gradle index 14bc4395cb..f23023a8bf 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,5 @@ rootProject.name = 'aion' -include 'modAionBase', 'modAion', 'modAionImpl', +include 'modAion', 'modAionImpl', 'modUtil', 'modCrypto', 'modLogger', From d8f25b174e30b3f62318661e104eb11180305b78 Mon Sep 17 00:00:00 2001 From: AionJayT <jay.tseng@aion.network> Date: Mon, 4 Mar 2019 17:52:05 -0500 Subject: [PATCH 02/15] 1.) change modLodder file structures 2.) change gradle build logger dependency from source code to the maven repo --- gradle.properties | 2 +- modAion/build.gradle | 2 +- modAionImpl/build.gradle | 2 +- modApiServer/build.gradle | 2 +- modBoot/build.gradle | 2 +- modDbImpl/build.gradle | 2 +- modEvtMgr/build.gradle | 3 +- modEvtMgrImpl/build.gradle | 3 +- modLogger/build.gradle | 108 ++++++++++++++++++ .../src/{ => main/java}/module-info.java | 0 .../java}/org/aion/log/AionLoggerFactory.java | 6 +- .../{ => main/java}/org/aion/log/LogEnum.java | 0 .../java}/org/aion/log/LogLevel.java | 0 .../{ => main/java}/org/aion/log/LogUtil.java | 29 ++--- modMcf/build.gradle | 2 +- modP2pImpl/build.gradle | 2 +- modPrecompiled/build.gradle | 2 +- modTxPoolImpl/build.gradle | 2 +- 18 files changed, 139 insertions(+), 30 deletions(-) rename modLogger/src/{ => main/java}/module-info.java (100%) rename modLogger/src/{ => main/java}/org/aion/log/AionLoggerFactory.java (97%) rename modLogger/src/{ => main/java}/org/aion/log/LogEnum.java (100%) rename modLogger/src/{ => main/java}/org/aion/log/LogLevel.java (100%) rename modLogger/src/{ => main/java}/org/aion/log/LogUtil.java (77%) diff --git a/gradle.properties b/gradle.properties index aa9d5fe7cb..625a1c6c30 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.java.home=/usr/lib/jvm/jdk-11.0.1 org.gradle.daemon=true org.gradle.jvmargs=-Xmx1g -XX:MaxPermSize=4g -XX:ReservedCodeCacheSize=1024m -org.gradle.parallel=false +org.gradle.parallel=true # Uncomment to include modGui in build # modGuiPath=aion_gui diff --git a/modAion/build.gradle b/modAion/build.gradle index ce5b745522..4a2fa8c91d 100644 --- a/modAion/build.gradle +++ b/modAion/build.gradle @@ -13,7 +13,7 @@ dependencies { compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.4' compile group: 'org.apache.commons', name: 'commons-collections4', version: '4.0' - testCompile project(':modLogger') + testCompile 'network.aion:log4j:0.4.0' testCompile project(':modCrypto') testCompile project(':modMcf') testCompile project(':modRlp') diff --git a/modAionImpl/build.gradle b/modAionImpl/build.gradle index f44c926a03..da55b8aaef 100644 --- a/modAionImpl/build.gradle +++ b/modAionImpl/build.gradle @@ -13,12 +13,12 @@ sourceSets { dependencies { compile 'network.aion:vm-api4j:0.4.0' compile 'network.aion:util4j:0.4.0' + compile 'network.aion:log4j:0.4.0' compile project(':modAion') compile project(':modRlp') compile project(':modCrypto') compile project(':modMcf') - compile project(':modLogger') compile project(':modP2pImpl') compile project(':modP2p') compile project(':modEvtMgr') diff --git a/modApiServer/build.gradle b/modApiServer/build.gradle index d281f6910f..1639cbf35c 100644 --- a/modApiServer/build.gradle +++ b/modApiServer/build.gradle @@ -16,12 +16,12 @@ sourceSets { dependencies { compile 'network.aion:vm-api4j:0.4.0' compile 'network.aion:util4j:0.4.0' + compile 'network.aion:log4j:0.4.0' compile project(':modAion') compile project(':modAionImpl') compile project(':aion_fastvm') compile project(':modMcf') - compile project(':modLogger') compile project(':modP2p') compile project(':modEvtMgr') compile project(':modEvtMgrImpl') diff --git a/modBoot/build.gradle b/modBoot/build.gradle index e23efdc52d..844932f5bc 100644 --- a/modBoot/build.gradle +++ b/modBoot/build.gradle @@ -3,11 +3,11 @@ ext.moduleName = 'aion.boot' dependencies { compile 'network.aion:vm-api4j:0.4.0' compile 'network.aion:util4j:0.4.0' + compile 'network.aion:log4j:0.4.0' compile project(':modCrypto') compile project(':modApiServer') compile project(':modAionImpl') - compile project(':modLogger') compile project(':modEvtMgr') compile project(':modP2p') compile project(':modMcf') diff --git a/modDbImpl/build.gradle b/modDbImpl/build.gradle index 8bd024afee..8d43f780a1 100644 --- a/modDbImpl/build.gradle +++ b/modDbImpl/build.gradle @@ -16,8 +16,8 @@ sourceSets { dependencies { compile 'network.aion:vm-api4j:0.4.0' compile 'network.aion:util4j:0.4.0' + compile 'network.aion:log4j:0.4.0' - compile project(':modLogger') compile 'com.google.guava:guava:25.1-jre' compile 'org.slf4j:slf4j-api:1.7.25' compile group: 'org.ethereum', name: 'leveldbjni-all', version: '1.18.3' diff --git a/modEvtMgr/build.gradle b/modEvtMgr/build.gradle index 0ecf1ec692..0694b4fe30 100644 --- a/modEvtMgr/build.gradle +++ b/modEvtMgr/build.gradle @@ -1,8 +1,9 @@ ext.moduleName = 'org.aion.evtmgr' dependencies { + testCompile 'network.aion:log4j:0.4.0' + testCompile project(':modEvtMgrImpl') - testCompile project(':modLogger') testCompile 'junit:junit:4.12' testCompile 'org.hamcrest:hamcrest-all:1.3' testCompile group: 'org.apache.commons', name: 'commons-collections4', version: '4.0' diff --git a/modEvtMgrImpl/build.gradle b/modEvtMgrImpl/build.gradle index 8928b8f182..ed0a1a5b90 100644 --- a/modEvtMgrImpl/build.gradle +++ b/modEvtMgrImpl/build.gradle @@ -1,8 +1,9 @@ ext.moduleName = 'aion.evtmgr.impl' dependencies { + compile 'network.aion:log4j:0.4.0' + compile project(':modEvtMgr') - compile project(':modLogger') compile 'com.google.guava:guava:25.1-jre' testCompile 'junit:junit:4.12' diff --git a/modLogger/build.gradle b/modLogger/build.gradle index cabb586fda..2a4c0a9187 100644 --- a/modLogger/build.gradle +++ b/modLogger/build.gradle @@ -1,7 +1,115 @@ ext.moduleName = 'aion.log' +// set the publish to true when the code ready to push the lib to the maven repo +def publish = false; + +apply plugin: 'maven' +apply plugin: 'signing' + +group = "network.aion" +archivesBaseName = "log4j" + +def getCommitHash = { -> + def hashStdOut = new ByteArrayOutputStream() + exec { + commandLine "sh", "-c", "git log --pretty=format:%h | head -1" + standardOutput = hashStdOut + } + + return hashStdOut.toString().trim() +} + dependencies { compile 'org.slf4j:slf4j-api:1.7.25' compile 'ch.qos.logback:logback-core:1.2.3' compile 'ch.qos.logback:logback-classic:1.2.3' } + +sourceSets { + + if (publish) { + version = "0.4.0" + } else { + jar.baseName = 'crypto4j-' + getCommitHash() + } + + main { + java.srcDirs = ['src/main/java'] + } + test { + java.srcDirs = ['src/test/java'] + } +} + +signing { + sign configurations.archives +} +signArchives.enabled = publish + + +task sourcesJar(type: Jar) { + classifier = 'sources' + from sourceSets.main.allSource +} +sourcesJar.enabled = publish + +javadoc { + inputs.property("moduleName", moduleName) + doFirst { + options.addStringOption('-module-path', classpath.asPath) + options.tags = [ "implNote" ] + } +} + +task javadocJar(type: Jar) { + classifier = 'javadoc' + from javadoc +} +javadocJar.enabled = publish + +artifacts { + archives sourcesJar, javadocJar +} + +uploadArchives { + repositories { + mavenDeployer { + + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + pom.project { + name 'log4j' + packaging 'jar' + // optionally artifactId can be defined here + description 'a log module for the aion java kernel.' + url 'https://github.com/aionnetwork/aion/tree/master-pre-merge/modLogger' + + scm { + connection 'scm:git:https://github.com/aionnetwork/aion.git' + developerConnection 'git:https://github.com/aionnetwork/aion.git' + url 'https://github.com/aionnetwork/aion/tree/master' + } + + licenses { + license { + name 'MIT' + url 'https://opensource.org/licenses/MIT' + } + } + + developers { + developer { + id 'aion foundation dev' + name 'aion foundation dev' + email 'toengineering@aion.network' + } + } + } + } + } +} +uploadArchives.enabled = publish diff --git a/modLogger/src/module-info.java b/modLogger/src/main/java/module-info.java similarity index 100% rename from modLogger/src/module-info.java rename to modLogger/src/main/java/module-info.java diff --git a/modLogger/src/org/aion/log/AionLoggerFactory.java b/modLogger/src/main/java/org/aion/log/AionLoggerFactory.java similarity index 97% rename from modLogger/src/org/aion/log/AionLoggerFactory.java rename to modLogger/src/main/java/org/aion/log/AionLoggerFactory.java index 553d27be7d..4469550a9b 100644 --- a/modLogger/src/org/aion/log/AionLoggerFactory.java +++ b/modLogger/src/main/java/org/aion/log/AionLoggerFactory.java @@ -72,9 +72,7 @@ private static Map<LogEnum, Level> constructModuleLoglevelMap( Map<String, String> moduleToLevelMap = new HashMap<>(); if (_moduleToLevelMap != null) { _moduleToLevelMap.forEach( - (module, level) -> { - moduleToLevelMap.put(module.toUpperCase(), level); - }); + (module, level) -> moduleToLevelMap.put(module.toUpperCase(), level)); } Map<LogEnum, Level> modules = new HashMap<>(); @@ -128,7 +126,7 @@ private static List<Appender<ILoggingEvent>> constructAppenders( RollingFileAppender<ILoggingEvent> fileSync = new RollingFileAppender<>(); - SizeBasedTriggeringPolicy tp = new SizeBasedTriggeringPolicy(); + SizeBasedTriggeringPolicy<ILoggingEvent> tp = new SizeBasedTriggeringPolicy<>(); tp.setContext(context); tp.start(); diff --git a/modLogger/src/org/aion/log/LogEnum.java b/modLogger/src/main/java/org/aion/log/LogEnum.java similarity index 100% rename from modLogger/src/org/aion/log/LogEnum.java rename to modLogger/src/main/java/org/aion/log/LogEnum.java diff --git a/modLogger/src/org/aion/log/LogLevel.java b/modLogger/src/main/java/org/aion/log/LogLevel.java similarity index 100% rename from modLogger/src/org/aion/log/LogLevel.java rename to modLogger/src/main/java/org/aion/log/LogLevel.java diff --git a/modLogger/src/org/aion/log/LogUtil.java b/modLogger/src/main/java/org/aion/log/LogUtil.java similarity index 77% rename from modLogger/src/org/aion/log/LogUtil.java rename to modLogger/src/main/java/org/aion/log/LogUtil.java index ec343631c3..46efe7c2e0 100644 --- a/modLogger/src/org/aion/log/LogUtil.java +++ b/modLogger/src/main/java/org/aion/log/LogUtil.java @@ -12,9 +12,9 @@ public class LogUtil { * 'http://stackoverflow.com/questions/9655181/how-to-convert-a-byte-array-to-a-hex-string-in-java'>stackoverflow * discussion</a> and our benchmark for performance gains */ - protected static final char[] hexArray = "0123456789abcfef".toCharArray(); + private static final char[] hexArray = "0123456789abcdef".toCharArray(); - protected static String toHex(byte[] bytes) { + private static String toHex(byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; for (int j = 0; j < bytes.length; j++) { int v = bytes[j] & 0xFF; @@ -24,16 +24,17 @@ protected static String toHex(byte[] bytes) { return new String(hexChars); } + + private static final String nullString = ""; + /** * Guarantees a return of at max, first 8 characters, even if data is ill formatted (null, * shorter, longer) etc. * - * @param data - * @return + * @param data byte array + * @return first 8 chars of the input HexString */ - protected static final String nullString = ""; - - protected static String toHexF8Internal(byte[] data) { + private static String toHexF8Internal(byte[] data) { if (data == null || data.length == 0) { return nullString; } @@ -49,10 +50,10 @@ protected static String toHexF8Internal(byte[] data) { * Guarantees a return of at max, last 8 characters, even if data is ill formatted (null, * shorter, longer) etc. * - * @param data - * @return + * @param data byte array + * @return last 8 chars of the input HexString */ - public static String toHexL8Internal(byte[] data) { + private static String toHexL8Internal(byte[] data) { if (data == null || data.length == 0) { return nullString; } @@ -68,8 +69,8 @@ public static String toHexL8Internal(byte[] data) { * Guarantees a return of at max, first 8 characters, even if data is ill formatted (null, * shorter, longer) etc. * - * @param data - * @return + * @param data byte array + * @return first 8 chars of the input HexString */ public static String toHexF8(byte[] data) { int len = 0; @@ -82,8 +83,8 @@ public static String toHexF8(byte[] data) { * Guarantees a return of at max, last 8 characters, even if data is ill formatted (null, * shorter, longer) etc. * - * @param data - * @return + * @param data byte array + * @return last 8 chars of the input HexString */ public static String toHexL8(byte[] data) { int len = 0; diff --git a/modMcf/build.gradle b/modMcf/build.gradle index 5d87bb733a..86c824931d 100644 --- a/modMcf/build.gradle +++ b/modMcf/build.gradle @@ -6,8 +6,8 @@ clean.dependsOn deleteNativeLibs dependencies { compile 'network.aion:vm-api4j:0.4.0' compile 'network.aion:util4j:0.4.0' + compile 'network.aion:log4j:0.4.0' - compile project(':modLogger') compile project(':modP2p') compile project(':modCrypto') compile project(':modRlp') diff --git a/modP2pImpl/build.gradle b/modP2pImpl/build.gradle index ae18768b87..94b04a99c5 100644 --- a/modP2pImpl/build.gradle +++ b/modP2pImpl/build.gradle @@ -10,9 +10,9 @@ sourceSets { dependencies { compile 'network.aion:util4j:0.4.0' + compile 'network.aion:log4j:0.4.0' compile project(':modP2p') - compile project(':modLogger') compile files('../lib/miniupnpc_linux.jar') compile 'org.apache.commons:commons-collections4:4.0' compile 'org.slf4j:slf4j-api:1.7.25' diff --git a/modPrecompiled/build.gradle b/modPrecompiled/build.gradle index 963a049a99..4cf5346d28 100644 --- a/modPrecompiled/build.gradle +++ b/modPrecompiled/build.gradle @@ -19,7 +19,7 @@ dependencies { testCompile project(':modRlp') testCompile project(':modPrecompiled') testCompile project(':modAionImpl') - testCompile project(':modLogger') + testCompile 'network.aion:log4j:0.4.0' testCompile project(':modDbImpl') testCompile 'junit:junit:4.12' testCompile 'org.hamcrest:hamcrest-all:1.3' diff --git a/modTxPoolImpl/build.gradle b/modTxPoolImpl/build.gradle index f9acecf4b2..1698f6c18d 100644 --- a/modTxPoolImpl/build.gradle +++ b/modTxPoolImpl/build.gradle @@ -5,9 +5,9 @@ clean.dependsOn deleteNativeLibs dependencies { compile 'network.aion:vm-api4j:0.4.0' compile 'network.aion:util4j:0.4.0' + compile 'network.aion:log4j:0.4.0' compile project(':modTxPool') - compile project(':modLogger') compile 'com.madgag.spongycastle:prov:1.58.0.0' compile 'com.madgag.spongycastle:core:1.58.0.0' From b1431fc442f0138c5361670e882549c704f87321 Mon Sep 17 00:00:00 2001 From: AionJayT <jay.tseng@aion.network> Date: Tue, 5 Mar 2019 10:58:54 -0500 Subject: [PATCH 03/15] squash commits fixed NPE concerns in some classes & format revised --- build.gradle | 1 - .../aion/zero/db/AionContractDetailsImpl.java | 18 +++++++++++------ .../org/aion/zero/types/A0BlockHeader.java | 20 +++++++++++++------ .../org/aion/zero/types/AionTransaction.java | 3 ++- ...SolutionImpl.java => AionPowSolution.java} | 4 ++-- .../src/org/aion/equihash/EquiValidator.java | 4 ++-- .../src/org/aion/equihash/Equihash.java | 5 +++-- .../src/org/aion/equihash/EquihashMiner.java | 3 ++- .../aion/equihash/OptimizedEquiValidator.java | 20 +++++++++---------- .../src/org/aion/zero/impl/AionHub.java | 4 ++-- .../impl/blockchain/AionPendingStateImpl.java | 2 +- .../aion/zero/impl/db/AionRepositoryImpl.java | 8 ++++++-- .../src/org/aion/zero/impl/pow/AionPoW.java | 6 +++--- .../org/aion/db/AionContractDetailsTest.java | 1 + .../test/org/aion/db/DoubleDataWordTest.java | 6 +++++- .../EquihashSolutionsGenerationTest210_9.java | 2 +- .../test/org/aion/zero/impl/AionHubTest.java | 1 - .../zero/impl/db/AionRepositoryImplTest.java | 8 +++++++- .../src/org/aion/api/server/pb/Message.java | 10 +++++----- 19 files changed, 78 insertions(+), 48 deletions(-) rename modAionImpl/src/org/aion/equihash/{SolutionImpl.java => AionPowSolution.java} (84%) diff --git a/build.gradle b/build.gradle index 34bb88bb68..4acfe21298 100644 --- a/build.gradle +++ b/build.gradle @@ -326,7 +326,6 @@ task ciBuild { def ciModules = [ 'aion_fastvm', 'modAion', - 'modAionBase', 'modAionImpl', 'modApiServer', 'modBoot', diff --git a/modAion/src/org/aion/zero/db/AionContractDetailsImpl.java b/modAion/src/org/aion/zero/db/AionContractDetailsImpl.java index 902a08cfda..535370bb31 100644 --- a/modAion/src/org/aion/zero/db/AionContractDetailsImpl.java +++ b/modAion/src/org/aion/zero/db/AionContractDetailsImpl.java @@ -43,7 +43,11 @@ public AionContractDetailsImpl(int prune, int memStorageLimit) { private AionContractDetailsImpl( Address address, SecureTrie storageTrie, Map<ByteArrayWrapper, byte[]> codes) { - this.address = address; + if (address == null) { + throw new IllegalArgumentException("Address can not be null!"); + } else { + this.address = address; + } this.storageTrie = storageTrie; setCodes(codes); } @@ -151,8 +155,8 @@ public void decode(byte[] rlpCode, boolean fastCheck) { RLPItem storageRoot = (RLPItem) rlpList.get(2); RLPElement code = rlpList.get(4); - if (address.getRLPData() == null || address.getRLPData().length == 0) { - this.address = null; + if (address == null || address.getRLPData() == null || address.getRLPData().length != Address.SIZE) { + throw new IllegalArgumentException("rlp decode error."); } else { this.address = Address.wrap(address.getRLPData()); } @@ -191,7 +195,7 @@ public void decode(byte[] rlpCode, boolean fastCheck) { public byte[] getEncoded() { if (rlpEncoded == null) { - byte[] rlpAddress = RLP.encodeElement(address == null ? null : address.toBytes()); + byte[] rlpAddress = RLP.encodeElement(address.toBytes()); byte[] rlpIsExternalStorage = RLP.encodeByte((byte) (externalStorage ? 1 : 0)); byte[] rlpStorageRoot = RLP.encodeElement( @@ -230,6 +234,9 @@ public Address getAddress() { */ @Override public void setAddress(Address address) { + if (address == null) { + throw new IllegalArgumentException("Address can not be null!"); + } this.address = address; this.rlpEncoded = null; } @@ -326,8 +333,7 @@ public AionContractDetailsImpl copy() { aionContractDetailsCopy.setCodes(getDeepCopyOfCodes()); aionContractDetailsCopy.setDirty(this.isDirty()); aionContractDetailsCopy.setDeleted(this.isDeleted()); - aionContractDetailsCopy.address = - (this.address == null) ? null : new Address(this.address.toBytes()); + aionContractDetailsCopy.address = new Address(this.address.toBytes()); aionContractDetailsCopy.rlpEncoded = (this.rlpEncoded == null) ? null diff --git a/modAion/src/org/aion/zero/types/A0BlockHeader.java b/modAion/src/org/aion/zero/types/A0BlockHeader.java index d15e971cd1..4d7c0bf4f2 100644 --- a/modAion/src/org/aion/zero/types/A0BlockHeader.java +++ b/modAion/src/org/aion/zero/types/A0BlockHeader.java @@ -83,10 +83,10 @@ public A0BlockHeader(RLPList rlpHeader) { // CoinBase byte[] data = rlpHeader.get(RPL_BH_COINBASE).getRLPData(); - this.coinbase = - (data == null) - ? null - : Address.wrap(rlpHeader.get(RPL_BH_COINBASE).getRLPData()); + if (data == null || data.length != Address.SIZE) { + throw new IllegalArgumentException("Coinbase can not be null!"); + } + this.coinbase = Address.wrap(data); // StateRoot this.stateRoot = rlpHeader.get(RPL_BH_STATEROOT).getRLPData(); @@ -156,7 +156,11 @@ public A0BlockHeader(A0BlockHeader toCopy) { System.arraycopy(toCopy.getParentHash(), 0, this.parentHash, 0, this.parentHash.length); // Copy elements in coinbase - this.coinbase = toCopy.coinbase.clone(); + if (toCopy.coinbase == null) { + throw new IllegalArgumentException("Coinbase can not be null!"); + } else { + this.coinbase = toCopy.coinbase.clone(); + } // Copy stateroot this.stateRoot = new byte[toCopy.getStateRoot().length]; @@ -215,7 +219,11 @@ public A0BlockHeader( byte[] nonce, byte[] solution) { this.version = version; - this.coinbase = (Address) coinbase; + if (coinbase == null) { + throw new IllegalArgumentException("Coinbase can not be null!"); + } else { + this.coinbase = coinbase; + } this.parentHash = parentHash; this.logsBloom = logsBloom; this.difficulty = difficulty; diff --git a/modAion/src/org/aion/zero/types/AionTransaction.java b/modAion/src/org/aion/zero/types/AionTransaction.java index 9c38a2f380..c77fb56c46 100644 --- a/modAion/src/org/aion/zero/types/AionTransaction.java +++ b/modAion/src/org/aion/zero/types/AionTransaction.java @@ -134,7 +134,8 @@ public void rlpParse() { this.value = tx.get(RLP_TX_VALUE).getRLPData(); this.data = tx.get(RLP_TX_DATA).getRLPData(); - if (tx.get(RLP_TX_TO).getRLPData() == null) { + byte[] rlpTo = tx.get(RLP_TX_TO).getRLPData(); + if (rlpTo == null || rlpTo.length == 0) { this.to = null; } else { this.to = Address.wrap(tx.get(RLP_TX_TO).getRLPData()); diff --git a/modAionImpl/src/org/aion/equihash/SolutionImpl.java b/modAionImpl/src/org/aion/equihash/AionPowSolution.java similarity index 84% rename from modAionImpl/src/org/aion/equihash/SolutionImpl.java rename to modAionImpl/src/org/aion/equihash/AionPowSolution.java index 0d6730d442..ddfe162c7f 100644 --- a/modAionImpl/src/org/aion/equihash/SolutionImpl.java +++ b/modAionImpl/src/org/aion/equihash/AionPowSolution.java @@ -9,13 +9,13 @@ * * @author Ross Kitsis (ross@nuco.io) */ -public class SolutionImpl implements Solution { +public class AionPowSolution implements Solution { private final IAionBlock block; private final byte[] nonce; private final byte[] solution; - public SolutionImpl(IAionBlock block, byte[] nonce, byte[] solution) { + public AionPowSolution(IAionBlock block, byte[] nonce, byte[] solution) { this.block = block; this.nonce = nonce; diff --git a/modAionImpl/src/org/aion/equihash/EquiValidator.java b/modAionImpl/src/org/aion/equihash/EquiValidator.java index ca7edf4c8b..52b8e8b017 100644 --- a/modAionImpl/src/org/aion/equihash/EquiValidator.java +++ b/modAionImpl/src/org/aion/equihash/EquiValidator.java @@ -148,14 +148,14 @@ public boolean isValidSolution(byte[] solution, byte[] blockHeader, byte[] nonce for (int i = 0; i < loopLen / 2; i++) { if (!hasCollision(X[i * 2], X[i * 2 + 1], collisionByteLength)) { - LOG.error("Invalid SolutionImpl: Collision not present"); + LOG.error("Invalid Solution: Collision not present"); System.out.println("No collision"); return false; } if (EquiUtils.indicesBefore(X[i * 2 + 1], X[i * 2], hashLen, lenIndices)) { System.out.println("Incorrect order"); - LOG.error("Invalid SolutionImpl: Index tree incorrecly ordered"); + LOG.error("Invalid Solution: Index tree incorrecly ordered"); return false; } if (!distinctIndices(X[i * 2 + 1], X[i * 2], hashLen, lenIndices)) { diff --git a/modAionImpl/src/org/aion/equihash/Equihash.java b/modAionImpl/src/org/aion/equihash/Equihash.java index e73a3cb799..9a2a38c86e 100644 --- a/modAionImpl/src/org/aion/equihash/Equihash.java +++ b/modAionImpl/src/org/aion/equihash/Equihash.java @@ -8,6 +8,7 @@ import java.math.BigInteger; import java.util.concurrent.atomic.AtomicLong; import org.aion.crypto.HashUtil; +import org.aion.interfaces.block.Solution; import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.aion.util.file.NativeLoader; @@ -76,7 +77,7 @@ public int[][] getSolutionsForNonce(byte[] header, byte[] nonce) { /* * Mine for a single nonce */ - public SolutionImpl mine(IAionBlock block, byte[] nonce) { + public AionPowSolution mine(IAionBlock block, byte[] nonce) { A0BlockHeader updateHeader = new A0BlockHeader(block.getHeader()); @@ -110,7 +111,7 @@ public SolutionImpl mine(IAionBlock block, byte[] nonce) { // Found a valid solution if (isValidBlock(validationBytes, target)) { - return new SolutionImpl(block, nonce, minimal); + return new AionPowSolution(block, nonce, minimal); } } diff --git a/modAionImpl/src/org/aion/equihash/EquihashMiner.java b/modAionImpl/src/org/aion/equihash/EquihashMiner.java index d5d3288c7c..54f11a0060 100644 --- a/modAionImpl/src/org/aion/equihash/EquihashMiner.java +++ b/modAionImpl/src/org/aion/equihash/EquihashMiner.java @@ -20,6 +20,7 @@ import org.aion.evtmgr.impl.es.EventExecuteService; import org.aion.evtmgr.impl.evt.EventConsensus; import org.aion.evtmgr.impl.evt.EventMiner; +import org.aion.interfaces.block.Solution; import org.aion.mcf.mine.AbstractMineRunner; import org.aion.util.others.MAF; import org.aion.zero.impl.blockchain.AionImpl; @@ -192,7 +193,7 @@ private void mine() { nonce = new byte[32]; ThreadLocalRandom.current().nextBytes(nonce); - SolutionImpl s = miner.mine(block, nonce); + Solution s = miner.mine(block, nonce); if (s != null) { IEvent ev = new EventConsensus(EventConsensus.CALLBACK.ON_SOLUTION); ev.setFuncArgs(Collections.singletonList(s)); diff --git a/modAionImpl/src/org/aion/equihash/OptimizedEquiValidator.java b/modAionImpl/src/org/aion/equihash/OptimizedEquiValidator.java index b9bb2cc3db..a9f8560e0a 100644 --- a/modAionImpl/src/org/aion/equihash/OptimizedEquiValidator.java +++ b/modAionImpl/src/org/aion/equihash/OptimizedEquiValidator.java @@ -183,7 +183,7 @@ private boolean verify( // Check out of order indices if (indices[index] >= indices[index1]) { - LOG.debug("SolutionImpl validation failed - indices out of order"); + LOG.debug("Solution validation failed - indices out of order"); return false; } @@ -192,13 +192,13 @@ private boolean verify( boolean verify0 = verify(blockHeader, nonce, blake, indices, index, hash0, round - 1); if (!verify0) { - LOG.debug("SolutionImpl validation failed - unable to verify left subtree"); + LOG.debug("Solution validation failed - unable to verify left subtree"); return false; } boolean verify1 = verify(blockHeader, nonce, blake, indices, index1, hash1, round - 1); if (!verify1) { - LOG.debug("SolutionImpl validation failed - unable to verify right subtree"); + LOG.debug("Solution validation failed - unable to verify right subtree"); return false; } @@ -210,14 +210,14 @@ private boolean verify( for (int i = 0; i < bitsDiv8; i++) { if (hash[i] != 0) { - LOG.debug("SolutionImpl validation failed - Non-zero XOR"); + LOG.debug("Solution validation failed - Non-zero XOR"); return false; } } // Check remainder bits if ((bitsMod8) > 0 && (hash[bitsDiv8] >> (8 - (bitsMod8))) != 0) { - LOG.debug("SolutionImpl validation failed - Non-zero XOR"); + LOG.debug("Solution validation failed - Non-zero XOR"); return false; } @@ -237,7 +237,7 @@ private boolean verifyNative( // Check out of order indices if (indices[index] >= indices[index1]) { - LOG.debug("SolutionImpl validation failed - indices out of order"); + LOG.debug("Solution validation failed - indices out of order"); return false; } @@ -246,13 +246,13 @@ private boolean verifyNative( boolean verify0 = verifyNative(indices, index, hash0, round - 1, hashes); if (!verify0) { - LOG.debug("SolutionImpl validation failed - unable to verify left subtree"); + LOG.debug("Solution validation failed - unable to verify left subtree"); return false; } boolean verify1 = verifyNative(indices, index1, hash1, round - 1, hashes); if (!verify1) { - LOG.debug("SolutionImpl validation failed - unable to verify right subtree"); + LOG.debug("Solution validation failed - unable to verify right subtree"); return false; } @@ -264,14 +264,14 @@ private boolean verifyNative( for (int i = 0; i < bitsDiv8; i++) { if (hash[i] != 0) { - LOG.debug("SolutionImpl validation failed - Non-zero XOR"); + LOG.debug("Solution validation failed - Non-zero XOR"); return false; } } // Check remainder bits if ((bitsMod8) > 0 && (hash[bitsDiv8] >> (8 - (bitsMod8))) != 0) { - LOG.debug("SolutionImpl validation failed - Non-zero XOR"); + LOG.debug("Solution validation failed - Non-zero XOR"); return false; } diff --git a/modAionImpl/src/org/aion/zero/impl/AionHub.java b/modAionImpl/src/org/aion/zero/impl/AionHub.java index 9cf3938be8..8074119770 100644 --- a/modAionImpl/src/org/aion/zero/impl/AionHub.java +++ b/modAionImpl/src/org/aion/zero/impl/AionHub.java @@ -260,7 +260,7 @@ private void loadEventMgr(boolean forTest) { } public Repository getRepository() { - return (Repository) repository; + return repository; } public IAionBlockchain getBlockchain() { @@ -339,7 +339,7 @@ private void loadBlockchain() { } } - recovered = this.blockchain.recoverWorldState((Repository) this.repository, bestBlock); + recovered = this.blockchain.recoverWorldState(this.repository, bestBlock); if (!this.repository.isValidRoot(bestBlock.getStateRoot())) { // reverting back one block diff --git a/modAionImpl/src/org/aion/zero/impl/blockchain/AionPendingStateImpl.java b/modAionImpl/src/org/aion/zero/impl/blockchain/AionPendingStateImpl.java index d9deb506ba..da88b5a455 100644 --- a/modAionImpl/src/org/aion/zero/impl/blockchain/AionPendingStateImpl.java +++ b/modAionImpl/src/org/aion/zero/impl/blockchain/AionPendingStateImpl.java @@ -283,7 +283,7 @@ public static AionPendingStateImpl createForTesting( private AionPendingStateImpl(CfgAion _cfgAion, AionRepositoryImpl _repository) { - this.repository = (Repository) _repository; + this.repository = _repository; this.isSeed = _cfgAion.getConsensus().isSeed(); diff --git a/modAionImpl/src/org/aion/zero/impl/db/AionRepositoryImpl.java b/modAionImpl/src/org/aion/zero/impl/db/AionRepositoryImpl.java index d8118dd816..6206061a71 100644 --- a/modAionImpl/src/org/aion/zero/impl/db/AionRepositoryImpl.java +++ b/modAionImpl/src/org/aion/zero/impl/db/AionRepositoryImpl.java @@ -15,7 +15,11 @@ import java.util.Optional; import java.util.Set; -import org.aion.interfaces.db.*; +import org.aion.interfaces.db.ContractDetails; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.interfaces.db.RepositoryConfig; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; +import org.aion.interfaces.db.Repository; import org.aion.mcf.core.AccountState; import org.aion.mcf.db.AbstractRepository; import org.aion.mcf.db.ContractDetailsCacheImpl; @@ -911,7 +915,7 @@ private static class AionRepositoryImplHolder { new AionRepositoryImpl( new RepositoryConfigImpl( config.getDatabasePath(), - (DetailsProvider) ContractDetailsAion.getInstance(), + ContractDetailsAion.getInstance(), config.getDb())); } } diff --git a/modAionImpl/src/org/aion/zero/impl/pow/AionPoW.java b/modAionImpl/src/org/aion/zero/impl/pow/AionPoW.java index abb34894e0..1d1faf5ea1 100644 --- a/modAionImpl/src/org/aion/zero/impl/pow/AionPoW.java +++ b/modAionImpl/src/org/aion/zero/impl/pow/AionPoW.java @@ -10,7 +10,7 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; -import org.aion.equihash.SolutionImpl; +import org.aion.equihash.AionPowSolution; import org.aion.evtmgr.IEvent; import org.aion.evtmgr.IEventMgr; import org.aion.evtmgr.IHandler; @@ -72,7 +72,7 @@ public void run() { createNewBlockTemplate(); } else if (e.getEventType() == IHandler.TYPE.CONSENSUS.getValue() && e.getCallbackType() == EventConsensus.CALLBACK.ON_SOLUTION.getValue()) { - processSolution((SolutionImpl) e.getFuncArgs().get(0)); + processSolution((AionPowSolution) e.getFuncArgs().get(0)); } else if (e.getEventType() == IHandler.TYPE.POISONPILL.getValue()) { go = false; } @@ -191,7 +191,7 @@ public void registerCallback() { * * @param solution The generated equihash solution */ - protected synchronized void processSolution(SolutionImpl solution) { + protected synchronized void processSolution(AionPowSolution solution) { if (!shutDown.get()) { if (LOG.isDebugEnabled()) { LOG.debug("Best block num [{}]", blockchain.getBestBlock().getNumber()); diff --git a/modAionImpl/test/org/aion/db/AionContractDetailsTest.java b/modAionImpl/test/org/aion/db/AionContractDetailsTest.java index 2843e13440..255c1ce827 100644 --- a/modAionImpl/test/org/aion/db/AionContractDetailsTest.java +++ b/modAionImpl/test/org/aion/db/AionContractDetailsTest.java @@ -84,6 +84,7 @@ public void test_1() throws Exception { contractDetails.setCode(code); contractDetails.put(new DataWordImpl(key_1).toWrapper(), new DataWordImpl(val_1).toWrapper()); contractDetails.put(new DataWordImpl(key_2).toWrapper(), new DataWordImpl(val_2).toWrapper()); + contractDetails.setAddress(Address.ZERO_ADDRESS()); byte[] data = contractDetails.getEncoded(); diff --git a/modAionImpl/test/org/aion/db/DoubleDataWordTest.java b/modAionImpl/test/org/aion/db/DoubleDataWordTest.java index e67727b022..4b62573d8b 100644 --- a/modAionImpl/test/org/aion/db/DoubleDataWordTest.java +++ b/modAionImpl/test/org/aion/db/DoubleDataWordTest.java @@ -5,7 +5,11 @@ import java.util.Properties; import java.util.Random; -import org.aion.interfaces.db.*; +import org.aion.interfaces.db.ContractDetails; +import org.aion.interfaces.db.PruneConfig; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.interfaces.db.RepositoryConfig; +import org.aion.interfaces.db.Repository; import org.aion.mcf.vm.types.DataWordImpl; import org.aion.types.Address; import org.aion.crypto.ECKeyFac; diff --git a/modAionImpl/test/org/aion/equihash/EquihashSolutionsGenerationTest210_9.java b/modAionImpl/test/org/aion/equihash/EquihashSolutionsGenerationTest210_9.java index 7ddfcd4e0e..b0edf1d8f7 100644 --- a/modAionImpl/test/org/aion/equihash/EquihashSolutionsGenerationTest210_9.java +++ b/modAionImpl/test/org/aion/equihash/EquihashSolutionsGenerationTest210_9.java @@ -375,7 +375,7 @@ public void testMine_wBlockData(AionBlock block) { // use real method for mine call when(spy.mine(block, header.getNonce())).thenCallRealMethod(); - SolutionImpl sol = spy.mine(block, block.getNonce()); + AionPowSolution sol = spy.mine(block, block.getNonce()); assertNotNull(sol); assertArrayEquals(header.getNonce(), sol.getNonce()); diff --git a/modAionImpl/test/org/aion/zero/impl/AionHubTest.java b/modAionImpl/test/org/aion/zero/impl/AionHubTest.java index ccc9e2588b..90426c19c0 100644 --- a/modAionImpl/test/org/aion/zero/impl/AionHubTest.java +++ b/modAionImpl/test/org/aion/zero/impl/AionHubTest.java @@ -19,7 +19,6 @@ import org.junit.After; import org.junit.BeforeClass; import org.junit.Test; - public class AionHubTest { private void checkHubNullity(AionHub hub) { diff --git a/modAionImpl/test/org/aion/zero/impl/db/AionRepositoryImplTest.java b/modAionImpl/test/org/aion/zero/impl/db/AionRepositoryImplTest.java index 45594d7d69..4ab4143b80 100644 --- a/modAionImpl/test/org/aion/zero/impl/db/AionRepositoryImplTest.java +++ b/modAionImpl/test/org/aion/zero/impl/db/AionRepositoryImplTest.java @@ -5,7 +5,13 @@ import java.math.BigInteger; import java.util.Optional; import java.util.Properties; -import org.aion.interfaces.db.*; + +import org.aion.interfaces.db.ContractDetails; +import org.aion.interfaces.db.PruneConfig; +import org.aion.interfaces.db.RepositoryCache; +import org.aion.interfaces.db.RepositoryConfig; +import org.aion.interfaces.db.ByteArrayKeyValueDatabase; +import org.aion.interfaces.db.Repository; import org.aion.mcf.vm.types.DataWordImpl; import org.aion.types.Address; import org.aion.types.ByteArrayWrapper; diff --git a/modApiServer/src/org/aion/api/server/pb/Message.java b/modApiServer/src/org/aion/api/server/pb/Message.java index a48531eade..d859b60d2a 100644 --- a/modApiServer/src/org/aion/api/server/pb/Message.java +++ b/modApiServer/src/org/aion/api/server/pb/Message.java @@ -71622,7 +71622,7 @@ public com.google.protobuf.ExtensionRegistry assignDescriptors( internal_static_org_aion_api_server_pb_t_BlockDetail_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_aion_api_server_pb_t_BlockDetail_descriptor, - new java.lang.String[] { "BlockNumber", "Timestamp", "NrgConsumed", "NrgLimit", "ParentHash", "MinerAddress", "StateRoot", "TxTrieRoot", "ReceiptTrieRoot", "LogsBloom", "Difficulty", "TotalDifficulty", "ExtraData", "Nonce", "SolutionImpl", "Hash", "Size", "Tx", "BlockTime", }); + new java.lang.String[] { "BlockNumber", "Timestamp", "NrgConsumed", "NrgLimit", "ParentHash", "MinerAddress", "StateRoot", "TxTrieRoot", "ReceiptTrieRoot", "LogsBloom", "Difficulty", "TotalDifficulty", "ExtraData", "Nonce", "Solution", "Hash", "Size", "Tx", "BlockTime", }); internal_static_org_aion_api_server_pb_t_TxDetail_descriptor = getDescriptor().getMessageTypes().get(7); internal_static_org_aion_api_server_pb_t_TxDetail_fieldAccessorTable = new @@ -71640,7 +71640,7 @@ public com.google.protobuf.ExtensionRegistry assignDescriptors( internal_static_org_aion_api_server_pb_t_Block_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_aion_api_server_pb_t_Block_descriptor, - new java.lang.String[] { "BlockNumber", "Timestamp", "NrgConsumed", "NrgLimit", "ParentHash", "MinerAddress", "StateRoot", "TxTrieRoot", "ReceiptTrieRoot", "LogsBloom", "Difficulty", "TotalDifficulty", "ExtraData", "Nonce", "SolutionImpl", "Hash", "Size", "TxHash", }); + new java.lang.String[] { "BlockNumber", "Timestamp", "NrgConsumed", "NrgLimit", "ParentHash", "MinerAddress", "StateRoot", "TxTrieRoot", "ReceiptTrieRoot", "LogsBloom", "Difficulty", "TotalDifficulty", "ExtraData", "Nonce", "Solution", "Hash", "Size", "TxHash", }); internal_static_org_aion_api_server_pb_t_BlockSql_descriptor = getDescriptor().getMessageTypes().get(10); internal_static_org_aion_api_server_pb_t_BlockSql_fieldAccessorTable = new @@ -71676,7 +71676,7 @@ public com.google.protobuf.ExtensionRegistry assignDescriptors( internal_static_org_aion_api_server_pb_rsp_getBlock_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_aion_api_server_pb_rsp_getBlock_descriptor, - new java.lang.String[] { "BlockNumber", "Timestamp", "NrgConsumed", "NrgLimit", "ParentHash", "MinerAddress", "StateRoot", "TxTrieRoot", "ReceiptTrieRoot", "LogsBloom", "Difficulty", "TotalDifficulty", "ExtraData", "Nonce", "SolutionImpl", "Hash", "Size", "TxHash", }); + new java.lang.String[] { "BlockNumber", "Timestamp", "NrgConsumed", "NrgLimit", "ParentHash", "MinerAddress", "StateRoot", "TxTrieRoot", "ReceiptTrieRoot", "LogsBloom", "Difficulty", "TotalDifficulty", "ExtraData", "Nonce", "Solution", "Hash", "Size", "TxHash", }); internal_static_org_aion_api_server_pb_req_getBlockHeaderByNumber_descriptor = getDescriptor().getMessageTypes().get(16); internal_static_org_aion_api_server_pb_req_getBlockHeaderByNumber_fieldAccessorTable = new @@ -71688,7 +71688,7 @@ public com.google.protobuf.ExtensionRegistry assignDescriptors( internal_static_org_aion_api_server_pb_rsp_getBlockHeader_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_aion_api_server_pb_rsp_getBlockHeader_descriptor, - new java.lang.String[] { "BlockNumber", "Timestamp", "NrgConsumed", "NrgLimit", "ParentHash", "MinerAddress", "StateRoot", "TxTrieRoot", "ReceiptTrieRoot", "LogsBloom", "Difficulty", "ExtraData", "Nonce", "SolutionImpl", "Hash", "Size", }); + new java.lang.String[] { "BlockNumber", "Timestamp", "NrgConsumed", "NrgLimit", "ParentHash", "MinerAddress", "StateRoot", "TxTrieRoot", "ReceiptTrieRoot", "LogsBloom", "Difficulty", "ExtraData", "Nonce", "Solution", "Hash", "Size", }); internal_static_org_aion_api_server_pb_req_sendTransaction_descriptor = getDescriptor().getMessageTypes().get(18); internal_static_org_aion_api_server_pb_req_sendTransaction_fieldAccessorTable = new @@ -71910,7 +71910,7 @@ public com.google.protobuf.ExtensionRegistry assignDescriptors( internal_static_org_aion_api_server_pb_req_submitWork_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_org_aion_api_server_pb_req_submitWork_descriptor, - new java.lang.String[] { "Nonce", "SolutionImpl", "Digest", }); + new java.lang.String[] { "Nonce", "Solution", "Digest", }); internal_static_org_aion_api_server_pb_rsp_submitWork_descriptor = getDescriptor().getMessageTypes().get(54); internal_static_org_aion_api_server_pb_rsp_submitWork_fieldAccessorTable = new From 7a56eb89d31962c8dac43b5743e73ba42446876f Mon Sep 17 00:00:00 2001 From: AionJayT <jay.tseng@aion.network> Date: Mon, 4 Mar 2019 17:52:05 -0500 Subject: [PATCH 04/15] 1.) change modLodder file structures 2.) change gradle build logger dependency from source code to the maven repo --- gradle.properties | 2 +- modAion/build.gradle | 2 +- modAionImpl/build.gradle | 2 +- modApiServer/build.gradle | 2 +- modBoot/build.gradle | 2 +- modDbImpl/build.gradle | 2 +- modEvtMgr/build.gradle | 3 +- modEvtMgrImpl/build.gradle | 3 +- modLogger/build.gradle | 108 ++++++++++++++++++ .../src/{ => main/java}/module-info.java | 0 .../java}/org/aion/log/AionLoggerFactory.java | 6 +- .../{ => main/java}/org/aion/log/LogEnum.java | 0 .../java}/org/aion/log/LogLevel.java | 0 .../{ => main/java}/org/aion/log/LogUtil.java | 29 ++--- modMcf/build.gradle | 2 +- modP2pImpl/build.gradle | 2 +- modPrecompiled/build.gradle | 2 +- modTxPoolImpl/build.gradle | 2 +- 18 files changed, 139 insertions(+), 30 deletions(-) rename modLogger/src/{ => main/java}/module-info.java (100%) rename modLogger/src/{ => main/java}/org/aion/log/AionLoggerFactory.java (97%) rename modLogger/src/{ => main/java}/org/aion/log/LogEnum.java (100%) rename modLogger/src/{ => main/java}/org/aion/log/LogLevel.java (100%) rename modLogger/src/{ => main/java}/org/aion/log/LogUtil.java (77%) diff --git a/gradle.properties b/gradle.properties index aa9d5fe7cb..625a1c6c30 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.java.home=/usr/lib/jvm/jdk-11.0.1 org.gradle.daemon=true org.gradle.jvmargs=-Xmx1g -XX:MaxPermSize=4g -XX:ReservedCodeCacheSize=1024m -org.gradle.parallel=false +org.gradle.parallel=true # Uncomment to include modGui in build # modGuiPath=aion_gui diff --git a/modAion/build.gradle b/modAion/build.gradle index ce5b745522..4a2fa8c91d 100644 --- a/modAion/build.gradle +++ b/modAion/build.gradle @@ -13,7 +13,7 @@ dependencies { compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.4' compile group: 'org.apache.commons', name: 'commons-collections4', version: '4.0' - testCompile project(':modLogger') + testCompile 'network.aion:log4j:0.4.0' testCompile project(':modCrypto') testCompile project(':modMcf') testCompile project(':modRlp') diff --git a/modAionImpl/build.gradle b/modAionImpl/build.gradle index f44c926a03..da55b8aaef 100644 --- a/modAionImpl/build.gradle +++ b/modAionImpl/build.gradle @@ -13,12 +13,12 @@ sourceSets { dependencies { compile 'network.aion:vm-api4j:0.4.0' compile 'network.aion:util4j:0.4.0' + compile 'network.aion:log4j:0.4.0' compile project(':modAion') compile project(':modRlp') compile project(':modCrypto') compile project(':modMcf') - compile project(':modLogger') compile project(':modP2pImpl') compile project(':modP2p') compile project(':modEvtMgr') diff --git a/modApiServer/build.gradle b/modApiServer/build.gradle index d281f6910f..1639cbf35c 100644 --- a/modApiServer/build.gradle +++ b/modApiServer/build.gradle @@ -16,12 +16,12 @@ sourceSets { dependencies { compile 'network.aion:vm-api4j:0.4.0' compile 'network.aion:util4j:0.4.0' + compile 'network.aion:log4j:0.4.0' compile project(':modAion') compile project(':modAionImpl') compile project(':aion_fastvm') compile project(':modMcf') - compile project(':modLogger') compile project(':modP2p') compile project(':modEvtMgr') compile project(':modEvtMgrImpl') diff --git a/modBoot/build.gradle b/modBoot/build.gradle index e23efdc52d..844932f5bc 100644 --- a/modBoot/build.gradle +++ b/modBoot/build.gradle @@ -3,11 +3,11 @@ ext.moduleName = 'aion.boot' dependencies { compile 'network.aion:vm-api4j:0.4.0' compile 'network.aion:util4j:0.4.0' + compile 'network.aion:log4j:0.4.0' compile project(':modCrypto') compile project(':modApiServer') compile project(':modAionImpl') - compile project(':modLogger') compile project(':modEvtMgr') compile project(':modP2p') compile project(':modMcf') diff --git a/modDbImpl/build.gradle b/modDbImpl/build.gradle index 8bd024afee..8d43f780a1 100644 --- a/modDbImpl/build.gradle +++ b/modDbImpl/build.gradle @@ -16,8 +16,8 @@ sourceSets { dependencies { compile 'network.aion:vm-api4j:0.4.0' compile 'network.aion:util4j:0.4.0' + compile 'network.aion:log4j:0.4.0' - compile project(':modLogger') compile 'com.google.guava:guava:25.1-jre' compile 'org.slf4j:slf4j-api:1.7.25' compile group: 'org.ethereum', name: 'leveldbjni-all', version: '1.18.3' diff --git a/modEvtMgr/build.gradle b/modEvtMgr/build.gradle index 0ecf1ec692..0694b4fe30 100644 --- a/modEvtMgr/build.gradle +++ b/modEvtMgr/build.gradle @@ -1,8 +1,9 @@ ext.moduleName = 'org.aion.evtmgr' dependencies { + testCompile 'network.aion:log4j:0.4.0' + testCompile project(':modEvtMgrImpl') - testCompile project(':modLogger') testCompile 'junit:junit:4.12' testCompile 'org.hamcrest:hamcrest-all:1.3' testCompile group: 'org.apache.commons', name: 'commons-collections4', version: '4.0' diff --git a/modEvtMgrImpl/build.gradle b/modEvtMgrImpl/build.gradle index 8928b8f182..ed0a1a5b90 100644 --- a/modEvtMgrImpl/build.gradle +++ b/modEvtMgrImpl/build.gradle @@ -1,8 +1,9 @@ ext.moduleName = 'aion.evtmgr.impl' dependencies { + compile 'network.aion:log4j:0.4.0' + compile project(':modEvtMgr') - compile project(':modLogger') compile 'com.google.guava:guava:25.1-jre' testCompile 'junit:junit:4.12' diff --git a/modLogger/build.gradle b/modLogger/build.gradle index cabb586fda..2a4c0a9187 100644 --- a/modLogger/build.gradle +++ b/modLogger/build.gradle @@ -1,7 +1,115 @@ ext.moduleName = 'aion.log' +// set the publish to true when the code ready to push the lib to the maven repo +def publish = false; + +apply plugin: 'maven' +apply plugin: 'signing' + +group = "network.aion" +archivesBaseName = "log4j" + +def getCommitHash = { -> + def hashStdOut = new ByteArrayOutputStream() + exec { + commandLine "sh", "-c", "git log --pretty=format:%h | head -1" + standardOutput = hashStdOut + } + + return hashStdOut.toString().trim() +} + dependencies { compile 'org.slf4j:slf4j-api:1.7.25' compile 'ch.qos.logback:logback-core:1.2.3' compile 'ch.qos.logback:logback-classic:1.2.3' } + +sourceSets { + + if (publish) { + version = "0.4.0" + } else { + jar.baseName = 'crypto4j-' + getCommitHash() + } + + main { + java.srcDirs = ['src/main/java'] + } + test { + java.srcDirs = ['src/test/java'] + } +} + +signing { + sign configurations.archives +} +signArchives.enabled = publish + + +task sourcesJar(type: Jar) { + classifier = 'sources' + from sourceSets.main.allSource +} +sourcesJar.enabled = publish + +javadoc { + inputs.property("moduleName", moduleName) + doFirst { + options.addStringOption('-module-path', classpath.asPath) + options.tags = [ "implNote" ] + } +} + +task javadocJar(type: Jar) { + classifier = 'javadoc' + from javadoc +} +javadocJar.enabled = publish + +artifacts { + archives sourcesJar, javadocJar +} + +uploadArchives { + repositories { + mavenDeployer { + + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + pom.project { + name 'log4j' + packaging 'jar' + // optionally artifactId can be defined here + description 'a log module for the aion java kernel.' + url 'https://github.com/aionnetwork/aion/tree/master-pre-merge/modLogger' + + scm { + connection 'scm:git:https://github.com/aionnetwork/aion.git' + developerConnection 'git:https://github.com/aionnetwork/aion.git' + url 'https://github.com/aionnetwork/aion/tree/master' + } + + licenses { + license { + name 'MIT' + url 'https://opensource.org/licenses/MIT' + } + } + + developers { + developer { + id 'aion foundation dev' + name 'aion foundation dev' + email 'toengineering@aion.network' + } + } + } + } + } +} +uploadArchives.enabled = publish diff --git a/modLogger/src/module-info.java b/modLogger/src/main/java/module-info.java similarity index 100% rename from modLogger/src/module-info.java rename to modLogger/src/main/java/module-info.java diff --git a/modLogger/src/org/aion/log/AionLoggerFactory.java b/modLogger/src/main/java/org/aion/log/AionLoggerFactory.java similarity index 97% rename from modLogger/src/org/aion/log/AionLoggerFactory.java rename to modLogger/src/main/java/org/aion/log/AionLoggerFactory.java index 553d27be7d..4469550a9b 100644 --- a/modLogger/src/org/aion/log/AionLoggerFactory.java +++ b/modLogger/src/main/java/org/aion/log/AionLoggerFactory.java @@ -72,9 +72,7 @@ private static Map<LogEnum, Level> constructModuleLoglevelMap( Map<String, String> moduleToLevelMap = new HashMap<>(); if (_moduleToLevelMap != null) { _moduleToLevelMap.forEach( - (module, level) -> { - moduleToLevelMap.put(module.toUpperCase(), level); - }); + (module, level) -> moduleToLevelMap.put(module.toUpperCase(), level)); } Map<LogEnum, Level> modules = new HashMap<>(); @@ -128,7 +126,7 @@ private static List<Appender<ILoggingEvent>> constructAppenders( RollingFileAppender<ILoggingEvent> fileSync = new RollingFileAppender<>(); - SizeBasedTriggeringPolicy tp = new SizeBasedTriggeringPolicy(); + SizeBasedTriggeringPolicy<ILoggingEvent> tp = new SizeBasedTriggeringPolicy<>(); tp.setContext(context); tp.start(); diff --git a/modLogger/src/org/aion/log/LogEnum.java b/modLogger/src/main/java/org/aion/log/LogEnum.java similarity index 100% rename from modLogger/src/org/aion/log/LogEnum.java rename to modLogger/src/main/java/org/aion/log/LogEnum.java diff --git a/modLogger/src/org/aion/log/LogLevel.java b/modLogger/src/main/java/org/aion/log/LogLevel.java similarity index 100% rename from modLogger/src/org/aion/log/LogLevel.java rename to modLogger/src/main/java/org/aion/log/LogLevel.java diff --git a/modLogger/src/org/aion/log/LogUtil.java b/modLogger/src/main/java/org/aion/log/LogUtil.java similarity index 77% rename from modLogger/src/org/aion/log/LogUtil.java rename to modLogger/src/main/java/org/aion/log/LogUtil.java index ec343631c3..46efe7c2e0 100644 --- a/modLogger/src/org/aion/log/LogUtil.java +++ b/modLogger/src/main/java/org/aion/log/LogUtil.java @@ -12,9 +12,9 @@ public class LogUtil { * 'http://stackoverflow.com/questions/9655181/how-to-convert-a-byte-array-to-a-hex-string-in-java'>stackoverflow * discussion</a> and our benchmark for performance gains */ - protected static final char[] hexArray = "0123456789abcfef".toCharArray(); + private static final char[] hexArray = "0123456789abcdef".toCharArray(); - protected static String toHex(byte[] bytes) { + private static String toHex(byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; for (int j = 0; j < bytes.length; j++) { int v = bytes[j] & 0xFF; @@ -24,16 +24,17 @@ protected static String toHex(byte[] bytes) { return new String(hexChars); } + + private static final String nullString = ""; + /** * Guarantees a return of at max, first 8 characters, even if data is ill formatted (null, * shorter, longer) etc. * - * @param data - * @return + * @param data byte array + * @return first 8 chars of the input HexString */ - protected static final String nullString = ""; - - protected static String toHexF8Internal(byte[] data) { + private static String toHexF8Internal(byte[] data) { if (data == null || data.length == 0) { return nullString; } @@ -49,10 +50,10 @@ protected static String toHexF8Internal(byte[] data) { * Guarantees a return of at max, last 8 characters, even if data is ill formatted (null, * shorter, longer) etc. * - * @param data - * @return + * @param data byte array + * @return last 8 chars of the input HexString */ - public static String toHexL8Internal(byte[] data) { + private static String toHexL8Internal(byte[] data) { if (data == null || data.length == 0) { return nullString; } @@ -68,8 +69,8 @@ public static String toHexL8Internal(byte[] data) { * Guarantees a return of at max, first 8 characters, even if data is ill formatted (null, * shorter, longer) etc. * - * @param data - * @return + * @param data byte array + * @return first 8 chars of the input HexString */ public static String toHexF8(byte[] data) { int len = 0; @@ -82,8 +83,8 @@ public static String toHexF8(byte[] data) { * Guarantees a return of at max, last 8 characters, even if data is ill formatted (null, * shorter, longer) etc. * - * @param data - * @return + * @param data byte array + * @return last 8 chars of the input HexString */ public static String toHexL8(byte[] data) { int len = 0; diff --git a/modMcf/build.gradle b/modMcf/build.gradle index 5d87bb733a..86c824931d 100644 --- a/modMcf/build.gradle +++ b/modMcf/build.gradle @@ -6,8 +6,8 @@ clean.dependsOn deleteNativeLibs dependencies { compile 'network.aion:vm-api4j:0.4.0' compile 'network.aion:util4j:0.4.0' + compile 'network.aion:log4j:0.4.0' - compile project(':modLogger') compile project(':modP2p') compile project(':modCrypto') compile project(':modRlp') diff --git a/modP2pImpl/build.gradle b/modP2pImpl/build.gradle index ae18768b87..94b04a99c5 100644 --- a/modP2pImpl/build.gradle +++ b/modP2pImpl/build.gradle @@ -10,9 +10,9 @@ sourceSets { dependencies { compile 'network.aion:util4j:0.4.0' + compile 'network.aion:log4j:0.4.0' compile project(':modP2p') - compile project(':modLogger') compile files('../lib/miniupnpc_linux.jar') compile 'org.apache.commons:commons-collections4:4.0' compile 'org.slf4j:slf4j-api:1.7.25' diff --git a/modPrecompiled/build.gradle b/modPrecompiled/build.gradle index 963a049a99..4cf5346d28 100644 --- a/modPrecompiled/build.gradle +++ b/modPrecompiled/build.gradle @@ -19,7 +19,7 @@ dependencies { testCompile project(':modRlp') testCompile project(':modPrecompiled') testCompile project(':modAionImpl') - testCompile project(':modLogger') + testCompile 'network.aion:log4j:0.4.0' testCompile project(':modDbImpl') testCompile 'junit:junit:4.12' testCompile 'org.hamcrest:hamcrest-all:1.3' diff --git a/modTxPoolImpl/build.gradle b/modTxPoolImpl/build.gradle index f9acecf4b2..1698f6c18d 100644 --- a/modTxPoolImpl/build.gradle +++ b/modTxPoolImpl/build.gradle @@ -5,9 +5,9 @@ clean.dependsOn deleteNativeLibs dependencies { compile 'network.aion:vm-api4j:0.4.0' compile 'network.aion:util4j:0.4.0' + compile 'network.aion:log4j:0.4.0' compile project(':modTxPool') - compile project(':modLogger') compile 'com.madgag.spongycastle:prov:1.58.0.0' compile 'com.madgag.spongycastle:core:1.58.0.0' From 792badc6c1aa7d99907d9a73f23c7a513a993255 Mon Sep 17 00:00:00 2001 From: AionJayT <jay.tseng@aion.network> Date: Tue, 5 Mar 2019 14:08:29 -0500 Subject: [PATCH 05/15] 1.) change modRlp file structures 2.) change gradle build rlp dependency from source code to the maven repo --- modAion/build.gradle | 3 +- modAionImpl/build.gradle | 2 +- modCrypto/build.gradle | 2 +- modMcf/build.gradle | 2 +- modRlp/build.gradle | 112 +++- modRlp/src/{ => main/java}/module-info.java | 0 .../java}/org/aion/rlp/CompactEncoder.java | 0 .../java}/org/aion/rlp/DecodeResult.java | 0 .../src/{ => main/java}/org/aion/rlp/RLP.java | 16 +- .../java}/org/aion/rlp/RLPElement.java | 0 .../{ => main/java}/org/aion/rlp/RLPItem.java | 4 +- .../{ => main/java}/org/aion/rlp/RLPList.java | 0 .../{ => main/java}/org/aion/rlp/Utils.java | 0 .../{ => main/java}/org/aion/rlp/Value.java | 0 .../org/aion/rlp/CompactEncoderTest.java | 0 .../java}/org/aion/rlp/DecodeResultTest.java | 0 .../test/java}/org/aion/rlp/RLPDump.java | 0 .../java}/org/aion/rlp/RLPElementTest.java | 0 .../java}/org/aion/rlp/RLPSpecExtraTest.java | 0 .../test/java}/org/aion/rlp/RLPSpecTest.java | 0 .../test/java}/org/aion/rlp/RLPTest.java | 6 +- .../test/java}/org/aion/rlp/RlpTestData.java | 0 .../test/java}/org/aion/rlp/UtilsTest.java | 0 .../test/java}/org/aion/rlp/ValueTest.java | 2 +- modRlp/test/org/aion/rlp/ByteUtilTest.java | 484 ------------------ modTxPoolImpl/build.gradle | 2 +- 26 files changed, 121 insertions(+), 514 deletions(-) rename modRlp/src/{ => main/java}/module-info.java (100%) rename modRlp/src/{ => main/java}/org/aion/rlp/CompactEncoder.java (100%) rename modRlp/src/{ => main/java}/org/aion/rlp/DecodeResult.java (100%) rename modRlp/src/{ => main/java}/org/aion/rlp/RLP.java (98%) rename modRlp/src/{ => main/java}/org/aion/rlp/RLPElement.java (100%) rename modRlp/src/{ => main/java}/org/aion/rlp/RLPItem.java (83%) rename modRlp/src/{ => main/java}/org/aion/rlp/RLPList.java (100%) rename modRlp/src/{ => main/java}/org/aion/rlp/Utils.java (100%) rename modRlp/src/{ => main/java}/org/aion/rlp/Value.java (100%) rename modRlp/{test => src/test/java}/org/aion/rlp/CompactEncoderTest.java (100%) rename modRlp/{test => src/test/java}/org/aion/rlp/DecodeResultTest.java (100%) rename modRlp/{test => src/test/java}/org/aion/rlp/RLPDump.java (100%) rename modRlp/{test => src/test/java}/org/aion/rlp/RLPElementTest.java (100%) rename modRlp/{test => src/test/java}/org/aion/rlp/RLPSpecExtraTest.java (100%) rename modRlp/{test => src/test/java}/org/aion/rlp/RLPSpecTest.java (100%) rename modRlp/{test => src/test/java}/org/aion/rlp/RLPTest.java (99%) rename modRlp/{test => src/test/java}/org/aion/rlp/RlpTestData.java (100%) rename modRlp/{test => src/test/java}/org/aion/rlp/UtilsTest.java (100%) rename modRlp/{test => src/test/java}/org/aion/rlp/ValueTest.java (98%) delete mode 100644 modRlp/test/org/aion/rlp/ByteUtilTest.java diff --git a/modAion/build.gradle b/modAion/build.gradle index 4a2fa8c91d..7ad1a58044 100644 --- a/modAion/build.gradle +++ b/modAion/build.gradle @@ -5,8 +5,8 @@ test.dependsOn copyNativeLibsForModuleTests dependencies { compile 'network.aion:vm-api4j:0.4.0' compile 'network.aion:util4j:0.4.0' + compile 'network.aion:rlp4j:0.4.0' - compile project(':modRlp') compile project(':modCrypto') compile project(':modMcf') compile 'org.json:json:20180813' @@ -16,7 +16,6 @@ dependencies { testCompile 'network.aion:log4j:0.4.0' testCompile project(':modCrypto') testCompile project(':modMcf') - testCompile project(':modRlp') testCompile 'junit:junit:4.12' testCompile 'org.hamcrest:hamcrest-all:1.3' testCompile group: 'org.apache.commons', name: 'commons-lang3', version: '3.4' diff --git a/modAionImpl/build.gradle b/modAionImpl/build.gradle index da55b8aaef..24e4f1875d 100644 --- a/modAionImpl/build.gradle +++ b/modAionImpl/build.gradle @@ -14,9 +14,9 @@ dependencies { compile 'network.aion:vm-api4j:0.4.0' compile 'network.aion:util4j:0.4.0' compile 'network.aion:log4j:0.4.0' + compile 'network.aion:rlp4j:0.4.0' compile project(':modAion') - compile project(':modRlp') compile project(':modCrypto') compile project(':modMcf') compile project(':modP2pImpl') diff --git a/modCrypto/build.gradle b/modCrypto/build.gradle index a0bd4217c8..c62b29c843 100644 --- a/modCrypto/build.gradle +++ b/modCrypto/build.gradle @@ -2,8 +2,8 @@ ext.moduleName = 'aion.crypto' dependencies { compile 'network.aion:util4j:0.4.0' + compile 'network.aion:rlp4j:0.4.0' - compile project(':modRlp') compile 'com.madgag.spongycastle:prov:1.58.0.0' compile 'com.madgag.spongycastle:core:1.58.0.0' compile 'org.slf4j:slf4j-api:1.7.25' diff --git a/modMcf/build.gradle b/modMcf/build.gradle index 86c824931d..8f65b6c513 100644 --- a/modMcf/build.gradle +++ b/modMcf/build.gradle @@ -7,10 +7,10 @@ dependencies { compile 'network.aion:vm-api4j:0.4.0' compile 'network.aion:util4j:0.4.0' compile 'network.aion:log4j:0.4.0' + compile 'network.aion:rlp4j:0.4.0' compile project(':modP2p') compile project(':modCrypto') - compile project(':modRlp') compile project(':modDbImpl') compile 'com.madgag.spongycastle:prov:1.58.0.0' compile 'com.madgag.spongycastle:core:1.58.0.0' diff --git a/modRlp/build.gradle b/modRlp/build.gradle index f22aa34a16..410c48a2c6 100644 --- a/modRlp/build.gradle +++ b/modRlp/build.gradle @@ -1,12 +1,27 @@ ext.moduleName = 'aion.rlp' +// set the publish to true when the code ready to push the lib to the maven repo +def publish = false; + +apply plugin: 'maven' +apply plugin: 'signing' + +group = "network.aion" +archivesBaseName = "rlp4j" + +def getCommitHash = { -> + def hashStdOut = new ByteArrayOutputStream() + exec { + commandLine "sh", "-c", "git log --pretty=format:%h | head -1" + standardOutput = hashStdOut + } + + return hashStdOut.toString().trim() +} + dependencies { compile 'network.aion:util4j:0.4.0' - compile 'com.madgag.spongycastle:prov:1.58.0.0' - compile 'com.madgag.spongycastle:core:1.58.0.0' - compile 'com.google.guava:guava:25.1-jre' - testCompile 'junit:junit:4.12' testCompile 'pl.pragmatists:JUnitParams:1.1.1' testCompile 'org.hamcrest:hamcrest-all:1.3' @@ -18,3 +33,92 @@ tasks.withType(JavaCompile) { println "Args for for $name are $options.allCompilerArgs" } } + +sourceSets { + if (publish) { + version = "0.4.0" + } else { + jar.baseName = 'rlp4j-' + getCommitHash() + } + + main { + java.srcDirs = ['src/main/java'] + } + test { + java.srcDirs = ['src/test/java'] + } +} + +signing { + sign configurations.archives +} +signArchives.enabled = publish + + +task sourcesJar(type: Jar) { + classifier = 'sources' + from sourceSets.main.allSource +} +sourcesJar.enabled = publish + +javadoc { + inputs.property("moduleName", moduleName) + doFirst { + options.addStringOption('-module-path', classpath.asPath) + options.tags = [ "implNote" ] + } +} + +task javadocJar(type: Jar) { + classifier = 'javadoc' + from javadoc +} +javadocJar.enabled = publish + +artifacts { + archives sourcesJar, javadocJar +} + +uploadArchives { + repositories { + mavenDeployer { + + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + pom.project { + name 'rlp4j' + packaging 'jar' + // optionally artifactId can be defined here + description 'a rlp module for the aion java kernel.' + url 'https://github.com/aionnetwork/aion/tree/master-pre-merge/modRlp' + + scm { + connection 'scm:git:https://github.com/aionnetwork/aion.git' + developerConnection 'git:https://github.com/aionnetwork/aion.git' + url 'https://github.com/aionnetwork/aion/tree/master' + } + + licenses { + license { + name 'MIT' + url 'https://opensource.org/licenses/MIT' + } + } + + developers { + developer { + id 'aion foundation dev' + name 'aion foundation dev' + email 'toengineering@aion.network' + } + } + } + } + } +} +uploadArchives.enabled = publish + diff --git a/modRlp/src/module-info.java b/modRlp/src/main/java/module-info.java similarity index 100% rename from modRlp/src/module-info.java rename to modRlp/src/main/java/module-info.java diff --git a/modRlp/src/org/aion/rlp/CompactEncoder.java b/modRlp/src/main/java/org/aion/rlp/CompactEncoder.java similarity index 100% rename from modRlp/src/org/aion/rlp/CompactEncoder.java rename to modRlp/src/main/java/org/aion/rlp/CompactEncoder.java diff --git a/modRlp/src/org/aion/rlp/DecodeResult.java b/modRlp/src/main/java/org/aion/rlp/DecodeResult.java similarity index 100% rename from modRlp/src/org/aion/rlp/DecodeResult.java rename to modRlp/src/main/java/org/aion/rlp/DecodeResult.java diff --git a/modRlp/src/org/aion/rlp/RLP.java b/modRlp/src/main/java/org/aion/rlp/RLP.java similarity index 98% rename from modRlp/src/org/aion/rlp/RLP.java rename to modRlp/src/main/java/org/aion/rlp/RLP.java index 169e74a4e1..b9a22f848c 100644 --- a/modRlp/src/org/aion/rlp/RLP.java +++ b/modRlp/src/main/java/org/aion/rlp/RLP.java @@ -259,27 +259,15 @@ private static void fullTraverse( } } } catch (Exception e) { - - // Only shown first 4K data - int length = endPos - startPos; - if (length > 4096) { - length = 4096; - } - throw new RuntimeException( "RLP wrong encoding (" + Hex.toHexString(msgData, startPos, endPos - startPos > 1024 ? 1024 : endPos - startPos) + ")", e); } catch (OutOfMemoryError e) { - // Only shown first 4K data - int length = endPos - startPos; - if (length > 4096) { - length = 4096; - } throw new RuntimeException( "Invalid RLP (excessive mem allocation while parsing) (" - + Hex.toHexString(msgData, startPos, length) + + Hex.toHexString(msgData, startPos, endPos - startPos) + ")", e); } @@ -353,7 +341,7 @@ private static DecodeResult decodeList(byte[] data, int pos, int len) { /** * Turn Object into its RLP encoded equivalent of a byte-array Support for String, Integer, - * BigInteger and Lists of any of these types. + * BigInteger and Lists of any of these type. * * @param input as object or List of objects * @return byte[] RLP encoded diff --git a/modRlp/src/org/aion/rlp/RLPElement.java b/modRlp/src/main/java/org/aion/rlp/RLPElement.java similarity index 100% rename from modRlp/src/org/aion/rlp/RLPElement.java rename to modRlp/src/main/java/org/aion/rlp/RLPElement.java diff --git a/modRlp/src/org/aion/rlp/RLPItem.java b/modRlp/src/main/java/org/aion/rlp/RLPItem.java similarity index 83% rename from modRlp/src/org/aion/rlp/RLPItem.java rename to modRlp/src/main/java/org/aion/rlp/RLPItem.java index 8f0f24d701..96b6559d4d 100644 --- a/modRlp/src/org/aion/rlp/RLPItem.java +++ b/modRlp/src/main/java/org/aion/rlp/RLPItem.java @@ -13,10 +13,10 @@ public class RLPItem implements RLPElement { private final byte[] rlpData; /** - * @Jay inside the RLP encode/decode logic, there is no difference between null obj and + * @implNote Inside the RLP encode/decode logic, there is no difference between null obj and * zero-byte array Therefore, put empty array when we see the input data is null * - * @param rlpData + * @param rlpData byte array represent the encoded rlp data */ public RLPItem(byte[] rlpData) { this.rlpData = (rlpData == null) ? ByteUtil.EMPTY_BYTE_ARRAY : rlpData; diff --git a/modRlp/src/org/aion/rlp/RLPList.java b/modRlp/src/main/java/org/aion/rlp/RLPList.java similarity index 100% rename from modRlp/src/org/aion/rlp/RLPList.java rename to modRlp/src/main/java/org/aion/rlp/RLPList.java diff --git a/modRlp/src/org/aion/rlp/Utils.java b/modRlp/src/main/java/org/aion/rlp/Utils.java similarity index 100% rename from modRlp/src/org/aion/rlp/Utils.java rename to modRlp/src/main/java/org/aion/rlp/Utils.java diff --git a/modRlp/src/org/aion/rlp/Value.java b/modRlp/src/main/java/org/aion/rlp/Value.java similarity index 100% rename from modRlp/src/org/aion/rlp/Value.java rename to modRlp/src/main/java/org/aion/rlp/Value.java diff --git a/modRlp/test/org/aion/rlp/CompactEncoderTest.java b/modRlp/src/test/java/org/aion/rlp/CompactEncoderTest.java similarity index 100% rename from modRlp/test/org/aion/rlp/CompactEncoderTest.java rename to modRlp/src/test/java/org/aion/rlp/CompactEncoderTest.java diff --git a/modRlp/test/org/aion/rlp/DecodeResultTest.java b/modRlp/src/test/java/org/aion/rlp/DecodeResultTest.java similarity index 100% rename from modRlp/test/org/aion/rlp/DecodeResultTest.java rename to modRlp/src/test/java/org/aion/rlp/DecodeResultTest.java diff --git a/modRlp/test/org/aion/rlp/RLPDump.java b/modRlp/src/test/java/org/aion/rlp/RLPDump.java similarity index 100% rename from modRlp/test/org/aion/rlp/RLPDump.java rename to modRlp/src/test/java/org/aion/rlp/RLPDump.java diff --git a/modRlp/test/org/aion/rlp/RLPElementTest.java b/modRlp/src/test/java/org/aion/rlp/RLPElementTest.java similarity index 100% rename from modRlp/test/org/aion/rlp/RLPElementTest.java rename to modRlp/src/test/java/org/aion/rlp/RLPElementTest.java diff --git a/modRlp/test/org/aion/rlp/RLPSpecExtraTest.java b/modRlp/src/test/java/org/aion/rlp/RLPSpecExtraTest.java similarity index 100% rename from modRlp/test/org/aion/rlp/RLPSpecExtraTest.java rename to modRlp/src/test/java/org/aion/rlp/RLPSpecExtraTest.java diff --git a/modRlp/test/org/aion/rlp/RLPSpecTest.java b/modRlp/src/test/java/org/aion/rlp/RLPSpecTest.java similarity index 100% rename from modRlp/test/org/aion/rlp/RLPSpecTest.java rename to modRlp/src/test/java/org/aion/rlp/RLPSpecTest.java diff --git a/modRlp/test/org/aion/rlp/RLPTest.java b/modRlp/src/test/java/org/aion/rlp/RLPTest.java similarity index 99% rename from modRlp/test/org/aion/rlp/RLPTest.java rename to modRlp/src/test/java/org/aion/rlp/RLPTest.java index 6026016edd..cbba08c101 100644 --- a/modRlp/test/org/aion/rlp/RLPTest.java +++ b/modRlp/src/test/java/org/aion/rlp/RLPTest.java @@ -40,13 +40,13 @@ import static org.aion.rlp.RlpTestData.test13; import static org.aion.rlp.RlpTestData.test16; import static org.aion.util.bytes.ByteUtil.byteArrayToInt; +import static org.aion.util.conversions.Hex.encode; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -import static org.spongycastle.util.encoders.Hex.encode; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -981,14 +981,14 @@ public void testRlpDecode() { decodedData = (byte[]) decode(Hex.decode(result13), pos).getDecoded(); assertTrue(test13.compareTo(new BigInteger(1, decodedData)) == 0); - // Need to test with different expected value, because decoding doesn't recognize types + // Need to test with different expected value, because decoding doesn't recognize type Object testObject1 = decode(Hex.decode(result14), pos).getDecoded(); assertTrue(Objects.deepEquals(expected14, testObject1)); // Object testObject2 = decode(Hex.decode(result15), pos).getDecoded(); // assertTrue(DeepEquals.deepEquals(test15, testObject2)); - // Need to test with different expected value, because decoding doesn't recognize types + // Need to test with different expected value, because decoding doesn't recognize type Object testObject3 = decode(Hex.decode(result16), pos).getDecoded(); assertTrue(Objects.deepEquals(expected16, testObject3)); } diff --git a/modRlp/test/org/aion/rlp/RlpTestData.java b/modRlp/src/test/java/org/aion/rlp/RlpTestData.java similarity index 100% rename from modRlp/test/org/aion/rlp/RlpTestData.java rename to modRlp/src/test/java/org/aion/rlp/RlpTestData.java diff --git a/modRlp/test/org/aion/rlp/UtilsTest.java b/modRlp/src/test/java/org/aion/rlp/UtilsTest.java similarity index 100% rename from modRlp/test/org/aion/rlp/UtilsTest.java rename to modRlp/src/test/java/org/aion/rlp/UtilsTest.java diff --git a/modRlp/test/org/aion/rlp/ValueTest.java b/modRlp/src/test/java/org/aion/rlp/ValueTest.java similarity index 98% rename from modRlp/test/org/aion/rlp/ValueTest.java rename to modRlp/src/test/java/org/aion/rlp/ValueTest.java index 77b068e26a..e1bba8f4ea 100644 --- a/modRlp/test/org/aion/rlp/ValueTest.java +++ b/modRlp/src/test/java/org/aion/rlp/ValueTest.java @@ -7,8 +7,8 @@ import java.math.BigInteger; import java.util.Arrays; +import org.aion.util.conversions.Hex; import org.junit.Test; -import org.spongycastle.util.encoders.Hex; public class ValueTest { diff --git a/modRlp/test/org/aion/rlp/ByteUtilTest.java b/modRlp/test/org/aion/rlp/ByteUtilTest.java deleted file mode 100644 index db58724b08..0000000000 --- a/modRlp/test/org/aion/rlp/ByteUtilTest.java +++ /dev/null @@ -1,484 +0,0 @@ -package org.aion.rlp; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import org.aion.util.bytes.ByteUtil; -import org.aion.util.conversions.Hex; -import org.junit.Assert; -import org.junit.Test; -import org.spongycastle.util.BigIntegers; - -/** TODO: functionality should be moved to modUtil */ -public class ByteUtilTest { - - @Test - public void testAppendByte() { - byte[] bytes = "tes".getBytes(); - byte b = 0x74; - Assert.assertArrayEquals("test".getBytes(), ByteUtil.appendByte(bytes, b)); - } - - @Test - public void testBigIntegerToBytes() { - byte[] expecteds = new byte[] {(byte) 0xff, (byte) 0xec, 0x78}; - BigInteger b = BigInteger.valueOf(16772216); - byte[] actuals = ByteUtil.bigIntegerToBytes(b); - assertArrayEquals(expecteds, actuals); - } - - @Test - public void testBigIntegerToBytesSign() { - { - BigInteger b = BigInteger.valueOf(-2); - byte[] actuals = ByteUtil.bigIntegerToBytesSigned(b, 8); - assertArrayEquals(Hex.decode("fffffffffffffffe"), actuals); - } - { - BigInteger b = BigInteger.valueOf(2); - byte[] actuals = ByteUtil.bigIntegerToBytesSigned(b, 8); - assertArrayEquals(Hex.decode("0000000000000002"), actuals); - } - { - BigInteger b = BigInteger.valueOf(0); - byte[] actuals = ByteUtil.bigIntegerToBytesSigned(b, 8); - assertArrayEquals(Hex.decode("0000000000000000"), actuals); - } - { - BigInteger b = new BigInteger("eeeeeeeeeeeeee", 16); - byte[] actuals = ByteUtil.bigIntegerToBytesSigned(b, 8); - assertArrayEquals(Hex.decode("00eeeeeeeeeeeeee"), actuals); - } - { - BigInteger b = new BigInteger("eeeeeeeeeeeeeeee", 16); - byte[] actuals = ByteUtil.bigIntegerToBytesSigned(b, 8); - assertArrayEquals(Hex.decode("eeeeeeeeeeeeeeee"), actuals); - } - } - - @Test - public void testBigIntegerToBytesNegative() { - byte[] expecteds = new byte[] {(byte) 0xff, 0x0, 0x13, (byte) 0x88}; - BigInteger b = BigInteger.valueOf(-16772216); - byte[] actuals = ByteUtil.bigIntegerToBytes(b); - assertArrayEquals(expecteds, actuals); - } - - @Test - public void testBigIntegerToBytesZero() { - byte[] expecteds = new byte[] {0x00}; - BigInteger b = BigInteger.ZERO; - byte[] actuals = ByteUtil.bigIntegerToBytes(b); - assertArrayEquals(expecteds, actuals); - } - - @Test - public void testToHexString() { - assertEquals("", ByteUtil.toHexString(null)); - } - - @Test - public void testCalcPacketLength() { - byte[] test = new byte[] {0x0f, 0x10, 0x43}; - byte[] expected = new byte[] {0x00, 0x00, 0x00, 0x03}; - assertArrayEquals(expected, ByteUtil.calcPacketLength(test)); - } - - @Test - public void testByteArrayToInt() { - assertEquals(0, ByteUtil.byteArrayToInt(null)); - assertEquals(0, ByteUtil.byteArrayToInt(new byte[0])); - - // byte[] x = new byte[] { 5,1,7,0,8 }; - // long start = System.currentTimeMillis(); - // for (int i = 0; i < 100000000; i++) { - // ByteArray.read32bit(x, 0); - // } - // long end = System.currentTimeMillis(); - // System.out.println(end - start + "ms"); - // - // long start1 = System.currentTimeMillis(); - // for (int i = 0; i < 100000000; i++) { - // new BigInteger(1, x).intValue(); - // } - // long end1 = System.currentTimeMillis(); - // System.out.println(end1 - start1 + "ms"); - } - - @Test - public void testNumBytes() { - String test1 = "0"; - String test2 = "1"; - String test3 = "1000000000"; // 3B9ACA00 - int expected1 = 1; - int expected2 = 1; - int expected3 = 4; - assertEquals(expected1, ByteUtil.numBytes(test1)); - assertEquals(expected2, ByteUtil.numBytes(test2)); - assertEquals(expected3, ByteUtil.numBytes(test3)); - } - - @Test - public void testStripLeadingZeroes() { - byte[] test1 = null; - byte[] test2 = new byte[] {}; - byte[] test3 = new byte[] {0x00}; - byte[] test4 = new byte[] {0x00, 0x01}; - byte[] test5 = new byte[] {0x00, 0x00, 0x01}; - byte[] expected1 = null; - byte[] expected2 = new byte[] {0}; - byte[] expected3 = new byte[] {0}; - byte[] expected4 = new byte[] {0x01}; - byte[] expected5 = new byte[] {0x01}; - assertArrayEquals(expected1, ByteUtil.stripLeadingZeroes(test1)); - assertArrayEquals(expected2, ByteUtil.stripLeadingZeroes(test2)); - assertArrayEquals(expected3, ByteUtil.stripLeadingZeroes(test3)); - assertArrayEquals(expected4, ByteUtil.stripLeadingZeroes(test4)); - assertArrayEquals(expected5, ByteUtil.stripLeadingZeroes(test5)); - } - - @Test - public void testMatchingNibbleLength1() { - // a larger than b - byte[] a = new byte[] {0x00, 0x01}; - byte[] b = new byte[] {0x00}; - int result = ByteUtil.matchingNibbleLength(a, b); - assertEquals(1, result); - } - - @Test - public void testMatchingNibbleLength2() { - // b larger than a - byte[] a = new byte[] {0x00}; - byte[] b = new byte[] {0x00, 0x01}; - int result = ByteUtil.matchingNibbleLength(a, b); - assertEquals(1, result); - } - - @Test - public void testMatchingNibbleLength3() { - // a and b the same length equal - byte[] a = new byte[] {0x00}; - byte[] b = new byte[] {0x00}; - int result = ByteUtil.matchingNibbleLength(a, b); - assertEquals(1, result); - } - - @Test - public void testMatchingNibbleLength4() { - // a and b the same length not equal - byte[] a = new byte[] {0x01}; - byte[] b = new byte[] {0x00}; - int result = ByteUtil.matchingNibbleLength(a, b); - assertEquals(0, result); - } - - @Test - public void testNiceNiblesOutput_1() { - byte[] test = {7, 0, 7, 5, 7, 0, 7, 0, 7, 9}; - String result = "\\x07\\x00\\x07\\x05\\x07\\x00\\x07\\x00\\x07\\x09"; - assertEquals(result, ByteUtil.nibblesToPrettyString(test)); - } - - @Test - public void testNiceNiblesOutput_2() { - byte[] test = {7, 0, 7, 0xf, 7, 0, 0xa, 0, 7, 9}; - String result = "\\x07\\x00\\x07\\x0f\\x07\\x00\\x0a\\x00\\x07\\x09"; - assertEquals(result, ByteUtil.nibblesToPrettyString(test)); - } - - @Test(expected = NullPointerException.class) - public void testMatchingNibbleLength5() { - // a == null - byte[] a = null; - byte[] b = new byte[] {0x00}; - ByteUtil.matchingNibbleLength(a, b); - } - - @Test(expected = NullPointerException.class) - public void testMatchingNibbleLength6() { - // b == null - byte[] a = new byte[] {0x00}; - byte[] b = null; - ByteUtil.matchingNibbleLength(a, b); - } - - @Test - public void testMatchingNibbleLength7() { - // a or b is empty - byte[] a = new byte[0]; - byte[] b = new byte[] {0x00}; - int result = ByteUtil.matchingNibbleLength(a, b); - assertEquals(0, result); - } - - /** - * This test shows the difference between iterating over, and comparing byte[] vs BigInteger - * value. - * - * <p>Results indicate that the former has ~15x better performance. Therefore this is used in - * the Miner.mine() method. - */ - @Test - public void testIncrementPerformance() { - boolean testEnabled = false; - - if (testEnabled) { - byte[] counter1 = new byte[4]; - byte[] max = ByteBuffer.allocate(4).putInt(Integer.MAX_VALUE).array(); - long start1 = System.currentTimeMillis(); - while (ByteUtil.increment(counter1)) { - if (compareTo(counter1, 0, 4, max, 0, 4) == 0) { - break; - } - } - System.out.println( - System.currentTimeMillis() - - start1 - + "ms to reach: " - + Hex.toHexString(counter1)); - - BigInteger counter2 = BigInteger.ZERO; - long start2 = System.currentTimeMillis(); - while (true) { - if (counter2.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) == 0) { - break; - } - counter2 = counter2.add(BigInteger.ONE); - } - System.out.println( - System.currentTimeMillis() - - start2 - + "ms to reach: " - + Hex.toHexString(BigIntegers.asUnsignedByteArray(4, counter2))); - } - } - - /** Compares two regions of byte array. */ - public static int compareTo( - byte[] array1, int offset1, int size1, byte[] array2, int offset2, int size2) { - byte[] b1 = Arrays.copyOfRange(array1, offset1, offset1 + size1); - byte[] b2 = Arrays.copyOfRange(array2, offset2, offset2 + size2); - - return Arrays.compare(b1, b2); - } - - @Test - public void firstNonZeroByte_1() { - - byte[] data = - Hex.decode("0000000000000000000000000000000000000000000000000000000000000000"); - int result = ByteUtil.firstNonZeroByte(data); - - assertEquals(-1, result); - } - - @Test - public void firstNonZeroByte_2() { - - byte[] data = - Hex.decode("0000000000000000000000000000000000000000000000000000000000332211"); - int result = ByteUtil.firstNonZeroByte(data); - - assertEquals(29, result); - } - - @Test - public void firstNonZeroByte_3() { - - byte[] data = - Hex.decode("2211009988776655443322110099887766554433221100998877665544332211"); - int result = ByteUtil.firstNonZeroByte(data); - - assertEquals(0, result); - } - - @Test - public void setBitTest() { - /* - Set on - */ - byte[] data = ByteBuffer.allocate(4).putInt(0).array(); - int posBit = 24; - int expected = 16777216; - int result = -1; - byte[] ret = ByteUtil.setBit(data, posBit, 1); - result = ByteUtil.byteArrayToInt(ret); - assertTrue(expected == result); - - posBit = 25; - expected = 50331648; - ret = ByteUtil.setBit(data, posBit, 1); - result = ByteUtil.byteArrayToInt(ret); - assertTrue(expected == result); - - posBit = 2; - expected = 50331652; - ret = ByteUtil.setBit(data, posBit, 1); - result = ByteUtil.byteArrayToInt(ret); - assertTrue(expected == result); - - /* - Set off - */ - posBit = 24; - expected = 33554436; - ret = ByteUtil.setBit(data, posBit, 0); - result = ByteUtil.byteArrayToInt(ret); - assertTrue(expected == result); - - posBit = 25; - expected = 4; - ret = ByteUtil.setBit(data, posBit, 0); - result = ByteUtil.byteArrayToInt(ret); - assertTrue(expected == result); - - posBit = 2; - expected = 0; - ret = ByteUtil.setBit(data, posBit, 0); - result = ByteUtil.byteArrayToInt(ret); - assertTrue(expected == result); - } - - @Test - public void getBitTest() { - byte[] data = ByteBuffer.allocate(4).putInt(0).array(); - ByteUtil.setBit(data, 24, 1); - ByteUtil.setBit(data, 25, 1); - ByteUtil.setBit(data, 2, 1); - - List<Integer> found = new ArrayList<>(); - for (int i = 0; i < (data.length * 8); i++) { - int res = ByteUtil.getBit(data, i); - if (res == 1) { - if (i != 24 && i != 25 && i != 2) { - assertTrue(false); - } else { - found.add(i); - } - } else { - if (i == 24 || i == 25 || i == 2) { - assertTrue(false); - } - } - } - - if (found.size() != 3) { - assertTrue(false); - } - assertTrue(found.get(0) == 2); - assertTrue(found.get(1) == 24); - assertTrue(found.get(2) == 25); - } - - @Test - public void numToBytesTest() { - byte[] bytes = ByteUtil.intToBytesNoLeadZeroes(-1); - assertArrayEquals(bytes, Hex.decode("ffffffff")); - bytes = ByteUtil.intToBytesNoLeadZeroes(1); - assertArrayEquals(bytes, Hex.decode("01")); - bytes = ByteUtil.intToBytesNoLeadZeroes(255); - assertArrayEquals(bytes, Hex.decode("ff")); - bytes = ByteUtil.intToBytesNoLeadZeroes(256); - assertArrayEquals(bytes, Hex.decode("0100")); - bytes = ByteUtil.intToBytesNoLeadZeroes(0); - assertArrayEquals(bytes, new byte[0]); - - bytes = ByteUtil.intToBytes(-1); - assertArrayEquals(bytes, Hex.decode("ffffffff")); - bytes = ByteUtil.intToBytes(1); - assertArrayEquals(bytes, Hex.decode("00000001")); - bytes = ByteUtil.intToBytes(255); - assertArrayEquals(bytes, Hex.decode("000000ff")); - bytes = ByteUtil.intToBytes(256); - assertArrayEquals(bytes, Hex.decode("00000100")); - bytes = ByteUtil.intToBytes(0); - assertArrayEquals(bytes, Hex.decode("00000000")); - - bytes = ByteUtil.longToBytesNoLeadZeroes(-1); - assertArrayEquals(bytes, Hex.decode("ffffffffffffffff")); - bytes = ByteUtil.longToBytesNoLeadZeroes(1); - assertArrayEquals(bytes, Hex.decode("01")); - bytes = ByteUtil.longToBytesNoLeadZeroes(255); - assertArrayEquals(bytes, Hex.decode("ff")); - bytes = ByteUtil.longToBytesNoLeadZeroes(1L << 32); - assertArrayEquals(bytes, Hex.decode("0100000000")); - bytes = ByteUtil.longToBytesNoLeadZeroes(0); - assertArrayEquals(bytes, new byte[0]); - - bytes = ByteUtil.longToBytes(-1); - assertArrayEquals(bytes, Hex.decode("ffffffffffffffff")); - bytes = ByteUtil.longToBytes(1); - assertArrayEquals(bytes, Hex.decode("0000000000000001")); - bytes = ByteUtil.longToBytes(255); - assertArrayEquals(bytes, Hex.decode("00000000000000ff")); - bytes = ByteUtil.longToBytes(256); - assertArrayEquals(bytes, Hex.decode("0000000000000100")); - bytes = ByteUtil.longToBytes(0); - assertArrayEquals(bytes, Hex.decode("0000000000000000")); - } - - @Test - public void testHexStringToBytes() { - { - String str = "0000"; - byte[] actuals = ByteUtil.hexStringToBytes(str); - byte[] expected = new byte[] {0, 0}; - assertArrayEquals(expected, actuals); - } - { - String str = "0x0000"; - byte[] actuals = ByteUtil.hexStringToBytes(str); - byte[] expected = new byte[] {0, 0}; - assertArrayEquals(expected, actuals); - } - { - String str = "0x45a6"; - byte[] actuals = ByteUtil.hexStringToBytes(str); - byte[] expected = new byte[] {69, -90}; - assertArrayEquals(expected, actuals); - } - { - String str = "1963093cee500c081443e1045c40264b670517af"; - byte[] actuals = ByteUtil.hexStringToBytes(str); - byte[] expected = Hex.decode(str); - assertArrayEquals(expected, actuals); - } - { - String str = "0x"; // Empty - byte[] actuals = ByteUtil.hexStringToBytes(str); - byte[] expected = new byte[] {}; - assertArrayEquals(expected, actuals); - } - { - String str = "0"; // Same as 0x00 - byte[] actuals = ByteUtil.hexStringToBytes(str); - byte[] expected = new byte[] {0}; - assertArrayEquals(expected, actuals); - } - { - String str = "0x00"; // This case shouldn't be empty array - byte[] actuals = ByteUtil.hexStringToBytes(str); - byte[] expected = new byte[] {0}; - assertArrayEquals(expected, actuals); - } - { - String str = "0xd"; // Should work with odd length, adding leading 0 - byte[] actuals = ByteUtil.hexStringToBytes(str); - byte[] expected = new byte[] {13}; - assertArrayEquals(expected, actuals); - } - { - String str = "0xd0d"; // Should work with odd length, adding leading 0 - byte[] actuals = ByteUtil.hexStringToBytes(str); - byte[] expected = new byte[] {13, 13}; - assertArrayEquals(expected, actuals); - } - } -} diff --git a/modTxPoolImpl/build.gradle b/modTxPoolImpl/build.gradle index 1698f6c18d..90377574f9 100644 --- a/modTxPoolImpl/build.gradle +++ b/modTxPoolImpl/build.gradle @@ -12,7 +12,7 @@ dependencies { compile 'com.madgag.spongycastle:core:1.58.0.0' testCompile project(':modCrypto') - testCompile project(':modRlp') + testCompile 'network.aion:rlp4j:0.4.0' testCompile project(':modMcf') testCompile project(':modAion') testCompile 'junit:junit:4.12' From 1719a8940a8cccb3b4212b5bbe4d53c719cdebca Mon Sep 17 00:00:00 2001 From: AionJayT <jay.tseng@aion.network> Date: Tue, 5 Mar 2019 14:46:59 -0500 Subject: [PATCH 06/15] 1.) change modCrypto file structures 2.) change gradle build modCrypto dependency from source code to the maven repo --- modAion/build.gradle | 5 +- modAionImpl/build.gradle | 12 +- modApiServer/build.gradle | 2 +- modBoot/build.gradle | 2 +- modCrypto/build.gradle | 120 +++++++++++++++++- modCrypto/{src_native => src/main/c}/Makefile | 2 +- .../main/c}/blake2-config.h | 0 .../{src_native => src/main/c}/blake2-impl.h | 0 modCrypto/{src_native => src/main/c}/blake2.h | 0 .../main/c}/blake2b-load-sse2.h | 0 .../main/c}/blake2b-load-sse41.h | 0 .../main/c}/blake2b-round.h | 0 .../{src_native => src/main/c}/blake2b.c | 0 .../c}/org_aion_crypto_hash_Blake2bNative.c | 0 .../c}/org_aion_crypto_hash_Blake2bNative.h | 0 .../src/{ => main/java}/module-info.java | 0 .../java}/org/aion/crypto/AddressSpecs.java | 1 - .../java}/org/aion/crypto/ECKey.java | 0 .../java}/org/aion/crypto/ECKeyFac.java | 0 .../java}/org/aion/crypto/HashUtil.java | 0 .../java}/org/aion/crypto/ISignature.java | 0 .../java}/org/aion/crypto/SignatureFac.java | 0 .../org/aion/crypto/ecdsa/ECDSASignature.java | 15 +-- .../org/aion/crypto/ecdsa/ECKeySecp256k1.java | 20 ++- .../org/aion/crypto/ed25519/Curve25519.java | 2 +- .../org/aion/crypto/ed25519/ECKeyEd25519.java | 0 .../aion/crypto/ed25519/Ed25519Signature.java | 0 .../java}/org/aion/crypto/hash/Blake2b.java | 2 +- .../org/aion/crypto/hash/Blake2bNative.java | 0 .../crypto/jce/ECAlgorithmParameters.java | 0 .../org/aion/crypto/jce/ECKeyAgreement.java | 0 .../org/aion/crypto/jce/ECKeyFactory.java | 0 .../aion/crypto/jce/ECKeyPairGenerator.java | 0 .../aion/crypto/jce/ECSignatureFactory.java | 0 .../aion/crypto/jce/SpongyCastleProvider.java | 0 .../java}/org/libsodium/jni/NaCl.java | 0 .../java}/org/libsodium/jni/Sodium.java | 0 .../org/libsodium/jni/SodiumConstants.java | 0 .../java}/org/libsodium/jni/SodiumJNI.java | 0 .../java}/org/libsodium/jni/crypto/Box.java | 0 .../java}/org/libsodium/jni/crypto/Hash.java | 0 .../java}/org/libsodium/jni/crypto/Point.java | 0 .../org/libsodium/jni/crypto/Random.java | 0 .../org/libsodium/jni/crypto/SecretBox.java | 0 .../java}/org/libsodium/jni/crypto/Util.java | 0 .../org/libsodium/jni/encoders/Encoder.java | 0 .../java}/org/libsodium/jni/encoders/Hex.java | 0 .../java}/org/libsodium/jni/encoders/Raw.java | 0 .../java}/org/libsodium/jni/keys/KeyPair.java | 0 .../org/libsodium/jni/keys/PrivateKey.java | 0 .../org/libsodium/jni/keys/PublicKey.java | 0 .../org/libsodium/jni/keys/SigningKey.java | 0 .../org/libsodium/jni/keys/VerifyKey.java | 0 modCrypto/src/org/aion/crypto/Hash256.java | 9 -- .../java}/org/aion/crypto/ChecksumTest.java | 0 .../java}/org/aion/crypto/ECKeyBench.java | 0 .../test/java}/org/aion/crypto/ECKeyTest.java | 0 .../test/java}/org/aion/crypto/HashBench.java | 0 .../test/java}/org/aion/crypto/HashTest.java | 0 .../java}/org/aion/crypto/SignatureTest.java | 0 .../org/aion/crypto/ecdsa/ECDSATest.java | 0 .../org/aion/crypto/hash/Blake2bTest.java | 0 modMcf/build.gradle | 2 +- modPrecompiled/build.gradle | 6 +- modTxPoolImpl/build.gradle | 11 +- modVM/build.gradle | 2 +- 66 files changed, 152 insertions(+), 61 deletions(-) rename modCrypto/{src_native => src/main/c}/Makefile (94%) rename modCrypto/{src_native => src/main/c}/blake2-config.h (100%) rename modCrypto/{src_native => src/main/c}/blake2-impl.h (100%) rename modCrypto/{src_native => src/main/c}/blake2.h (100%) rename modCrypto/{src_native => src/main/c}/blake2b-load-sse2.h (100%) rename modCrypto/{src_native => src/main/c}/blake2b-load-sse41.h (100%) rename modCrypto/{src_native => src/main/c}/blake2b-round.h (100%) rename modCrypto/{src_native => src/main/c}/blake2b.c (100%) rename modCrypto/{src_native => src/main/c}/org_aion_crypto_hash_Blake2bNative.c (100%) rename modCrypto/{src_native => src/main/c}/org_aion_crypto_hash_Blake2bNative.h (100%) rename modCrypto/src/{ => main/java}/module-info.java (100%) rename modCrypto/src/{ => main/java}/org/aion/crypto/AddressSpecs.java (98%) rename modCrypto/src/{ => main/java}/org/aion/crypto/ECKey.java (100%) rename modCrypto/src/{ => main/java}/org/aion/crypto/ECKeyFac.java (100%) rename modCrypto/src/{ => main/java}/org/aion/crypto/HashUtil.java (100%) rename modCrypto/src/{ => main/java}/org/aion/crypto/ISignature.java (100%) rename modCrypto/src/{ => main/java}/org/aion/crypto/SignatureFac.java (100%) rename modCrypto/src/{ => main/java}/org/aion/crypto/ecdsa/ECDSASignature.java (94%) rename modCrypto/src/{ => main/java}/org/aion/crypto/ecdsa/ECKeySecp256k1.java (98%) rename modCrypto/src/{ => main/java}/org/aion/crypto/ed25519/Curve25519.java (96%) rename modCrypto/src/{ => main/java}/org/aion/crypto/ed25519/ECKeyEd25519.java (100%) rename modCrypto/src/{ => main/java}/org/aion/crypto/ed25519/Ed25519Signature.java (100%) rename modCrypto/src/{ => main/java}/org/aion/crypto/hash/Blake2b.java (99%) rename modCrypto/src/{ => main/java}/org/aion/crypto/hash/Blake2bNative.java (100%) rename modCrypto/src/{ => main/java}/org/aion/crypto/jce/ECAlgorithmParameters.java (100%) rename modCrypto/src/{ => main/java}/org/aion/crypto/jce/ECKeyAgreement.java (100%) rename modCrypto/src/{ => main/java}/org/aion/crypto/jce/ECKeyFactory.java (100%) rename modCrypto/src/{ => main/java}/org/aion/crypto/jce/ECKeyPairGenerator.java (100%) rename modCrypto/src/{ => main/java}/org/aion/crypto/jce/ECSignatureFactory.java (100%) rename modCrypto/src/{ => main/java}/org/aion/crypto/jce/SpongyCastleProvider.java (100%) rename modCrypto/src/{ => main/java}/org/libsodium/jni/NaCl.java (100%) rename modCrypto/src/{ => main/java}/org/libsodium/jni/Sodium.java (100%) rename modCrypto/src/{ => main/java}/org/libsodium/jni/SodiumConstants.java (100%) rename modCrypto/src/{ => main/java}/org/libsodium/jni/SodiumJNI.java (100%) rename modCrypto/src/{ => main/java}/org/libsodium/jni/crypto/Box.java (100%) rename modCrypto/src/{ => main/java}/org/libsodium/jni/crypto/Hash.java (100%) rename modCrypto/src/{ => main/java}/org/libsodium/jni/crypto/Point.java (100%) rename modCrypto/src/{ => main/java}/org/libsodium/jni/crypto/Random.java (100%) rename modCrypto/src/{ => main/java}/org/libsodium/jni/crypto/SecretBox.java (100%) rename modCrypto/src/{ => main/java}/org/libsodium/jni/crypto/Util.java (100%) rename modCrypto/src/{ => main/java}/org/libsodium/jni/encoders/Encoder.java (100%) rename modCrypto/src/{ => main/java}/org/libsodium/jni/encoders/Hex.java (100%) rename modCrypto/src/{ => main/java}/org/libsodium/jni/encoders/Raw.java (100%) rename modCrypto/src/{ => main/java}/org/libsodium/jni/keys/KeyPair.java (100%) rename modCrypto/src/{ => main/java}/org/libsodium/jni/keys/PrivateKey.java (100%) rename modCrypto/src/{ => main/java}/org/libsodium/jni/keys/PublicKey.java (100%) rename modCrypto/src/{ => main/java}/org/libsodium/jni/keys/SigningKey.java (100%) rename modCrypto/src/{ => main/java}/org/libsodium/jni/keys/VerifyKey.java (100%) delete mode 100644 modCrypto/src/org/aion/crypto/Hash256.java rename modCrypto/{test => src/test/java}/org/aion/crypto/ChecksumTest.java (100%) rename modCrypto/{test => src/test/java}/org/aion/crypto/ECKeyBench.java (100%) rename modCrypto/{test => src/test/java}/org/aion/crypto/ECKeyTest.java (100%) rename modCrypto/{test => src/test/java}/org/aion/crypto/HashBench.java (100%) rename modCrypto/{test => src/test/java}/org/aion/crypto/HashTest.java (100%) rename modCrypto/{test => src/test/java}/org/aion/crypto/SignatureTest.java (100%) rename modCrypto/{test => src/test/java}/org/aion/crypto/ecdsa/ECDSATest.java (100%) rename modCrypto/{test => src/test/java}/org/aion/crypto/hash/Blake2bTest.java (100%) diff --git a/modAion/build.gradle b/modAion/build.gradle index 7ad1a58044..7f0275e2c8 100644 --- a/modAion/build.gradle +++ b/modAion/build.gradle @@ -6,19 +6,16 @@ dependencies { compile 'network.aion:vm-api4j:0.4.0' compile 'network.aion:util4j:0.4.0' compile 'network.aion:rlp4j:0.4.0' + compile 'network.aion:crypto4j:0.4.0' - compile project(':modCrypto') compile project(':modMcf') compile 'org.json:json:20180813' compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.4' compile group: 'org.apache.commons', name: 'commons-collections4', version: '4.0' testCompile 'network.aion:log4j:0.4.0' - testCompile project(':modCrypto') - testCompile project(':modMcf') testCompile 'junit:junit:4.12' testCompile 'org.hamcrest:hamcrest-all:1.3' - testCompile group: 'org.apache.commons', name: 'commons-lang3', version: '3.4' } // Skip unit tests when doing build task; unit tests are all mixed up with diff --git a/modAionImpl/build.gradle b/modAionImpl/build.gradle index 24e4f1875d..ef1c3b485e 100644 --- a/modAionImpl/build.gradle +++ b/modAionImpl/build.gradle @@ -15,9 +15,9 @@ dependencies { compile 'network.aion:util4j:0.4.0' compile 'network.aion:log4j:0.4.0' compile 'network.aion:rlp4j:0.4.0' + compile 'network.aion:crypto4j:0.4.0' compile project(':modAion') - compile project(':modCrypto') compile project(':modMcf') compile project(':modP2pImpl') compile project(':modP2p') @@ -32,11 +32,11 @@ dependencies { testCompile project(path: ':modTxPoolImpl') testCompile project(path: ':modDbImpl', configuration: 'testClassesOut') - compile group: 'org.ow2.asm', name: 'asm', version: '6.2.1' - compile group: 'org.ow2.asm', name: 'asm-analysis', version: '6.2.1' - compile group: 'org.ow2.asm', name: 'asm-commons', version: '6.2.1' - compile group: 'org.ow2.asm', name: 'asm-tree', version: '6.2.1' - compile group: 'org.ow2.asm', name: 'asm-util', version: '6.2.1' + // compile group: 'org.ow2.asm', name: 'asm', version: '6.2.1' + // compile group: 'org.ow2.asm', name: 'asm-analysis', version: '6.2.1' + // compile group: 'org.ow2.asm', name: 'asm-commons', version: '6.2.1' + // compile group: 'org.ow2.asm', name: 'asm-tree', version: '6.2.1' + // compile group: 'org.ow2.asm', name: 'asm-util', version: '6.2.1' testCompile files('../lib/org-aion-avm-rt.jar') testCompile files('../lib/org-aion-avm-api.jar') diff --git a/modApiServer/build.gradle b/modApiServer/build.gradle index 1639cbf35c..341f10b07d 100644 --- a/modApiServer/build.gradle +++ b/modApiServer/build.gradle @@ -17,6 +17,7 @@ dependencies { compile 'network.aion:vm-api4j:0.4.0' compile 'network.aion:util4j:0.4.0' compile 'network.aion:log4j:0.4.0' + compile 'network.aion:crypto4j:0.4.0' compile project(':modAion') compile project(':modAionImpl') @@ -25,7 +26,6 @@ dependencies { compile project(':modP2p') compile project(':modEvtMgr') compile project(':modEvtMgrImpl') - compile project(':modCrypto') compile project(':3rdParty.libnzmq') compile 'org.json:json:20180813' compile group: 'com.google.protobuf', name: 'protobuf-java', version: '3.5.0' diff --git a/modBoot/build.gradle b/modBoot/build.gradle index 844932f5bc..fb3c5b0272 100644 --- a/modBoot/build.gradle +++ b/modBoot/build.gradle @@ -4,8 +4,8 @@ dependencies { compile 'network.aion:vm-api4j:0.4.0' compile 'network.aion:util4j:0.4.0' compile 'network.aion:log4j:0.4.0' + compile 'network.aion:crypto4j:0.4.0' - compile project(':modCrypto') compile project(':modApiServer') compile project(':modAionImpl') compile project(':modEvtMgr') diff --git a/modCrypto/build.gradle b/modCrypto/build.gradle index c62b29c843..578e5ff20b 100644 --- a/modCrypto/build.gradle +++ b/modCrypto/build.gradle @@ -1,5 +1,24 @@ ext.moduleName = 'aion.crypto' +// set the publish to true when the code ready to push the lib to the maven repo +def publish = false; + +apply plugin: 'maven' +apply plugin: 'signing' + +group = "network.aion" +archivesBaseName = "crypto4j" + +def getCommitHash = { -> + def hashStdOut = new ByteArrayOutputStream() + exec { + commandLine "sh", "-c", "git log --pretty=format:%h | head -1" + standardOutput = hashStdOut + } + + return hashStdOut.toString().trim() +} + dependencies { compile 'network.aion:util4j:0.4.0' compile 'network.aion:rlp4j:0.4.0' @@ -7,18 +26,115 @@ dependencies { compile 'com.madgag.spongycastle:prov:1.58.0.0' compile 'com.madgag.spongycastle:core:1.58.0.0' compile 'org.slf4j:slf4j-api:1.7.25' - compile 'com.google.guava:guava:25.1-jre' testCompile 'com.google.truth:truth:0.42' testCompile 'org.hamcrest:hamcrest-core:1.3' testCompile "org.mockito:mockito-core:2.23.0" } +tasks.withType(JavaCompile) { + doFirst { + println "Args for for $name are $options.allCompilerArgs" + } +} + +sourceSets { + + if (publish) { + version = "0.4.0" + } else { + jar.baseName = 'crypto4j-' + getCommitHash() + } + + main { + java.srcDirs = ['src/main/java'] + } + test { + java.srcDirs = ['src/test/java'] + } +} + + task compileNative(type: Exec) { - workingDir 'src_native' + workingDir 'src/main/c' commandLine 'make' } +signing { + sign configurations.archives +} +signArchives.enabled = publish + +task sourcesJar(type: Jar) { + classifier = 'sources' + from sourceSets.main.allSource +} +sourcesJar.enabled = publish + + +javadoc { + inputs.property("moduleName", moduleName) + doFirst { + options.addStringOption('-module-path', classpath.asPath) + options.tags = [ "implNote" ] + } +} + + +task javadocJar(type: Jar) { + classifier = 'javadoc' + from javadoc +} +javadocJar.enabled = publish + + +artifacts { + archives sourcesJar, javadocJar +} + +uploadArchives { + repositories { + mavenDeployer { + + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { + authentication(userName: ossrhUsername, password: ossrhPassword) + } + + pom.project { + name 'crypto4j' + packaging 'jar' + // optionally artifactId can be defined here + description 'a crypto module for the aion java kernel.' + url 'https://github.com/aionnetwork/aion/tree/master-pre-merge/modCrypto' + + scm { + connection 'scm:git:https://github.com/aionnetwork/aion.git' + developerConnection 'git:https://github.com/aionnetwork/aion.git' + url 'https://github.com/aionnetwork/aion/tree/master' + } + + licenses { + license { + name 'MIT' + url 'https://opensource.org/licenses/MIT' + } + } + + developers { + developer { + id 'aion foundation dev' + name 'aion foundation dev' + email 'toengineering@aion.network' + } + } + } + } + } +} +uploadArchives.enabled = publish + build.dependsOn compileNative test.dependsOn copyNativeLibsForModuleTests clean.dependsOn deleteNativeLibs diff --git a/modCrypto/src_native/Makefile b/modCrypto/src/main/c/Makefile similarity index 94% rename from modCrypto/src_native/Makefile rename to modCrypto/src/main/c/Makefile index dbb94e7a78..5ed9aa0470 100644 --- a/modCrypto/src_native/Makefile +++ b/modCrypto/src/main/c/Makefile @@ -1,4 +1,4 @@ -AION_HOME=../.. +AION_HOME=../../../.. JAVA_HOME=/usr/lib/jvm/jdk-11.0.1 diff --git a/modCrypto/src_native/blake2-config.h b/modCrypto/src/main/c/blake2-config.h similarity index 100% rename from modCrypto/src_native/blake2-config.h rename to modCrypto/src/main/c/blake2-config.h diff --git a/modCrypto/src_native/blake2-impl.h b/modCrypto/src/main/c/blake2-impl.h similarity index 100% rename from modCrypto/src_native/blake2-impl.h rename to modCrypto/src/main/c/blake2-impl.h diff --git a/modCrypto/src_native/blake2.h b/modCrypto/src/main/c/blake2.h similarity index 100% rename from modCrypto/src_native/blake2.h rename to modCrypto/src/main/c/blake2.h diff --git a/modCrypto/src_native/blake2b-load-sse2.h b/modCrypto/src/main/c/blake2b-load-sse2.h similarity index 100% rename from modCrypto/src_native/blake2b-load-sse2.h rename to modCrypto/src/main/c/blake2b-load-sse2.h diff --git a/modCrypto/src_native/blake2b-load-sse41.h b/modCrypto/src/main/c/blake2b-load-sse41.h similarity index 100% rename from modCrypto/src_native/blake2b-load-sse41.h rename to modCrypto/src/main/c/blake2b-load-sse41.h diff --git a/modCrypto/src_native/blake2b-round.h b/modCrypto/src/main/c/blake2b-round.h similarity index 100% rename from modCrypto/src_native/blake2b-round.h rename to modCrypto/src/main/c/blake2b-round.h diff --git a/modCrypto/src_native/blake2b.c b/modCrypto/src/main/c/blake2b.c similarity index 100% rename from modCrypto/src_native/blake2b.c rename to modCrypto/src/main/c/blake2b.c diff --git a/modCrypto/src_native/org_aion_crypto_hash_Blake2bNative.c b/modCrypto/src/main/c/org_aion_crypto_hash_Blake2bNative.c similarity index 100% rename from modCrypto/src_native/org_aion_crypto_hash_Blake2bNative.c rename to modCrypto/src/main/c/org_aion_crypto_hash_Blake2bNative.c diff --git a/modCrypto/src_native/org_aion_crypto_hash_Blake2bNative.h b/modCrypto/src/main/c/org_aion_crypto_hash_Blake2bNative.h similarity index 100% rename from modCrypto/src_native/org_aion_crypto_hash_Blake2bNative.h rename to modCrypto/src/main/c/org_aion_crypto_hash_Blake2bNative.h diff --git a/modCrypto/src/module-info.java b/modCrypto/src/main/java/module-info.java similarity index 100% rename from modCrypto/src/module-info.java rename to modCrypto/src/main/java/module-info.java diff --git a/modCrypto/src/org/aion/crypto/AddressSpecs.java b/modCrypto/src/main/java/org/aion/crypto/AddressSpecs.java similarity index 98% rename from modCrypto/src/org/aion/crypto/AddressSpecs.java rename to modCrypto/src/main/java/org/aion/crypto/AddressSpecs.java index fba0d1122d..e4c8776d44 100644 --- a/modCrypto/src/org/aion/crypto/AddressSpecs.java +++ b/modCrypto/src/main/java/org/aion/crypto/AddressSpecs.java @@ -47,7 +47,6 @@ public static Optional<String> checksummedAddress(String address) { b.get(i) ? Character.toUpperCase(caddr[i]) : Character.toLowerCase(caddr[i]); - continue; } } return Optional.of(String.valueOf(caddr)); diff --git a/modCrypto/src/org/aion/crypto/ECKey.java b/modCrypto/src/main/java/org/aion/crypto/ECKey.java similarity index 100% rename from modCrypto/src/org/aion/crypto/ECKey.java rename to modCrypto/src/main/java/org/aion/crypto/ECKey.java diff --git a/modCrypto/src/org/aion/crypto/ECKeyFac.java b/modCrypto/src/main/java/org/aion/crypto/ECKeyFac.java similarity index 100% rename from modCrypto/src/org/aion/crypto/ECKeyFac.java rename to modCrypto/src/main/java/org/aion/crypto/ECKeyFac.java diff --git a/modCrypto/src/org/aion/crypto/HashUtil.java b/modCrypto/src/main/java/org/aion/crypto/HashUtil.java similarity index 100% rename from modCrypto/src/org/aion/crypto/HashUtil.java rename to modCrypto/src/main/java/org/aion/crypto/HashUtil.java diff --git a/modCrypto/src/org/aion/crypto/ISignature.java b/modCrypto/src/main/java/org/aion/crypto/ISignature.java similarity index 100% rename from modCrypto/src/org/aion/crypto/ISignature.java rename to modCrypto/src/main/java/org/aion/crypto/ISignature.java diff --git a/modCrypto/src/org/aion/crypto/SignatureFac.java b/modCrypto/src/main/java/org/aion/crypto/SignatureFac.java similarity index 100% rename from modCrypto/src/org/aion/crypto/SignatureFac.java rename to modCrypto/src/main/java/org/aion/crypto/SignatureFac.java diff --git a/modCrypto/src/org/aion/crypto/ecdsa/ECDSASignature.java b/modCrypto/src/main/java/org/aion/crypto/ecdsa/ECDSASignature.java similarity index 94% rename from modCrypto/src/org/aion/crypto/ecdsa/ECDSASignature.java rename to modCrypto/src/main/java/org/aion/crypto/ecdsa/ECDSASignature.java index 5dac267b31..cf1c30315c 100644 --- a/modCrypto/src/org/aion/crypto/ecdsa/ECDSASignature.java +++ b/modCrypto/src/main/java/org/aion/crypto/ecdsa/ECDSASignature.java @@ -61,7 +61,7 @@ public byte[] getPubkey(byte[] msg) { * @param r * @param s */ - public ECDSASignature(BigInteger r, BigInteger s) { + ECDSASignature(BigInteger r, BigInteger s) { this.r = r; this.s = s; } @@ -136,9 +136,7 @@ public static boolean isLessThan(BigInteger valueA, BigInteger valueB) { } public static ECDSASignature decodeFromDER(byte[] bytes) { - ASN1InputStream decoder = null; - try { - decoder = new ASN1InputStream(bytes); + try (ASN1InputStream decoder = new ASN1InputStream(bytes)) { DLSequence seq = (DLSequence) decoder.readObject(); if (seq == null) { throw new RuntimeException("Reached past end of ASN.1 stream."); @@ -157,13 +155,6 @@ public static ECDSASignature decodeFromDER(byte[] bytes) { return new ECDSASignature(r.getPositiveValue(), s.getPositiveValue()); } catch (IOException e) { throw new RuntimeException(e); - } finally { - if (decoder != null) { - try { - decoder.close(); - } catch (IOException x) { - } - } } } @@ -177,7 +168,7 @@ public static ECDSASignature decodeFromDER(byte[] bytes) { * * @return - */ - public ECDSASignature toCanonicalised() { + ECDSASignature toCanonicalised() { if (s.compareTo(HALF_CURVE_ORDER) > 0) { // The order of the curve is the number of valid points that exist // on that curve. If S is in the upper diff --git a/modCrypto/src/org/aion/crypto/ecdsa/ECKeySecp256k1.java b/modCrypto/src/main/java/org/aion/crypto/ecdsa/ECKeySecp256k1.java similarity index 98% rename from modCrypto/src/org/aion/crypto/ecdsa/ECKeySecp256k1.java rename to modCrypto/src/main/java/org/aion/crypto/ecdsa/ECKeySecp256k1.java index 8c53ebc6dd..9129189659 100644 --- a/modCrypto/src/org/aion/crypto/ecdsa/ECKeySecp256k1.java +++ b/modCrypto/src/main/java/org/aion/crypto/ecdsa/ECKeySecp256k1.java @@ -72,7 +72,7 @@ */ public class ECKeySecp256k1 implements ECKey, Serializable { - private static final Logger logger = LoggerFactory.getLogger(ECKeySecp256k1.class); + private static final Logger logger = LoggerFactory.getLogger("CRYPTO"); public static final BigInteger ETH_SECP256K1N = new BigInteger("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16); @@ -85,7 +85,8 @@ public class ECKeySecp256k1 implements ECKey, Serializable { /** * Equal to CURVE.getN().shiftRight(1), used for canonicalising the S value of a signature. * ECDSA signatures are mutable in the sense that for a given (R, S) pair, then both (R, S) and - * (R, N - S mod N) are valid signatures. Canonical signatures are those where 1 <= S <= N/2 + * (R, N - S mod N) are valid signatures. Canonical signatures are those where 1 less equal then + * S less equal then N/2 * * <p>See * https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#Low_S_values_in_signatures @@ -114,7 +115,7 @@ public class ECKeySecp256k1 implements ECKey, Serializable { // TODO: Redesign this class to use consistent internals and more efficient // serialization. private final PrivateKey privKey; - protected final ECPoint pub; + private final ECPoint pub; // the Java Cryptographic Architecture provider to use for Signature // this is set along with the PrivateKey privKey and must be compatible @@ -385,7 +386,7 @@ public boolean hasPrivKey() { /** * Returns public key bytes from the given private key. To convert a byte array into a - * BigInteger, use <tt> new BigInteger(1, bytes);</tt> + * BigInteger, use <code> new BigInteger(1, bytes);</code> * * @param privKey - * @param compressed - @@ -810,7 +811,7 @@ public boolean verify(byte[] data, ECDSASignature signature, byte[] pub) { } } - /** @TODO: yao Verify from Base64 encoded signature Switch to DER encoding in future */ + /** TODO: yao Verify from Base64 encoded signature Switch to DER encoding in future */ public boolean verify(byte[] messageHash, String signatureBase64) throws SignatureException { byte[] signatureEncoded; try { @@ -888,18 +889,13 @@ public boolean isPubKeyCanonical() { public boolean isPubKeyCanonical(byte[] pubkey) { if (pubkey[0] == 0x04) { // Uncompressed pubkey - if (pubkey.length != 65) { - return false; - } + return pubkey.length == 65; } else if (pubkey[0] == 0x02 || pubkey[0] == 0x03) { // Compressed pubkey - if (pubkey.length != 33) { - return false; - } + return pubkey.length == 33; } else { return false; } - return true; } /** diff --git a/modCrypto/src/org/aion/crypto/ed25519/Curve25519.java b/modCrypto/src/main/java/org/aion/crypto/ed25519/Curve25519.java similarity index 96% rename from modCrypto/src/org/aion/crypto/ed25519/Curve25519.java rename to modCrypto/src/main/java/org/aion/crypto/ed25519/Curve25519.java index cc2f137e74..4081bbe917 100644 --- a/modCrypto/src/org/aion/crypto/ed25519/Curve25519.java +++ b/modCrypto/src/main/java/org/aion/crypto/ed25519/Curve25519.java @@ -140,7 +140,7 @@ public static final boolean sign(byte[] v, byte[] h, byte[] x, byte[] s) { * h [in] signature hash * P [in] public key */ - public static final void verify(byte[] Y, byte[] v, byte[] h, byte[] P) { + public static void verify(byte[] Y, byte[] v, byte[] h, byte[] P) { /* Y = v abs(P) + h G */ byte[] d = new byte[32]; long10[] p = new long10[] {new long10(), new long10()}, diff --git a/modCrypto/src/org/aion/crypto/ed25519/ECKeyEd25519.java b/modCrypto/src/main/java/org/aion/crypto/ed25519/ECKeyEd25519.java similarity index 100% rename from modCrypto/src/org/aion/crypto/ed25519/ECKeyEd25519.java rename to modCrypto/src/main/java/org/aion/crypto/ed25519/ECKeyEd25519.java diff --git a/modCrypto/src/org/aion/crypto/ed25519/Ed25519Signature.java b/modCrypto/src/main/java/org/aion/crypto/ed25519/Ed25519Signature.java similarity index 100% rename from modCrypto/src/org/aion/crypto/ed25519/Ed25519Signature.java rename to modCrypto/src/main/java/org/aion/crypto/ed25519/Ed25519Signature.java diff --git a/modCrypto/src/org/aion/crypto/hash/Blake2b.java b/modCrypto/src/main/java/org/aion/crypto/hash/Blake2b.java similarity index 99% rename from modCrypto/src/org/aion/crypto/hash/Blake2b.java rename to modCrypto/src/main/java/org/aion/crypto/hash/Blake2b.java index 24ef5c02c1..e77d95a540 100644 --- a/modCrypto/src/org/aion/crypto/hash/Blake2b.java +++ b/modCrypto/src/main/java/org/aion/crypto/hash/Blake2b.java @@ -63,7 +63,7 @@ interface Spec { /** max tree inner length value */ int MAX_TREE_INNER_LEN = 0xFF; - /** initialization values map ref-Spec IV[i] -> slice iv[i*8:i*8+7] */ + /** initialization values map ref-Spec IV[i] to slice iv[i*8:i*8+7] */ long[] IV = { 0x6a09e667f3bcc908L, 0xbb67ae8584caa73bL, diff --git a/modCrypto/src/org/aion/crypto/hash/Blake2bNative.java b/modCrypto/src/main/java/org/aion/crypto/hash/Blake2bNative.java similarity index 100% rename from modCrypto/src/org/aion/crypto/hash/Blake2bNative.java rename to modCrypto/src/main/java/org/aion/crypto/hash/Blake2bNative.java diff --git a/modCrypto/src/org/aion/crypto/jce/ECAlgorithmParameters.java b/modCrypto/src/main/java/org/aion/crypto/jce/ECAlgorithmParameters.java similarity index 100% rename from modCrypto/src/org/aion/crypto/jce/ECAlgorithmParameters.java rename to modCrypto/src/main/java/org/aion/crypto/jce/ECAlgorithmParameters.java diff --git a/modCrypto/src/org/aion/crypto/jce/ECKeyAgreement.java b/modCrypto/src/main/java/org/aion/crypto/jce/ECKeyAgreement.java similarity index 100% rename from modCrypto/src/org/aion/crypto/jce/ECKeyAgreement.java rename to modCrypto/src/main/java/org/aion/crypto/jce/ECKeyAgreement.java diff --git a/modCrypto/src/org/aion/crypto/jce/ECKeyFactory.java b/modCrypto/src/main/java/org/aion/crypto/jce/ECKeyFactory.java similarity index 100% rename from modCrypto/src/org/aion/crypto/jce/ECKeyFactory.java rename to modCrypto/src/main/java/org/aion/crypto/jce/ECKeyFactory.java diff --git a/modCrypto/src/org/aion/crypto/jce/ECKeyPairGenerator.java b/modCrypto/src/main/java/org/aion/crypto/jce/ECKeyPairGenerator.java similarity index 100% rename from modCrypto/src/org/aion/crypto/jce/ECKeyPairGenerator.java rename to modCrypto/src/main/java/org/aion/crypto/jce/ECKeyPairGenerator.java diff --git a/modCrypto/src/org/aion/crypto/jce/ECSignatureFactory.java b/modCrypto/src/main/java/org/aion/crypto/jce/ECSignatureFactory.java similarity index 100% rename from modCrypto/src/org/aion/crypto/jce/ECSignatureFactory.java rename to modCrypto/src/main/java/org/aion/crypto/jce/ECSignatureFactory.java diff --git a/modCrypto/src/org/aion/crypto/jce/SpongyCastleProvider.java b/modCrypto/src/main/java/org/aion/crypto/jce/SpongyCastleProvider.java similarity index 100% rename from modCrypto/src/org/aion/crypto/jce/SpongyCastleProvider.java rename to modCrypto/src/main/java/org/aion/crypto/jce/SpongyCastleProvider.java diff --git a/modCrypto/src/org/libsodium/jni/NaCl.java b/modCrypto/src/main/java/org/libsodium/jni/NaCl.java similarity index 100% rename from modCrypto/src/org/libsodium/jni/NaCl.java rename to modCrypto/src/main/java/org/libsodium/jni/NaCl.java diff --git a/modCrypto/src/org/libsodium/jni/Sodium.java b/modCrypto/src/main/java/org/libsodium/jni/Sodium.java similarity index 100% rename from modCrypto/src/org/libsodium/jni/Sodium.java rename to modCrypto/src/main/java/org/libsodium/jni/Sodium.java diff --git a/modCrypto/src/org/libsodium/jni/SodiumConstants.java b/modCrypto/src/main/java/org/libsodium/jni/SodiumConstants.java similarity index 100% rename from modCrypto/src/org/libsodium/jni/SodiumConstants.java rename to modCrypto/src/main/java/org/libsodium/jni/SodiumConstants.java diff --git a/modCrypto/src/org/libsodium/jni/SodiumJNI.java b/modCrypto/src/main/java/org/libsodium/jni/SodiumJNI.java similarity index 100% rename from modCrypto/src/org/libsodium/jni/SodiumJNI.java rename to modCrypto/src/main/java/org/libsodium/jni/SodiumJNI.java diff --git a/modCrypto/src/org/libsodium/jni/crypto/Box.java b/modCrypto/src/main/java/org/libsodium/jni/crypto/Box.java similarity index 100% rename from modCrypto/src/org/libsodium/jni/crypto/Box.java rename to modCrypto/src/main/java/org/libsodium/jni/crypto/Box.java diff --git a/modCrypto/src/org/libsodium/jni/crypto/Hash.java b/modCrypto/src/main/java/org/libsodium/jni/crypto/Hash.java similarity index 100% rename from modCrypto/src/org/libsodium/jni/crypto/Hash.java rename to modCrypto/src/main/java/org/libsodium/jni/crypto/Hash.java diff --git a/modCrypto/src/org/libsodium/jni/crypto/Point.java b/modCrypto/src/main/java/org/libsodium/jni/crypto/Point.java similarity index 100% rename from modCrypto/src/org/libsodium/jni/crypto/Point.java rename to modCrypto/src/main/java/org/libsodium/jni/crypto/Point.java diff --git a/modCrypto/src/org/libsodium/jni/crypto/Random.java b/modCrypto/src/main/java/org/libsodium/jni/crypto/Random.java similarity index 100% rename from modCrypto/src/org/libsodium/jni/crypto/Random.java rename to modCrypto/src/main/java/org/libsodium/jni/crypto/Random.java diff --git a/modCrypto/src/org/libsodium/jni/crypto/SecretBox.java b/modCrypto/src/main/java/org/libsodium/jni/crypto/SecretBox.java similarity index 100% rename from modCrypto/src/org/libsodium/jni/crypto/SecretBox.java rename to modCrypto/src/main/java/org/libsodium/jni/crypto/SecretBox.java diff --git a/modCrypto/src/org/libsodium/jni/crypto/Util.java b/modCrypto/src/main/java/org/libsodium/jni/crypto/Util.java similarity index 100% rename from modCrypto/src/org/libsodium/jni/crypto/Util.java rename to modCrypto/src/main/java/org/libsodium/jni/crypto/Util.java diff --git a/modCrypto/src/org/libsodium/jni/encoders/Encoder.java b/modCrypto/src/main/java/org/libsodium/jni/encoders/Encoder.java similarity index 100% rename from modCrypto/src/org/libsodium/jni/encoders/Encoder.java rename to modCrypto/src/main/java/org/libsodium/jni/encoders/Encoder.java diff --git a/modCrypto/src/org/libsodium/jni/encoders/Hex.java b/modCrypto/src/main/java/org/libsodium/jni/encoders/Hex.java similarity index 100% rename from modCrypto/src/org/libsodium/jni/encoders/Hex.java rename to modCrypto/src/main/java/org/libsodium/jni/encoders/Hex.java diff --git a/modCrypto/src/org/libsodium/jni/encoders/Raw.java b/modCrypto/src/main/java/org/libsodium/jni/encoders/Raw.java similarity index 100% rename from modCrypto/src/org/libsodium/jni/encoders/Raw.java rename to modCrypto/src/main/java/org/libsodium/jni/encoders/Raw.java diff --git a/modCrypto/src/org/libsodium/jni/keys/KeyPair.java b/modCrypto/src/main/java/org/libsodium/jni/keys/KeyPair.java similarity index 100% rename from modCrypto/src/org/libsodium/jni/keys/KeyPair.java rename to modCrypto/src/main/java/org/libsodium/jni/keys/KeyPair.java diff --git a/modCrypto/src/org/libsodium/jni/keys/PrivateKey.java b/modCrypto/src/main/java/org/libsodium/jni/keys/PrivateKey.java similarity index 100% rename from modCrypto/src/org/libsodium/jni/keys/PrivateKey.java rename to modCrypto/src/main/java/org/libsodium/jni/keys/PrivateKey.java diff --git a/modCrypto/src/org/libsodium/jni/keys/PublicKey.java b/modCrypto/src/main/java/org/libsodium/jni/keys/PublicKey.java similarity index 100% rename from modCrypto/src/org/libsodium/jni/keys/PublicKey.java rename to modCrypto/src/main/java/org/libsodium/jni/keys/PublicKey.java diff --git a/modCrypto/src/org/libsodium/jni/keys/SigningKey.java b/modCrypto/src/main/java/org/libsodium/jni/keys/SigningKey.java similarity index 100% rename from modCrypto/src/org/libsodium/jni/keys/SigningKey.java rename to modCrypto/src/main/java/org/libsodium/jni/keys/SigningKey.java diff --git a/modCrypto/src/org/libsodium/jni/keys/VerifyKey.java b/modCrypto/src/main/java/org/libsodium/jni/keys/VerifyKey.java similarity index 100% rename from modCrypto/src/org/libsodium/jni/keys/VerifyKey.java rename to modCrypto/src/main/java/org/libsodium/jni/keys/VerifyKey.java diff --git a/modCrypto/src/org/aion/crypto/Hash256.java b/modCrypto/src/org/aion/crypto/Hash256.java deleted file mode 100644 index eab2837c35..0000000000 --- a/modCrypto/src/org/aion/crypto/Hash256.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.aion.crypto; - -/** @author jin */ -public interface Hash256 { - - byte[] h256(byte[] in); - - byte[] h256(byte[] in1, byte[] in2); -} diff --git a/modCrypto/test/org/aion/crypto/ChecksumTest.java b/modCrypto/src/test/java/org/aion/crypto/ChecksumTest.java similarity index 100% rename from modCrypto/test/org/aion/crypto/ChecksumTest.java rename to modCrypto/src/test/java/org/aion/crypto/ChecksumTest.java diff --git a/modCrypto/test/org/aion/crypto/ECKeyBench.java b/modCrypto/src/test/java/org/aion/crypto/ECKeyBench.java similarity index 100% rename from modCrypto/test/org/aion/crypto/ECKeyBench.java rename to modCrypto/src/test/java/org/aion/crypto/ECKeyBench.java diff --git a/modCrypto/test/org/aion/crypto/ECKeyTest.java b/modCrypto/src/test/java/org/aion/crypto/ECKeyTest.java similarity index 100% rename from modCrypto/test/org/aion/crypto/ECKeyTest.java rename to modCrypto/src/test/java/org/aion/crypto/ECKeyTest.java diff --git a/modCrypto/test/org/aion/crypto/HashBench.java b/modCrypto/src/test/java/org/aion/crypto/HashBench.java similarity index 100% rename from modCrypto/test/org/aion/crypto/HashBench.java rename to modCrypto/src/test/java/org/aion/crypto/HashBench.java diff --git a/modCrypto/test/org/aion/crypto/HashTest.java b/modCrypto/src/test/java/org/aion/crypto/HashTest.java similarity index 100% rename from modCrypto/test/org/aion/crypto/HashTest.java rename to modCrypto/src/test/java/org/aion/crypto/HashTest.java diff --git a/modCrypto/test/org/aion/crypto/SignatureTest.java b/modCrypto/src/test/java/org/aion/crypto/SignatureTest.java similarity index 100% rename from modCrypto/test/org/aion/crypto/SignatureTest.java rename to modCrypto/src/test/java/org/aion/crypto/SignatureTest.java diff --git a/modCrypto/test/org/aion/crypto/ecdsa/ECDSATest.java b/modCrypto/src/test/java/org/aion/crypto/ecdsa/ECDSATest.java similarity index 100% rename from modCrypto/test/org/aion/crypto/ecdsa/ECDSATest.java rename to modCrypto/src/test/java/org/aion/crypto/ecdsa/ECDSATest.java diff --git a/modCrypto/test/org/aion/crypto/hash/Blake2bTest.java b/modCrypto/src/test/java/org/aion/crypto/hash/Blake2bTest.java similarity index 100% rename from modCrypto/test/org/aion/crypto/hash/Blake2bTest.java rename to modCrypto/src/test/java/org/aion/crypto/hash/Blake2bTest.java diff --git a/modMcf/build.gradle b/modMcf/build.gradle index 8f65b6c513..17d95b08d9 100644 --- a/modMcf/build.gradle +++ b/modMcf/build.gradle @@ -8,9 +8,9 @@ dependencies { compile 'network.aion:util4j:0.4.0' compile 'network.aion:log4j:0.4.0' compile 'network.aion:rlp4j:0.4.0' + compile 'network.aion:crypto4j:0.4.0' compile project(':modP2p') - compile project(':modCrypto') compile project(':modDbImpl') compile 'com.madgag.spongycastle:prov:1.58.0.0' compile 'com.madgag.spongycastle:core:1.58.0.0' diff --git a/modPrecompiled/build.gradle b/modPrecompiled/build.gradle index 4cf5346d28..e2b6af9f8a 100644 --- a/modPrecompiled/build.gradle +++ b/modPrecompiled/build.gradle @@ -5,9 +5,9 @@ clean.dependsOn deleteNativeLibs dependencies { compile 'network.aion:vm-api4j:0.4.0' compile 'network.aion:util4j:0.4.0' + compile 'network.aion:crypto4j:0.4.0' compile project(':modMcf') - compile project(':modCrypto') compile project(':modAion') compile 'com.google.guava:guava:25.1-jre' compile 'com.google.code.findbugs:jsr305:3.0.2' @@ -16,11 +16,11 @@ dependencies { testCompile project(':modMcf') testCompile project(':modEvtMgr') - testCompile project(':modRlp') testCompile project(':modPrecompiled') testCompile project(':modAionImpl') - testCompile 'network.aion:log4j:0.4.0' testCompile project(':modDbImpl') + testCompile 'network.aion:rlp4j:0.4.0' + testCompile 'network.aion:log4j:0.4.0' testCompile 'junit:junit:4.12' testCompile 'org.hamcrest:hamcrest-all:1.3' testCompile group: 'com.googlecode.java-diff-utils', name: 'diffutils', version: '1.2' diff --git a/modTxPoolImpl/build.gradle b/modTxPoolImpl/build.gradle index 90377574f9..df3d602de7 100644 --- a/modTxPoolImpl/build.gradle +++ b/modTxPoolImpl/build.gradle @@ -6,17 +6,18 @@ dependencies { compile 'network.aion:vm-api4j:0.4.0' compile 'network.aion:util4j:0.4.0' compile 'network.aion:log4j:0.4.0' - - compile project(':modTxPool') compile 'com.madgag.spongycastle:prov:1.58.0.0' compile 'com.madgag.spongycastle:core:1.58.0.0' - testCompile project(':modCrypto') + compile project(':modTxPool') + + testCompile 'network.aion:crypto4j:0.4.0' testCompile 'network.aion:rlp4j:0.4.0' - testCompile project(':modMcf') - testCompile project(':modAion') testCompile 'junit:junit:4.12' testCompile 'org.hamcrest:hamcrest-core:1.3' + + testCompile project(':modMcf') + testCompile project(':modAion') } // Skip unit tests when doing build task; unit tests are all mixed up with diff --git a/modVM/build.gradle b/modVM/build.gradle index 4a5969ea0e..6351f65a85 100644 --- a/modVM/build.gradle +++ b/modVM/build.gradle @@ -3,10 +3,10 @@ ext.moduleName = 'aion.vm' dependencies { compile 'network.aion:vm-api4j:0.4.0' compile 'network.aion:util4j:0.4.0' + compile 'network.aion:crypto4j:0.4.0' compile project(':modAion') compile project(':modMcf') - compile project(':modCrypto') compile project(':aion_fastvm') compile files('../lib/org-aion-avm-core.jar') compile 'com.google.code.findbugs:jsr305:3.0.2' From 16ec3d51abe3299d7c3d83b3b5826511a24bd56a Mon Sep 17 00:00:00 2001 From: AionJayT <jay.tseng@aion.network> Date: Tue, 5 Mar 2019 14:48:13 -0500 Subject: [PATCH 07/15] revise NodeMgrTest due to comsume too many resources during the test --- .../test/org/aion/p2p/impl/comm/NodeMgrTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modP2pImpl/test/org/aion/p2p/impl/comm/NodeMgrTest.java b/modP2pImpl/test/org/aion/p2p/impl/comm/NodeMgrTest.java index 2e89728687..d5fbea79e8 100644 --- a/modP2pImpl/test/org/aion/p2p/impl/comm/NodeMgrTest.java +++ b/modP2pImpl/test/org/aion/p2p/impl/comm/NodeMgrTest.java @@ -544,7 +544,7 @@ public void testDumpNodeInfo3() { @Test public void testConcurrency() throws InterruptedException { - AtomicInteger count = new AtomicInteger(1000); + AtomicInteger count = new AtomicInteger(100); AtomicBoolean start = new AtomicBoolean(false); @@ -562,7 +562,7 @@ public void run() { e.printStackTrace(); } try { - Thread.sleep(r.nextInt(5) + 5); + Thread.sleep(r.nextInt(5) + 20); } catch (InterruptedException e) { e.printStackTrace(); } @@ -598,7 +598,7 @@ private INode genNode() { () -> { while (start.get()) { try { - Thread.sleep(15); + Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } @@ -623,7 +623,7 @@ private INode genNode() { () -> { while (start.get()) { try { - Thread.sleep(15); + Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } @@ -670,7 +670,7 @@ private INode genNode() { count.getAndDecrement(); try { - Thread.sleep(8); + Thread.sleep(15); } catch (InterruptedException e) { e.printStackTrace(); } From 862c65fb0f5a1c3196275769df347b79d08e29c1 Mon Sep 17 00:00:00 2001 From: AionJayT <jay.tseng@aion.network> Date: Wed, 6 Mar 2019 11:38:37 -0500 Subject: [PATCH 08/15] fix redo import fail when the database is for avmtestnet --- modAionImpl/src/org/aion/zero/impl/cli/Cli.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modAionImpl/src/org/aion/zero/impl/cli/Cli.java b/modAionImpl/src/org/aion/zero/impl/cli/Cli.java index e35905321e..d8c13cf72f 100644 --- a/modAionImpl/src/org/aion/zero/impl/cli/Cli.java +++ b/modAionImpl/src/org/aion/zero/impl/cli/Cli.java @@ -25,6 +25,7 @@ import org.aion.mcf.config.CfgSsl; import org.aion.mcf.config.CfgSync; import org.aion.util.conversions.Hex; +import org.aion.vm.VirtualMachineProvider; import org.aion.zero.impl.Version; import org.aion.zero.impl.config.Network; import org.aion.zero.impl.db.RecoveryUtils; @@ -533,7 +534,10 @@ public ReturnType call(final String[] args, Cfg cfg) { + "ยป cannot be converted to a number."); return ERROR; } + + VirtualMachineProvider.initializeAllVirtualMachines(); RecoveryUtils.redoMainChainImport(height); + VirtualMachineProvider.shutdownAllVirtualMachines(); return EXIT; } } From 62690d4de09003700bfeb933a710d2fdaec69238 Mon Sep 17 00:00:00 2001 From: AionJayT <jay.tseng@aion.network> Date: Wed, 6 Mar 2019 11:59:27 -0500 Subject: [PATCH 09/15] add vm invalidblocktest --- .../aion/zero/impl/vm/InvalidBlockTest.java | 100 ++++++++++++++++++ .../aion/zero/impl/vm/contracts/Contract.java | 27 +++++ 2 files changed, 127 insertions(+) create mode 100644 modAionImpl/test/org/aion/zero/impl/vm/InvalidBlockTest.java create mode 100644 modAionImpl/test/org/aion/zero/impl/vm/contracts/Contract.java diff --git a/modAionImpl/test/org/aion/zero/impl/vm/InvalidBlockTest.java b/modAionImpl/test/org/aion/zero/impl/vm/InvalidBlockTest.java new file mode 100644 index 0000000000..3d8bbcc42b --- /dev/null +++ b/modAionImpl/test/org/aion/zero/impl/vm/InvalidBlockTest.java @@ -0,0 +1,100 @@ +package org.aion.zero.impl.vm; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.mongodb.client.model.Collation; +import org.aion.avm.core.dappreading.JarBuilder; +import org.aion.avm.core.util.CodeAndArguments; +import org.aion.crypto.ECKey; +import org.aion.mcf.core.ImportResult; +import org.aion.types.Address; +import org.aion.vm.VirtualMachineProvider; +import org.aion.zero.impl.StandaloneBlockchain; +import org.aion.zero.impl.types.AionBlock; +import org.aion.zero.impl.types.AionBlockSummary; +import org.aion.zero.impl.vm.contracts.Contract; +import org.aion.zero.types.AionTransaction; +import org.apache.commons.lang3.tuple.Pair; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +public class InvalidBlockTest { + private StandaloneBlockchain blockchain; + private ECKey deployerKey; + private long energyPrice = 1; + + @BeforeClass + public static void setupAvm() { + VirtualMachineProvider.initializeAllVirtualMachines(); + } + + @AfterClass + public static void tearDownAvm() { + VirtualMachineProvider.shutdownAllVirtualMachines(); + } + + @Before + public void setup() { + StandaloneBlockchain.Bundle bundle = + new StandaloneBlockchain.Builder() + .withDefaultAccounts() + .withValidatorConfiguration("simple") + .withAvmEnabled() + .build(); + this.blockchain = bundle.bc; + this.deployerKey = bundle.privateKeys.get(0); + } + + @After + public void tearDown() { + this.blockchain = null; + this.deployerKey = null; + } + + @Test + public void test() { + BigInteger nonce = this.blockchain.getRepository().getNonce(Address.wrap(this.deployerKey.getAddress())); + List<AionTransaction> transactions = makeTransactions(10, nonce); + Collections.shuffle(transactions); + + AionBlock parent = this.blockchain.getBestBlock(); + AionBlock block = this.blockchain.createNewBlock(parent, transactions, false); + + Pair<ImportResult, AionBlockSummary> res = this.blockchain.tryToConnectAndFetchSummary(block); + System.out.println(res.getLeft()); + System.out.println(res.getRight().getReceipts()); + } + + private List<AionTransaction> makeTransactions(int num, BigInteger initialNonce) { + List<AionTransaction> transactions = new ArrayList<>(); + + byte[] jar = new CodeAndArguments(JarBuilder.buildJarForMainAndClasses(Contract.class), new byte[0]).encodeToBytes(); + BigInteger nonce = initialNonce; + + for (int i = 0; i < num; i++) { + + AionTransaction transaction = new AionTransaction( + nonce.toByteArray(), + Address.wrap(this.deployerKey.getAddress()), + null, + BigInteger.ZERO.toByteArray(), + jar, + 5_000_000L, + 10_000_000_000L, + (byte) 0x0f); + transaction.sign(this.deployerKey); + + transactions.add(transaction); + nonce = nonce.add(BigInteger.ONE); + } + + return transactions; + } + +} \ No newline at end of file diff --git a/modAionImpl/test/org/aion/zero/impl/vm/contracts/Contract.java b/modAionImpl/test/org/aion/zero/impl/vm/contracts/Contract.java new file mode 100644 index 0000000000..68a6edb132 --- /dev/null +++ b/modAionImpl/test/org/aion/zero/impl/vm/contracts/Contract.java @@ -0,0 +1,27 @@ +package org.aion.zero.impl.vm.contracts; + +import org.aion.avm.api.ABIDecoder; +import org.aion.avm.api.Address; +import org.aion.avm.api.BlockchainRuntime; + +public final class Contract { + private static final Address owner = BlockchainRuntime.getCaller(); + + public static boolean isOwner() { + return BlockchainRuntime.getCaller().equals(owner); + } + + public static void transfer(Address address) { + BlockchainRuntime.call(address, BlockchainRuntime.getValue(), new byte[0], BlockchainRuntime.getRemainingEnergy()); + } + + public static void output() { + BlockchainRuntime.println("Owner is: " + owner); + BlockchainRuntime.log(owner.unwrap(), "message".getBytes()); + } + + public static byte[] main() { + return ABIDecoder.decodeAndRunWithClass(Contract.class, BlockchainRuntime.getData()); + } + +} \ No newline at end of file From 72d33382cc49fef7cba4688f321ce497d272be31 Mon Sep 17 00:00:00 2001 From: AionJayT <jay.tseng@aion.network> Date: Tue, 12 Mar 2019 12:21:16 -0400 Subject: [PATCH 10/15] add contract storage expend&check test --- .../aion/zero/impl/db/AionRepositoryImpl.java | 1 - .../org/aion/db/AionContractDetailsTest.java | 73 +++++++++++++++ .../impl/BlockChainContractStoreTest.java | 4 + .../impl/BlockchainAccountStateBenchmark.java | 93 ++++++++++++++----- 4 files changed, 149 insertions(+), 22 deletions(-) create mode 100644 modAionImpl/test/org/aion/zero/impl/BlockChainContractStoreTest.java diff --git a/modAionImpl/src/org/aion/zero/impl/db/AionRepositoryImpl.java b/modAionImpl/src/org/aion/zero/impl/db/AionRepositoryImpl.java index 6206061a71..8d910a2df6 100644 --- a/modAionImpl/src/org/aion/zero/impl/db/AionRepositoryImpl.java +++ b/modAionImpl/src/org/aion/zero/impl/db/AionRepositoryImpl.java @@ -511,7 +511,6 @@ public void commitBlock(A0BlockHeader blockHeader) { try { worldState.sync(); - detailsDS.syncLargeStorage(); if (pruneEnabled) { if (stateDSPrune.isArchiveEnabled() && blockHeader.getNumber() % archiveRate == 0) { diff --git a/modAionImpl/test/org/aion/db/AionContractDetailsTest.java b/modAionImpl/test/org/aion/db/AionContractDetailsTest.java index 255c1ce827..8fd0b14931 100644 --- a/modAionImpl/test/org/aion/db/AionContractDetailsTest.java +++ b/modAionImpl/test/org/aion/db/AionContractDetailsTest.java @@ -1,5 +1,6 @@ package org.aion.db; +import static org.aion.mcf.db.DatabaseUtils.connectAndOpen; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -11,6 +12,9 @@ import org.aion.interfaces.db.ContractDetails; import org.aion.interfaces.db.PruneConfig; import org.aion.interfaces.db.RepositoryConfig; +import org.aion.log.AionLoggerFactory; +import org.aion.log.LogEnum; +import org.aion.mcf.trie.JournalPruneDataSource; import org.aion.mcf.vm.types.DataWordImpl; import org.aion.types.Address; import org.aion.types.ByteArrayWrapper; @@ -24,8 +28,11 @@ import org.aion.zero.impl.db.ContractDetailsAion; import org.apache.commons.lang3.RandomUtils; import org.junit.Test; +import org.slf4j.Logger; public class AionContractDetailsTest { + private static final Logger LOG = AionLoggerFactory.getLogger(LogEnum.DB.name()); + private static final int IN_MEMORY_STORAGE_LIMIT = 1000000; // CfgAion.inst().getDb().getDetailsInMemoryStorageLimit(); @@ -289,6 +296,72 @@ public void testExternalStorageSerialization() { deserialized.delete(new DataWordImpl(RandomUtils.nextBytes(16)).toWrapper()); } + @Test + public void testContractStorageSwitch() { + Address address = Address.wrap(RandomUtils.nextBytes(Address.SIZE)); + byte[] code = RandomUtils.nextBytes(512); + Map<DataWordImpl, DataWordImpl> elements = new HashMap<>(); + + int memstoragelimit = 512; + AionContractDetailsImpl original = new AionContractDetailsImpl(0, memstoragelimit); + + // getting storage specific properties + Properties sharedProps; + sharedProps = repoConfig.getDatabaseConfig("storage"); + sharedProps.setProperty(DatabaseFactory.Props.ENABLE_LOCKING, "false"); + sharedProps.setProperty(DatabaseFactory.Props.DB_PATH, repoConfig.getDbPath()); + sharedProps.setProperty(DatabaseFactory.Props.DB_NAME, "storage"); + ByteArrayKeyValueDatabase storagedb = connectAndOpen(sharedProps, LOG); + JournalPruneDataSource jpd = new JournalPruneDataSource(storagedb); + original.setDataSource(jpd); + original.setAddress(address); + original.setCode(code); + + // the first 2 insertion use memory storage + for (int i = 0; i < 2; i++) { + DataWordImpl key = new DataWordImpl(RandomUtils.nextBytes(16)); + DataWordImpl value = new DataWordImpl(RandomUtils.nextBytes(16)); + + elements.put(key, value); + original.put(key.toWrapper(), wrapValueForPut(value)); + } + + original.decode(original.getEncoded()); + original.syncStorage(); + assertTrue(!original.externalStorage); + + // transfer to external storage since 3rd insert + DataWordImpl key3rd = new DataWordImpl(RandomUtils.nextBytes(16)); + DataWordImpl value = new DataWordImpl(RandomUtils.nextBytes(16)); + elements.put(key3rd, value); + original.put(key3rd.toWrapper(), wrapValueForPut(value)); + + original.decode(original.getEncoded()); + original.syncStorage(); + assertTrue(original.externalStorage); + + byte[] rlp = original.getEncoded(); + + AionContractDetailsImpl deserialized = new AionContractDetailsImpl(0, memstoragelimit); + deserialized.setDataSource(jpd); + deserialized.decode(rlp); + + assertTrue(deserialized.externalStorage); + assertEquals(address, deserialized.getAddress()); + assertEquals(ByteUtil.toHexString(code), ByteUtil.toHexString(deserialized.getCode())); + + for (DataWordImpl key : elements.keySet()) { + assertEquals( + elements.get(key).toWrapper(), + wrapValueFromGet(deserialized.get(key.toWrapper()))); + } + + DataWordImpl deletedKey = elements.keySet().iterator().next(); + + deserialized.delete(deletedKey.toWrapper()); + deserialized.delete(new DataWordImpl(RandomUtils.nextBytes(16)).toWrapper()); + } + @Test public void testExternalStorageTransition() { Address address = Address.wrap(RandomUtils.nextBytes(Address.SIZE)); diff --git a/modAionImpl/test/org/aion/zero/impl/BlockChainContractStoreTest.java b/modAionImpl/test/org/aion/zero/impl/BlockChainContractStoreTest.java new file mode 100644 index 0000000000..8373d9eaf4 --- /dev/null +++ b/modAionImpl/test/org/aion/zero/impl/BlockChainContractStoreTest.java @@ -0,0 +1,4 @@ +package org.aion.zero.impl; + +public class BlockChainContractStoreTest { +} diff --git a/modAionImpl/test/org/aion/zero/impl/BlockchainAccountStateBenchmark.java b/modAionImpl/test/org/aion/zero/impl/BlockchainAccountStateBenchmark.java index 5985c65eae..a4f781072f 100644 --- a/modAionImpl/test/org/aion/zero/impl/BlockchainAccountStateBenchmark.java +++ b/modAionImpl/test/org/aion/zero/impl/BlockchainAccountStateBenchmark.java @@ -1,14 +1,14 @@ package org.aion.zero.impl; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import java.io.File; import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; +import java.security.SecureRandom; +import java.util.*; + import org.aion.types.Address; import org.aion.crypto.ECKey; import org.aion.crypto.HashUtil; @@ -17,6 +17,8 @@ import org.aion.mcf.core.ImportResult; import org.aion.util.bytes.ByteUtil; +import org.aion.util.conversions.Hex; +import org.aion.zero.db.AionContractDetailsImpl; import org.aion.zero.impl.types.AionBlock; import org.aion.zero.impl.types.AionTxInfo; import org.aion.zero.types.AionTransaction; @@ -30,9 +32,11 @@ @RunWith(Parameterized.class) public class BlockchainAccountStateBenchmark { - public static String baseTestPath = "test_db"; + private Random random = new SecureRandom(); + + private static String baseTestPath = "test_db"; - public static String[] dbPaths = { + private static String[] dbPaths = { "level_db_state_test", "level_db_expansion_test", "h2_db_state_test", @@ -41,7 +45,7 @@ public class BlockchainAccountStateBenchmark { "rocks_db_expansion_test" }; - public static void resetFileState() { + private static void resetFileState() { File f = new File(baseTestPath); if (f.exists()) { FileUtils.deleteRecursively(f); @@ -124,12 +128,9 @@ public String getDbPath() { }); } - private String name; - private StandaloneBlockchain.Bundle bundle; public BlockchainAccountStateBenchmark(String name, StandaloneBlockchain.Bundle bundle) { - this.name = name; this.bundle = bundle; } @@ -192,7 +193,7 @@ private static AionBlock createBundleAndCheck( } private static final String STATE_EXPANSION_BYTECODE = - "0x605060405260006001600050909055341561001a5760006000fd5b61001f565b6101688061002e6000396000f30060506040526000356c01000000000000000000000000900463ffffffff16806331e658a514610049578063549262ba1461008957806361bc221a1461009f57610043565b60006000fd5b34156100555760006000fd5b610073600480808060100135903590916020019091929050506100c9565b6040518082815260100191505060405180910390f35b34156100955760006000fd5b61009d6100eb565b005b34156100ab5760006000fd5b6100b3610133565b6040518082815260100191505060405180910390f35b6000600050602052818160005260105260306000209050600091509150505481565b6001600060005060006001600050546000825281601001526020019081526010016000209050600050819090905550600160008181505480929190600101919050909055505b565b600160005054815600a165627a7a72305820c615f3373321aa7e9c05d9a69e49508147861fb2a54f2945fbbaa7d851125fe80029"; + "605060405234156100105760006000fd5b610015565b610146806100246000396000f30060506040526000356c01000000000000000000000000900463ffffffff16806326121ff0146100335761002d565b60006000fd5b341561003f5760006000fd5b610047610049565b005b6000600050805480600101828161006091906100b3565b91909060005260106000209050906002020160005b7e112233445566778899001122334455667788990011223344556677889900119091929091925091909060001916909091806001018390555550505b565b8154818355818115116100e25760020281600202836000526010600020905091820191016100e191906100e7565b5b505050565b61011791906100f1565b80821115610113576000818150806000905560010160009055506002016100f1565b5090565b905600a165627a7a72305820c4bdcf87b810c9e707e3df169b98d6a37a6e6f3356cc8c120ea06c64696f85c20029"; @Ignore @Test @@ -229,7 +230,7 @@ public void testExpandOneAccountStorage() throws InterruptedException { } } - public static Pair<AionBlock, byte[]> createContract( + private static Pair<AionBlock, byte[]> createContract( StandaloneBlockchain bc, ECKey key, AionBlock parentBlock) { BigInteger accountNonce = bc.getRepository().getNonce(new Address(key.getAddress())); @@ -244,7 +245,7 @@ public static Pair<AionBlock, byte[]> createContract( 1); creationTx.sign(key); - AionBlock block = bc.createNewBlock(parentBlock, Arrays.asList(creationTx), true); + AionBlock block = bc.createNewBlock(parentBlock, Collections.singletonList(creationTx), true); return Pair.of(block, creationTx.getTransactionHash()); } @@ -253,22 +254,28 @@ private static AionBlock createContractBundle( final ECKey key, final AionBlock parentBlock, final Address contractAddress) { + return createContractBundle(bc, key, parentBlock, contractAddress, 133); + } + + private static AionBlock createContractBundle( + final StandaloneBlockchain bc, + final ECKey key, + final AionBlock parentBlock, + final Address contractAddress, + final int repeat) { BigInteger accountNonce = bc.getRepository().getNonce(new Address(key.getAddress())); List<AionTransaction> transactions = new ArrayList<>(); - // command - ByteBuffer buf = ByteBuffer.allocate(4); - buf.put(HashUtil.keccak256("put()".getBytes()), 0, 4); + byte[] callData = Hex.decode("26121ff0"); - // create 400 transactions per bundle // byte[] nonce, Address to, byte[] value, byte[] data, long nrg, long nrgPrice - for (int i = 0; i < 133; i++) { + for (int i = 0; i < repeat; i++) { AionTransaction sendTransaction = new AionTransaction( accountNonce.toByteArray(), contractAddress, BigInteger.ZERO.toByteArray(), - buf.array(), + callData, 200000, 1); sendTransaction.sign(key); @@ -278,7 +285,7 @@ private static AionBlock createContractBundle( AionBlock block = bc.createNewBlock(parentBlock, transactions, true); - assertThat(block.getTransactionsList().size()).isEqualTo(133); + assertThat(block.getTransactionsList().size()).isEqualTo(repeat); // clear the trie bc.getRepository().flush(); @@ -291,4 +298,48 @@ private static AionBlock createContractBundle( assertThat(result).isEqualTo(ImportResult.IMPORTED_BEST); return block; } + + @Test + public void testExpandContractsStorage() throws InterruptedException { + try { + StandaloneBlockchain.Bundle bundle = this.bundle; + + StandaloneBlockchain bc = bundle.bc; + + int r = random.nextInt(bundle.privateKeys.size()); + ECKey key = bundle.privateKeys.get(r); + // deploy contract + Pair<AionBlock, byte[]> res = createContract(bc, key, bc.getGenesis()); + bc.tryToConnect(res.getLeft()); + AionTxInfo info = bc.getTransactionInfo(res.getRight()); + assertThat(info.getReceipt().isValid()).isTrue(); + + Address contractAddress = info.getReceipt().getTransaction().getContractAddress(); + + byte[] contractCode = + bc.getRepository() + .getCode(info.getReceipt().getTransaction().getContractAddress()); + + + System.out.println("deployed contract code: " + ByteUtil.toHexString(contractCode)); + System.out.println("deployed at: " + contractAddress); + + AionContractDetailsImpl acdi = new AionContractDetailsImpl(bc.getRepository().getContractDetails(contractAddress).getEncoded()); + assertFalse(acdi.externalStorage); + + // around 350 tx to letting the contract storage from memory switch to the external storage. + for (int i = 0; i < 9; i++) { + createContractBundle(bc, key, bc.getBestBlock(), contractAddress, 50); + } + + acdi = new AionContractDetailsImpl(bc.getRepository().getContractDetails(contractAddress).getEncoded()); + assertTrue(acdi.externalStorage); + + } catch (Exception e) { + e.printStackTrace(); + } finally { + bundle.bc.getRepository().close(); + Thread.sleep(1000L); + } + } } From 43efe0396246ad1bee61cfc08315774f73350ad0 Mon Sep 17 00:00:00 2001 From: AionJayT <jay.tseng@aion.network> Date: Tue, 12 Mar 2019 12:22:48 -0400 Subject: [PATCH 11/15] fix IntelliJ can't run the test in the mcf and aionImpl module --- modAionImpl/build.gradle | 1 + modDbImpl/src/module-info.java | 1 + modMcf/build.gradle | 2 ++ 3 files changed, 4 insertions(+) diff --git a/modAionImpl/build.gradle b/modAionImpl/build.gradle index ef1c3b485e..126fdf85ec 100644 --- a/modAionImpl/build.gradle +++ b/modAionImpl/build.gradle @@ -42,6 +42,7 @@ dependencies { testCompile files('../lib/org-aion-avm-api.jar') + testCompile 'network.aion:crypto4j:0.4.0' testCompile 'junit:junit:4.12' testCompile 'pl.pragmatists:JUnitParams:1.1.1' testCompile 'org.hamcrest:hamcrest-all:1.3' diff --git a/modDbImpl/src/module-info.java b/modDbImpl/src/module-info.java index 6b57ada028..62b434d9ba 100644 --- a/modDbImpl/src/module-info.java +++ b/modDbImpl/src/module-info.java @@ -7,6 +7,7 @@ requires h2.mvstore; requires com.google.common; requires mongo.java.driver; + requires leveldbjni.all; exports org.aion.db.impl; exports org.aion.db.impl.leveldb; diff --git a/modMcf/build.gradle b/modMcf/build.gradle index 17d95b08d9..b9c21305c3 100644 --- a/modMcf/build.gradle +++ b/modMcf/build.gradle @@ -28,6 +28,8 @@ dependencies { testCompile group: 'commons-codec', name: 'commons-codec', version: '1.10' testCompile "org.mockito:mockito-core:2.23.0" testCompile 'pl.pragmatists:JUnitParams:1.1.1' + testCompile 'network.aion:crypto4j:0.4.0' + } // Skip unit tests when doing build task; unit tests are all mixed up with From d93edf3ec26be7806735d876ba96eb1763846e29 Mon Sep 17 00:00:00 2001 From: AionJayT <jay.tseng@aion.network> Date: Thu, 7 Mar 2019 16:20:52 -0500 Subject: [PATCH 12/15] rlp.Value & TrieImpl.copyNode tune-up --- modMcf/src/org/aion/mcf/trie/TrieImpl.java | 2 +- modRlp/src/main/java/org/aion/rlp/Value.java | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modMcf/src/org/aion/mcf/trie/TrieImpl.java b/modMcf/src/org/aion/mcf/trie/TrieImpl.java index ac546947b3..8e5cc9de65 100644 --- a/modMcf/src/org/aion/mcf/trie/TrieImpl.java +++ b/modMcf/src/org/aion/mcf/trie/TrieImpl.java @@ -473,7 +473,7 @@ private boolean isEmptyNode(Object node) { || n.length() == 0); } - private Object[] copyNode(Value currentNode) { + private static Object[] copyNode(Value currentNode) { Object[] itemList = emptyStringSlice(LIST_SIZE); for (int i = 0; i < LIST_SIZE; i++) { Object cpy = currentNode.get(i).asObj(); diff --git a/modRlp/src/main/java/org/aion/rlp/Value.java b/modRlp/src/main/java/org/aion/rlp/Value.java index 4adeb07ea5..8fb5a5a0d4 100644 --- a/modRlp/src/main/java/org/aion/rlp/Value.java +++ b/modRlp/src/main/java/org/aion/rlp/Value.java @@ -147,13 +147,14 @@ public byte[] getData() { public Value get(int index) { if (isList()) { // Guard for OutOfBounds - if (asList().size() <= index) { + List<Object> list = asList(); + if (list.size() <= index) { return new Value(null); } if (index < 0) { throw new RuntimeException("Negative index not allowed"); } - return new Value(asList().get(index)); + return new Value(list.get(index)); } // If this wasn't a slice you probably shouldn't be using this function return new Value(null); From ede99517636a1b56d1c28025ef2a38bceba249a9 Mon Sep 17 00:00:00 2001 From: AionJayT <jay.tseng@aion.network> Date: Tue, 12 Mar 2019 13:49:10 -0400 Subject: [PATCH 13/15] remove unused file --- .../test/org/aion/zero/impl/BlockChainContractStoreTest.java | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 modAionImpl/test/org/aion/zero/impl/BlockChainContractStoreTest.java diff --git a/modAionImpl/test/org/aion/zero/impl/BlockChainContractStoreTest.java b/modAionImpl/test/org/aion/zero/impl/BlockChainContractStoreTest.java deleted file mode 100644 index 8373d9eaf4..0000000000 --- a/modAionImpl/test/org/aion/zero/impl/BlockChainContractStoreTest.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.aion.zero.impl; - -public class BlockChainContractStoreTest { -} From e376ed5393f8bcd77a4ae6340dca70af8de4d495 Mon Sep 17 00:00:00 2001 From: AionJayT <jay.tseng@aion.network> Date: Tue, 12 Mar 2019 13:57:38 -0400 Subject: [PATCH 14/15] fix class import --- .../aion/zero/impl/BlockchainAccountStateBenchmark.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modAionImpl/test/org/aion/zero/impl/BlockchainAccountStateBenchmark.java b/modAionImpl/test/org/aion/zero/impl/BlockchainAccountStateBenchmark.java index a4f781072f..7ea44f8d4b 100644 --- a/modAionImpl/test/org/aion/zero/impl/BlockchainAccountStateBenchmark.java +++ b/modAionImpl/test/org/aion/zero/impl/BlockchainAccountStateBenchmark.java @@ -7,7 +7,12 @@ import java.io.File; import java.math.BigInteger; import java.security.SecureRandom; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Random; +import java.util.ArrayList; +import java.util.Collections; import org.aion.types.Address; import org.aion.crypto.ECKey; From 761147ca69e3f66c5de21dd2675c422fd66d8799 Mon Sep 17 00:00:00 2001 From: AionJayT <jay.tseng@aion.network> Date: Wed, 13 Mar 2019 15:32:20 -0400 Subject: [PATCH 15/15] remove unused constructor argument --- .../org/aion/zero/impl/BlockchainAccountStateBenchmark.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modAionImpl/test/org/aion/zero/impl/BlockchainAccountStateBenchmark.java b/modAionImpl/test/org/aion/zero/impl/BlockchainAccountStateBenchmark.java index 25325cd2da..221560c14f 100644 --- a/modAionImpl/test/org/aion/zero/impl/BlockchainAccountStateBenchmark.java +++ b/modAionImpl/test/org/aion/zero/impl/BlockchainAccountStateBenchmark.java @@ -136,7 +136,7 @@ public String getDbPath() { private StandaloneBlockchain.Bundle bundle; - public BlockchainAccountStateBenchmark(String name, StandaloneBlockchain.Bundle bundle) { + public BlockchainAccountStateBenchmark(StandaloneBlockchain.Bundle bundle) { this.bundle = bundle; }