Compare commits
22 Commits
main
...
feature/me
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cba2a778c3 | ||
|
|
7a71c6bbfe | ||
|
|
7fe82bde44 | ||
|
|
1bbe8234fd | ||
|
|
358bdda5a6 | ||
|
|
22d8b22ea1 | ||
|
|
6d0777930d | ||
|
|
3884a2a0ca | ||
|
|
212b175b6b | ||
|
|
9905bf2964 | ||
|
|
5db6a5bfe2 | ||
|
|
3d88a95823 | ||
|
|
bf7c7e7144 | ||
|
|
bb6992cadb | ||
|
|
fcf488b065 | ||
|
|
33d508fb44 | ||
|
|
00bc56adc3 | ||
|
|
b8847bd8a9 | ||
|
|
52ea558811 | ||
|
|
b32a6e6bbb | ||
|
|
5c83114197 | ||
|
|
963a9b074a |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/.build
|
||||||
|
*.xcuserstate
|
||||||
@@ -1,289 +0,0 @@
|
|||||||
// !$*UTF8*$!
|
|
||||||
{
|
|
||||||
archiveVersion = 1;
|
|
||||||
classes = {
|
|
||||||
};
|
|
||||||
objectVersion = 50;
|
|
||||||
objects = {
|
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
|
||||||
B5E66EBD2407FDE000E89D17 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E66EBC2407FDE000E89D17 /* main.swift */; };
|
|
||||||
/* End PBXBuildFile section */
|
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
|
||||||
B5E66EB72407FDE000E89D17 /* CopyFiles */ = {
|
|
||||||
isa = PBXCopyFilesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
dstPath = /usr/share/man/man1/;
|
|
||||||
dstSubfolderSpec = 0;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 1;
|
|
||||||
};
|
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
|
||||||
B5E66EB92407FDE000E89D17 /* Blockchain */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Blockchain; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
B5E66EBC2407FDE000E89D17 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
|
||||||
/* End PBXFileReference section */
|
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
|
||||||
B5E66EB62407FDE000E89D17 /* Frameworks */ = {
|
|
||||||
isa = PBXFrameworksBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXFrameworksBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
|
||||||
B5E66EB02407FDE000E89D17 = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
B5E66EBB2407FDE000E89D17 /* Blockchain */,
|
|
||||||
B5E66EBA2407FDE000E89D17 /* Products */,
|
|
||||||
);
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
B5E66EBA2407FDE000E89D17 /* Products */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
B5E66EB92407FDE000E89D17 /* Blockchain */,
|
|
||||||
);
|
|
||||||
name = Products;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
B5E66EBB2407FDE000E89D17 /* Blockchain */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
B5E66EBC2407FDE000E89D17 /* main.swift */,
|
|
||||||
);
|
|
||||||
path = Blockchain;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
/* End PBXGroup section */
|
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
|
||||||
B5E66EB82407FDE000E89D17 /* Blockchain */ = {
|
|
||||||
isa = PBXNativeTarget;
|
|
||||||
buildConfigurationList = B5E66EC02407FDE000E89D17 /* Build configuration list for PBXNativeTarget "Blockchain" */;
|
|
||||||
buildPhases = (
|
|
||||||
B5E66EB52407FDE000E89D17 /* Sources */,
|
|
||||||
B5E66EB62407FDE000E89D17 /* Frameworks */,
|
|
||||||
B5E66EB72407FDE000E89D17 /* CopyFiles */,
|
|
||||||
);
|
|
||||||
buildRules = (
|
|
||||||
);
|
|
||||||
dependencies = (
|
|
||||||
);
|
|
||||||
name = Blockchain;
|
|
||||||
productName = Blockchain;
|
|
||||||
productReference = B5E66EB92407FDE000E89D17 /* Blockchain */;
|
|
||||||
productType = "com.apple.product-type.tool";
|
|
||||||
};
|
|
||||||
/* End PBXNativeTarget section */
|
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
|
||||||
B5E66EB12407FDE000E89D17 /* Project object */ = {
|
|
||||||
isa = PBXProject;
|
|
||||||
attributes = {
|
|
||||||
LastSwiftUpdateCheck = 1130;
|
|
||||||
LastUpgradeCheck = 1130;
|
|
||||||
ORGANIZATIONNAME = "Victor BODINAUD";
|
|
||||||
TargetAttributes = {
|
|
||||||
B5E66EB82407FDE000E89D17 = {
|
|
||||||
CreatedOnToolsVersion = 11.3.1;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
buildConfigurationList = B5E66EB42407FDE000E89D17 /* Build configuration list for PBXProject "Blockchain" */;
|
|
||||||
compatibilityVersion = "Xcode 9.3";
|
|
||||||
developmentRegion = en;
|
|
||||||
hasScannedForEncodings = 0;
|
|
||||||
knownRegions = (
|
|
||||||
en,
|
|
||||||
Base,
|
|
||||||
);
|
|
||||||
mainGroup = B5E66EB02407FDE000E89D17;
|
|
||||||
productRefGroup = B5E66EBA2407FDE000E89D17 /* Products */;
|
|
||||||
projectDirPath = "";
|
|
||||||
projectRoot = "";
|
|
||||||
targets = (
|
|
||||||
B5E66EB82407FDE000E89D17 /* Blockchain */,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
/* End PBXProject section */
|
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
|
||||||
B5E66EB52407FDE000E89D17 /* Sources */ = {
|
|
||||||
isa = PBXSourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
B5E66EBD2407FDE000E89D17 /* main.swift in Sources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXSourcesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
|
||||||
B5E66EBE2407FDE000E89D17 /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
|
||||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
|
||||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
|
||||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
|
||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_COMMA = YES;
|
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
|
||||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
|
||||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
|
||||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
|
||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
|
||||||
COPY_PHASE_STRIP = NO;
|
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
|
||||||
ENABLE_TESTABILITY = YES;
|
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
|
||||||
GCC_DYNAMIC_NO_PIC = NO;
|
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
|
||||||
"DEBUG=1",
|
|
||||||
"$(inherited)",
|
|
||||||
);
|
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
|
||||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
|
||||||
MTL_FAST_MATH = YES;
|
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
|
||||||
SDKROOT = macosx;
|
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
B5E66EBF2407FDE000E89D17 /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
|
||||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
|
||||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
|
||||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
|
||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_COMMA = YES;
|
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
|
||||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
|
||||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
|
||||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
|
||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
|
||||||
COPY_PHASE_STRIP = NO;
|
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
|
||||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
|
||||||
MTL_FAST_MATH = YES;
|
|
||||||
SDKROOT = macosx;
|
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
B5E66EC12407FDE000E89D17 /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
CODE_SIGN_STYLE = Automatic;
|
|
||||||
DEVELOPMENT_TEAM = WVH3Y23X7X;
|
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
SWIFT_VERSION = 5.0;
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
B5E66EC22407FDE000E89D17 /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
CODE_SIGN_STYLE = Automatic;
|
|
||||||
DEVELOPMENT_TEAM = WVH3Y23X7X;
|
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
SWIFT_VERSION = 5.0;
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
/* End XCBuildConfiguration section */
|
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
|
||||||
B5E66EB42407FDE000E89D17 /* Build configuration list for PBXProject "Blockchain" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
B5E66EBE2407FDE000E89D17 /* Debug */,
|
|
||||||
B5E66EBF2407FDE000E89D17 /* Release */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Release;
|
|
||||||
};
|
|
||||||
B5E66EC02407FDE000E89D17 /* Build configuration list for PBXNativeTarget "Blockchain" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
B5E66EC12407FDE000E89D17 /* Debug */,
|
|
||||||
B5E66EC22407FDE000E89D17 /* Release */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Release;
|
|
||||||
};
|
|
||||||
/* End XCConfigurationList section */
|
|
||||||
};
|
|
||||||
rootObject = B5E66EB12407FDE000E89D17 /* Project object */;
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>IDEDidComputeMac32BitWarning</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
//
|
|
||||||
// main.swift
|
|
||||||
// Blockchain
|
|
||||||
//
|
|
||||||
// Created by Victor BODINAUD on 27/02/2020.
|
|
||||||
// Copyright © 2020 Victor BODINAUD. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
print("Hello, World!")
|
|
||||||
|
|
||||||
658
SwiftChain.xcodeproj/project.pbxproj
Normal file
658
SwiftChain.xcodeproj/project.pbxproj
Normal file
@@ -0,0 +1,658 @@
|
|||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 77;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
576693F62DC8EDCE0024463C /* CryptoSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 576693F52DC8EDCE0024463C /* CryptoSwift */; };
|
||||||
|
57B6CA1A2CFDFA39009F401F /* SwiftChainCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 57B6CA102CFDFA39009F401F /* SwiftChainCore.framework */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
57B6CA1B2CFDFA39009F401F /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 57BA29782CF778F5009E4448 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 57B6CA0F2CFDFA39009F401F;
|
||||||
|
remoteInfo = SwiftChainCore;
|
||||||
|
};
|
||||||
|
57B6CA392CFDFB1F009F401F /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 57BA29782CF778F5009E4448 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 57B6CA0F2CFDFA39009F401F;
|
||||||
|
remoteInfo = SwiftChainCore;
|
||||||
|
};
|
||||||
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
57BA297E2CF778F5009E4448 /* CopyFiles */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = /usr/share/man/man1/;
|
||||||
|
dstSubfolderSpec = 0;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 1;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
57B6CA102CFDFA39009F401F /* SwiftChainCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftChainCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
57B6CA192CFDFA39009F401F /* SwiftChainCoreTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftChainCoreTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
57BA29802CF778F5009E4448 /* SwiftChain */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = SwiftChain; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||||
|
57B6CA252CFDFA39009F401F /* Exceptions for "SwiftChainCore" folder in "SwiftChainCore" target */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||||
|
publicHeaders = (
|
||||||
|
SwiftChainCore.h,
|
||||||
|
);
|
||||||
|
target = 57B6CA0F2CFDFA39009F401F /* SwiftChainCore */;
|
||||||
|
};
|
||||||
|
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||||
|
|
||||||
|
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||||
|
57B6CA112CFDFA39009F401F /* SwiftChainCore */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
exceptions = (
|
||||||
|
57B6CA252CFDFA39009F401F /* Exceptions for "SwiftChainCore" folder in "SwiftChainCore" target */,
|
||||||
|
);
|
||||||
|
path = SwiftChainCore;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
57B6CA1D2CFDFA39009F401F /* SwiftChainCoreTests */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
path = SwiftChainCoreTests;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
57BA29822CF778F5009E4448 /* SwiftChain */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
path = SwiftChain;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
57B6CA0D2CFDFA39009F401F /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
576693F62DC8EDCE0024463C /* CryptoSwift in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
57B6CA162CFDFA39009F401F /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
57B6CA1A2CFDFA39009F401F /* SwiftChainCore.framework in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
57BA297D2CF778F5009E4448 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
57BA29772CF778F5009E4448 = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
57BA29822CF778F5009E4448 /* SwiftChain */,
|
||||||
|
57B6CA112CFDFA39009F401F /* SwiftChainCore */,
|
||||||
|
57B6CA1D2CFDFA39009F401F /* SwiftChainCoreTests */,
|
||||||
|
57BA29812CF778F5009E4448 /* Products */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
57BA29812CF778F5009E4448 /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
57BA29802CF778F5009E4448 /* SwiftChain */,
|
||||||
|
57B6CA102CFDFA39009F401F /* SwiftChainCore.framework */,
|
||||||
|
57B6CA192CFDFA39009F401F /* SwiftChainCoreTests.xctest */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXHeadersBuildPhase section */
|
||||||
|
57B6CA0B2CFDFA39009F401F /* Headers */ = {
|
||||||
|
isa = PBXHeadersBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXHeadersBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
57B6CA0F2CFDFA39009F401F /* SwiftChainCore */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 57B6CA262CFDFA39009F401F /* Build configuration list for PBXNativeTarget "SwiftChainCore" */;
|
||||||
|
buildPhases = (
|
||||||
|
57B6CA0B2CFDFA39009F401F /* Headers */,
|
||||||
|
57B6CA0C2CFDFA39009F401F /* Sources */,
|
||||||
|
57B6CA0D2CFDFA39009F401F /* Frameworks */,
|
||||||
|
57B6CA0E2CFDFA39009F401F /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
5766942C2DC8FBA30024463C /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
fileSystemSynchronizedGroups = (
|
||||||
|
57B6CA112CFDFA39009F401F /* SwiftChainCore */,
|
||||||
|
);
|
||||||
|
name = SwiftChainCore;
|
||||||
|
packageProductDependencies = (
|
||||||
|
576693F52DC8EDCE0024463C /* CryptoSwift */,
|
||||||
|
);
|
||||||
|
productName = SwiftChainCore;
|
||||||
|
productReference = 57B6CA102CFDFA39009F401F /* SwiftChainCore.framework */;
|
||||||
|
productType = "com.apple.product-type.framework";
|
||||||
|
};
|
||||||
|
57B6CA182CFDFA39009F401F /* SwiftChainCoreTests */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 57B6CA272CFDFA39009F401F /* Build configuration list for PBXNativeTarget "SwiftChainCoreTests" */;
|
||||||
|
buildPhases = (
|
||||||
|
57B6CA152CFDFA39009F401F /* Sources */,
|
||||||
|
57B6CA162CFDFA39009F401F /* Frameworks */,
|
||||||
|
57B6CA172CFDFA39009F401F /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
57B6CA1C2CFDFA39009F401F /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
fileSystemSynchronizedGroups = (
|
||||||
|
57B6CA1D2CFDFA39009F401F /* SwiftChainCoreTests */,
|
||||||
|
);
|
||||||
|
name = SwiftChainCoreTests;
|
||||||
|
packageProductDependencies = (
|
||||||
|
);
|
||||||
|
productName = SwiftChainCoreTests;
|
||||||
|
productReference = 57B6CA192CFDFA39009F401F /* SwiftChainCoreTests.xctest */;
|
||||||
|
productType = "com.apple.product-type.bundle.unit-test";
|
||||||
|
};
|
||||||
|
57BA297F2CF778F5009E4448 /* SwiftChain */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 57BA29872CF778F5009E4448 /* Build configuration list for PBXNativeTarget "SwiftChain" */;
|
||||||
|
buildPhases = (
|
||||||
|
57BA297C2CF778F5009E4448 /* Sources */,
|
||||||
|
57BA297D2CF778F5009E4448 /* Frameworks */,
|
||||||
|
57BA297E2CF778F5009E4448 /* CopyFiles */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
57B6CA3A2CFDFB1F009F401F /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
fileSystemSynchronizedGroups = (
|
||||||
|
57BA29822CF778F5009E4448 /* SwiftChain */,
|
||||||
|
);
|
||||||
|
name = SwiftChain;
|
||||||
|
packageProductDependencies = (
|
||||||
|
57BA298B2CF77907009E4448 /* CryptoSwift */,
|
||||||
|
);
|
||||||
|
productName = SwiftChain;
|
||||||
|
productReference = 57BA29802CF778F5009E4448 /* SwiftChain */;
|
||||||
|
productType = "com.apple.product-type.tool";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
57BA29782CF778F5009E4448 /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
BuildIndependentTargetsInParallel = 1;
|
||||||
|
LastSwiftUpdateCheck = 1610;
|
||||||
|
LastUpgradeCheck = 1610;
|
||||||
|
TargetAttributes = {
|
||||||
|
57B6CA0F2CFDFA39009F401F = {
|
||||||
|
CreatedOnToolsVersion = 16.1;
|
||||||
|
};
|
||||||
|
57B6CA182CFDFA39009F401F = {
|
||||||
|
CreatedOnToolsVersion = 16.1;
|
||||||
|
};
|
||||||
|
57BA297F2CF778F5009E4448 = {
|
||||||
|
CreatedOnToolsVersion = 16.1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = 57BA297B2CF778F5009E4448 /* Build configuration list for PBXProject "SwiftChain" */;
|
||||||
|
developmentRegion = en;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
en,
|
||||||
|
Base,
|
||||||
|
);
|
||||||
|
mainGroup = 57BA29772CF778F5009E4448;
|
||||||
|
minimizedProjectReferenceProxies = 1;
|
||||||
|
packageReferences = (
|
||||||
|
57BA298A2CF77907009E4448 /* XCRemoteSwiftPackageReference "CryptoSwift" */,
|
||||||
|
);
|
||||||
|
preferredProjectObjectVersion = 77;
|
||||||
|
productRefGroup = 57BA29812CF778F5009E4448 /* Products */;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
57BA297F2CF778F5009E4448 /* SwiftChain */,
|
||||||
|
57B6CA0F2CFDFA39009F401F /* SwiftChainCore */,
|
||||||
|
57B6CA182CFDFA39009F401F /* SwiftChainCoreTests */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
57B6CA0E2CFDFA39009F401F /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
57B6CA172CFDFA39009F401F /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
57B6CA0C2CFDFA39009F401F /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
57B6CA152CFDFA39009F401F /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
57BA297C2CF778F5009E4448 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */
|
||||||
|
5766942C2DC8FBA30024463C /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
productRef = 5766942B2DC8FBA30024463C /* CryptoSwift */;
|
||||||
|
};
|
||||||
|
57B6CA1C2CFDFA39009F401F /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 57B6CA0F2CFDFA39009F401F /* SwiftChainCore */;
|
||||||
|
targetProxy = 57B6CA1B2CFDFA39009F401F /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
57B6CA3A2CFDFB1F009F401F /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 57B6CA0F2CFDFA39009F401F /* SwiftChainCore */;
|
||||||
|
targetProxy = 57B6CA392CFDFB1F009F401F /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
57B6CA212CFDFA39009F401F /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES;
|
||||||
|
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEFINES_MODULE = YES;
|
||||||
|
DEVELOPMENT_TEAM = WVH3Y23X7X;
|
||||||
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
|
DYLIB_CURRENT_VERSION = 1;
|
||||||
|
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||||
|
ENABLE_MODULE_VERIFIER = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 18.1;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@loader_path/Frameworks",
|
||||||
|
);
|
||||||
|
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = (
|
||||||
|
"@executable_path/../Frameworks",
|
||||||
|
"@loader_path/Frameworks",
|
||||||
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 15.1;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
|
||||||
|
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.bodinaud.SwiftChainCore;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||||
|
SDKROOT = auto;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator";
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_INSTALL_OBJC_HEADER = NO;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2,7";
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
VERSION_INFO_PREFIX = "";
|
||||||
|
XROS_DEPLOYMENT_TARGET = 2.1;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
57B6CA222CFDFA39009F401F /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES;
|
||||||
|
BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEFINES_MODULE = YES;
|
||||||
|
DEVELOPMENT_TEAM = WVH3Y23X7X;
|
||||||
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
|
DYLIB_CURRENT_VERSION = 1;
|
||||||
|
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||||
|
ENABLE_MODULE_VERIFIER = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 18.1;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@loader_path/Frameworks",
|
||||||
|
);
|
||||||
|
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = (
|
||||||
|
"@executable_path/../Frameworks",
|
||||||
|
"@loader_path/Frameworks",
|
||||||
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 15.1;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
|
||||||
|
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.bodinaud.SwiftChainCore;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||||
|
SDKROOT = auto;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator";
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_INSTALL_OBJC_HEADER = NO;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2,7";
|
||||||
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
|
VERSION_INFO_PREFIX = "";
|
||||||
|
XROS_DEPLOYMENT_TARGET = 2.1;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
57B6CA232CFDFA39009F401F /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = WVH3Y23X7X;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 18.1;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 15.1;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.bodinaud.SwiftChainCoreTests;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = auto;
|
||||||
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator";
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2,7";
|
||||||
|
XROS_DEPLOYMENT_TARGET = 2.1;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
57B6CA242CFDFA39009F401F /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = WVH3Y23X7X;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 18.1;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 15.1;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.bodinaud.SwiftChainCoreTests;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SDKROOT = auto;
|
||||||
|
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx xros xrsimulator";
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2,7";
|
||||||
|
XROS_DEPLOYMENT_TARGET = 2.1;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
57BA29852CF778F5009E4448 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"DEBUG=1",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 15.1;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
SDKROOT = macosx;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
57BA29862CF778F5009E4448 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 15.1;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
SDKROOT = macosx;
|
||||||
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
57BA29882CF778F5009E4448 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
DEVELOPMENT_TEAM = WVH3Y23X7X;
|
||||||
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
57BA29892CF778F5009E4448 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
DEVELOPMENT_TEAM = WVH3Y23X7X;
|
||||||
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
57B6CA262CFDFA39009F401F /* Build configuration list for PBXNativeTarget "SwiftChainCore" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
57B6CA212CFDFA39009F401F /* Debug */,
|
||||||
|
57B6CA222CFDFA39009F401F /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
57B6CA272CFDFA39009F401F /* Build configuration list for PBXNativeTarget "SwiftChainCoreTests" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
57B6CA232CFDFA39009F401F /* Debug */,
|
||||||
|
57B6CA242CFDFA39009F401F /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
57BA297B2CF778F5009E4448 /* Build configuration list for PBXProject "SwiftChain" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
57BA29852CF778F5009E4448 /* Debug */,
|
||||||
|
57BA29862CF778F5009E4448 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
57BA29872CF778F5009E4448 /* Build configuration list for PBXNativeTarget "SwiftChain" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
57BA29882CF778F5009E4448 /* Debug */,
|
||||||
|
57BA29892CF778F5009E4448 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
|
57BA298A2CF77907009E4448 /* XCRemoteSwiftPackageReference "CryptoSwift" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/krzyzanowskim/CryptoSwift.git";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 1.8.3;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
576693F52DC8EDCE0024463C /* CryptoSwift */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = CryptoSwift;
|
||||||
|
};
|
||||||
|
5766942B2DC8FBA30024463C /* CryptoSwift */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 57BA298A2CF77907009E4448 /* XCRemoteSwiftPackageReference "CryptoSwift" */;
|
||||||
|
productName = CryptoSwift;
|
||||||
|
};
|
||||||
|
57BA298B2CF77907009E4448 /* CryptoSwift */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 57BA298A2CF77907009E4448 /* XCRemoteSwiftPackageReference "CryptoSwift" */;
|
||||||
|
productName = CryptoSwift;
|
||||||
|
};
|
||||||
|
/* End XCSwiftPackageProductDependency section */
|
||||||
|
};
|
||||||
|
rootObject = 57BA29782CF778F5009E4448 /* Project object */;
|
||||||
|
}
|
||||||
@@ -2,6 +2,6 @@
|
|||||||
<Workspace
|
<Workspace
|
||||||
version = "1.0">
|
version = "1.0">
|
||||||
<FileRef
|
<FileRef
|
||||||
location = "self:Blockchain.xcodeproj">
|
location = "self:">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
</Workspace>
|
</Workspace>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"originHash" : "72c2bbc73844ab67fc7d9913c0750200cb8c2703ae8ac776acd11affc367875c",
|
||||||
|
"pins" : [
|
||||||
|
{
|
||||||
|
"identity" : "cryptoswift",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/krzyzanowskim/CryptoSwift.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "729e01bc9b9dab466ac85f21fb9ee2bc1c61b258",
|
||||||
|
"version" : "1.8.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version" : 3
|
||||||
|
}
|
||||||
@@ -4,7 +4,12 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>SchemeUserState</key>
|
<key>SchemeUserState</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>Blockchain.xcscheme_^#shared#^_</key>
|
<key>SwiftChain.xcscheme_^#shared#^_</key>
|
||||||
|
<dict>
|
||||||
|
<key>orderHint</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
</dict>
|
||||||
|
<key>SwiftChainCore.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>0</integer>
|
<integer>0</integer>
|
||||||
174
SwiftChain/main.swift
Normal file
174
SwiftChain/main.swift
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
//
|
||||||
|
// main.swift
|
||||||
|
// Blockchain
|
||||||
|
//
|
||||||
|
// Created by Victor BODINAUD on 27/02/2020.
|
||||||
|
// Copyright © 2020 Victor BODINAUD. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftChainCore
|
||||||
|
|
||||||
|
// Create two nodes on different ports
|
||||||
|
let node1 = Node(port: 8333)
|
||||||
|
let node2 = Node(port: 8334)
|
||||||
|
|
||||||
|
// Track which node is active
|
||||||
|
var activeNode = node1
|
||||||
|
var activeNodeName = "Node 1"
|
||||||
|
|
||||||
|
var command: String?
|
||||||
|
var currentMinerAddress = "MINER1"
|
||||||
|
var wallets: [String: Wallet] = [:]
|
||||||
|
|
||||||
|
// Connect nodes to each other (add after node initialization)
|
||||||
|
node1.connectToPeer(host: "localhost", port: 8334) // Connect to node2
|
||||||
|
node2.connectToPeer(host: "localhost", port: 8333) // Connect to node1
|
||||||
|
|
||||||
|
print("""
|
||||||
|
Blockchain CLI - Commandes disponibles:
|
||||||
|
- createwallet : Créer un nouveau wallet
|
||||||
|
- listwallet : Liste des wallets
|
||||||
|
- mine : Miner un nouveau bloc
|
||||||
|
- send : Envoyer des tokens
|
||||||
|
- balance : Voir le solde d'une adresse
|
||||||
|
- pending : Voir les transactions en attente
|
||||||
|
- validity : Vérifier la validité de la chaîne
|
||||||
|
- setminer : Changer l'adresse du mineur
|
||||||
|
- connect <host> : Se connecter à un pair
|
||||||
|
- peers : Liste des pairs connectés
|
||||||
|
- mempool : État du mempool
|
||||||
|
- switchnode : Basculer entre Node 1 et Node 2
|
||||||
|
- exit : Quitter
|
||||||
|
|
||||||
|
Node actif: \(activeNodeName)
|
||||||
|
""")
|
||||||
|
|
||||||
|
repeat {
|
||||||
|
LogManager.log("\nEntrer une commande:", level: .info)
|
||||||
|
command = readLine()?.lowercased()
|
||||||
|
|
||||||
|
switch command {
|
||||||
|
case "connect":
|
||||||
|
LogManager.log("Entrez l'adresse du pair (ex: 192.168.1.100):", level: .info)
|
||||||
|
if let host = readLine() {
|
||||||
|
activeNode.connectToPeer(host: host)
|
||||||
|
}
|
||||||
|
|
||||||
|
case "peers":
|
||||||
|
activeNode.listPeers()
|
||||||
|
|
||||||
|
case "mine":
|
||||||
|
if let _ = activeNode.mineBlock(minerAddress: currentMinerAddress) {
|
||||||
|
LogManager.log("Bloc miné avec succès. Récompense envoyée à \(currentMinerAddress)", level: .success)
|
||||||
|
LogManager.log("Nouveau solde: \(activeNode.getBalance(currentMinerAddress))", level: .info)
|
||||||
|
} else {
|
||||||
|
LogManager.log("Échec du minage", level: .error)
|
||||||
|
}
|
||||||
|
|
||||||
|
case "balance":
|
||||||
|
LogManager.log("Entrer l'adresse:", level: .info)
|
||||||
|
if let address = readLine() {
|
||||||
|
let balance = activeNode.getBalance(address)
|
||||||
|
LogManager.log("Solde de \(address): \(balance)", level: .info)
|
||||||
|
}
|
||||||
|
|
||||||
|
case "setminer":
|
||||||
|
LogManager.log("Nouvelle adresse du mineur:", level: .info)
|
||||||
|
if let address = readLine() {
|
||||||
|
currentMinerAddress = address
|
||||||
|
LogManager.log("Adresse du mineur mise à jour: \(currentMinerAddress)", level: .success)
|
||||||
|
}
|
||||||
|
|
||||||
|
case "pending":
|
||||||
|
let transactions = activeNode.getPendingTransactions()
|
||||||
|
LogManager.log("Transactions en attente: \(transactions.count)", level: .info)
|
||||||
|
for (index, tx) in transactions.enumerated() {
|
||||||
|
LogManager.log("""
|
||||||
|
\(index + 1). De: \(tx.sender)
|
||||||
|
À: \(tx.receiver)
|
||||||
|
Montant: \(tx.amount)
|
||||||
|
Type: \(tx.type)
|
||||||
|
""", level: .info)
|
||||||
|
}
|
||||||
|
|
||||||
|
case "validity":
|
||||||
|
let isValid = activeNode.isChainValid()
|
||||||
|
LogManager.log("Chaîne valide: \(isValid)", level: .success)
|
||||||
|
|
||||||
|
case "exit":
|
||||||
|
LogManager.log("Au revoir!", level: .info)
|
||||||
|
|
||||||
|
case "createwallet":
|
||||||
|
let wallet = Wallet()
|
||||||
|
wallets[wallet.address] = wallet
|
||||||
|
LogManager.log("Nouveau wallet créé!", level: .success)
|
||||||
|
LogManager.log("Adresse: \(wallet.address)", level: .success)
|
||||||
|
|
||||||
|
case "listwallet":
|
||||||
|
LogManager.log("\nWallets disponibles:", level: .info)
|
||||||
|
for (address, _) in wallets {
|
||||||
|
LogManager.log("- \(address) (Solde: \(activeNode.getBalance(address)))", level: .info)
|
||||||
|
}
|
||||||
|
|
||||||
|
case "send":
|
||||||
|
LogManager.log("Votre adresse (wallet):", level: .info)
|
||||||
|
guard let senderAddress = readLine(),
|
||||||
|
let wallet = wallets[senderAddress]
|
||||||
|
else {
|
||||||
|
LogManager.log("Wallet non trouvé", level: .error)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
LogManager.log("Adresse du destinataire:", level: .info)
|
||||||
|
guard let receiverAddress = readLine() else {
|
||||||
|
LogManager.log("Adresse du destinataire invalide", level: .error)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Montant:")
|
||||||
|
guard let amountStr = readLine(),
|
||||||
|
let amount = Int(amountStr),
|
||||||
|
amount > 0
|
||||||
|
else {
|
||||||
|
LogManager.log("Montant invalide", level: .error)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
let transaction = Transaction(
|
||||||
|
sender: senderAddress,
|
||||||
|
receiver: receiverAddress,
|
||||||
|
amount: amount,
|
||||||
|
type: "TRANSFER"
|
||||||
|
)
|
||||||
|
|
||||||
|
transaction.senderPublicKey = wallet.getPublicKeyData()
|
||||||
|
if let signature = wallet.signTransaction(transaction) {
|
||||||
|
transaction.signature = signature
|
||||||
|
if activeNode.submitTransaction(transaction) {
|
||||||
|
LogManager.log("Transaction signée et propagée au réseau!", level: .success)
|
||||||
|
} else {
|
||||||
|
LogManager.log("Erreur lors de l'envoi de la transaction", level: .error)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LogManager.log("Erreur lors de la signature de la transaction", level: .error)
|
||||||
|
}
|
||||||
|
|
||||||
|
case "mempool":
|
||||||
|
activeNode.printMemPoolStatus()
|
||||||
|
|
||||||
|
case "switchnode":
|
||||||
|
if activeNode === node1 {
|
||||||
|
activeNode = node2
|
||||||
|
activeNodeName = "Node 2"
|
||||||
|
} else {
|
||||||
|
activeNode = node1
|
||||||
|
activeNodeName = "Node 1"
|
||||||
|
}
|
||||||
|
LogManager.log("Node actif: \(activeNodeName)", level: .info)
|
||||||
|
|
||||||
|
default:
|
||||||
|
LogManager.log("Commande inconnue", level: .error)
|
||||||
|
}
|
||||||
|
|
||||||
|
} while command != "exit"
|
||||||
18
SwiftChainCore/Models/Account.swift
Normal file
18
SwiftChainCore/Models/Account.swift
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
//
|
||||||
|
// File.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Victor BODINAUD on 31/03/2021.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public class Account {
|
||||||
|
var id: String
|
||||||
|
var balance: Int
|
||||||
|
|
||||||
|
init(id: String, balance: Int) {
|
||||||
|
self.id = id
|
||||||
|
self.balance = balance
|
||||||
|
}
|
||||||
|
}
|
||||||
66
SwiftChainCore/Models/AccountManager.swift
Normal file
66
SwiftChainCore/Models/AccountManager.swift
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
//
|
||||||
|
// AccountManager.swift
|
||||||
|
// SwiftChain
|
||||||
|
//
|
||||||
|
// Created by Victor on 27/11/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
public class AccountManager {
|
||||||
|
private var accounts: [String: Account] = [:]
|
||||||
|
private let miningReward = 50
|
||||||
|
|
||||||
|
func getAccount(_ address: String) -> Account {
|
||||||
|
if let account = accounts[address] {
|
||||||
|
return account
|
||||||
|
}
|
||||||
|
let newAccount = Account(id: address, balance: 0)
|
||||||
|
accounts[address] = newAccount
|
||||||
|
return newAccount
|
||||||
|
}
|
||||||
|
|
||||||
|
func canProcessTransaction(_ transaction: Transaction) -> Bool {
|
||||||
|
if transaction.type == "MINING_REWARD" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
let senderAccount = getAccount(transaction.sender)
|
||||||
|
return senderAccount.balance >= transaction.amount
|
||||||
|
}
|
||||||
|
|
||||||
|
func processTransaction(_ transaction: Transaction) -> Bool {
|
||||||
|
if !canProcessTransaction(transaction) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if transaction.type == "MINING_REWARD" {
|
||||||
|
let minerAccount = getAccount(transaction.receiver)
|
||||||
|
minerAccount.balance += miningReward
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
let senderAccount = getAccount(transaction.sender)
|
||||||
|
let receiverAccount = getAccount(transaction.receiver)
|
||||||
|
|
||||||
|
senderAccount.balance -= transaction.amount
|
||||||
|
receiverAccount.balance += transaction.amount
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBalance(_ address: String) -> Int {
|
||||||
|
return getAccount(address).balance
|
||||||
|
}
|
||||||
|
|
||||||
|
func processBlock(_ block: Block) -> Bool {
|
||||||
|
let tempAccounts = accounts
|
||||||
|
|
||||||
|
for transaction in block.transactions {
|
||||||
|
if !processTransaction(transaction) {
|
||||||
|
accounts = tempAccounts
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
116
SwiftChainCore/Models/Block.swift
Normal file
116
SwiftChainCore/Models/Block.swift
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
//
|
||||||
|
// Block.swift
|
||||||
|
// Blockchain
|
||||||
|
//
|
||||||
|
// Created by Victor BODINAUD on 27/02/2020.
|
||||||
|
// Copyright © 2020 Victor BODINAUD. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
internal import CryptoSwift
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public class Block: Codable {
|
||||||
|
var hash: String
|
||||||
|
var transactions: [Transaction]
|
||||||
|
var previousHash: String
|
||||||
|
var index: Int
|
||||||
|
var nonce: Int
|
||||||
|
var timestamp: Int
|
||||||
|
var difficulty: Int
|
||||||
|
var miner: String?
|
||||||
|
|
||||||
|
enum CodingKeys: String, CodingKey {
|
||||||
|
case hash, transactions, previousHash, index, nonce, timestamp, difficulty, miner
|
||||||
|
}
|
||||||
|
|
||||||
|
init(hash: String = "", transactions: [Transaction] = [], previousHash: String = "",
|
||||||
|
index: Int = 0, nonce: Int = 0, timestamp: Int = 0, difficulty: Int = 4)
|
||||||
|
{
|
||||||
|
self.hash = hash
|
||||||
|
self.transactions = transactions
|
||||||
|
self.previousHash = previousHash
|
||||||
|
self.index = index
|
||||||
|
self.nonce = nonce
|
||||||
|
self.timestamp = timestamp
|
||||||
|
self.difficulty = difficulty
|
||||||
|
}
|
||||||
|
|
||||||
|
func mineBlock() -> Double {
|
||||||
|
let startTime = Date()
|
||||||
|
let target = String(repeating: "0", count: difficulty)
|
||||||
|
|
||||||
|
print("Mining block \(index) with difficulty \(difficulty)...")
|
||||||
|
|
||||||
|
repeat {
|
||||||
|
nonce += 1
|
||||||
|
hash = generateHash()
|
||||||
|
} while !hash.hasPrefix(target)
|
||||||
|
|
||||||
|
let timeElapsed = Date().timeIntervalSince(startTime)
|
||||||
|
print("Block mined! Nonce: \(nonce), Hash: \(hash)")
|
||||||
|
|
||||||
|
return timeElapsed
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValid() -> Bool {
|
||||||
|
// Vérification de base du hash
|
||||||
|
let target = String(repeating: "0", count: difficulty)
|
||||||
|
if hash != generateHash() || !hash.hasPrefix(target) {
|
||||||
|
print("Block \(index): Invalid hash")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier l'index
|
||||||
|
if index < 0 {
|
||||||
|
print("Block: Invalid index")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier le timestamp
|
||||||
|
let currentTime = Int(Date().timeIntervalSince1970)
|
||||||
|
if timestamp > currentTime + 7200 || timestamp < 1701388800 { // 2h dans le futur max
|
||||||
|
print("Block: Invalid timestamp")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier les transactions
|
||||||
|
for transaction in transactions {
|
||||||
|
if !transaction.isValid() {
|
||||||
|
print("Block: Invalid transaction found")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier qu'il n'y a qu'une seule récompense de minage
|
||||||
|
if transaction.type == "MINING_REWARD" {
|
||||||
|
if transactions.filter({ $0.type == "MINING_REWARD" }).count > 1 {
|
||||||
|
print("Block: Multiple mining rewards found")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateHash() -> String {
|
||||||
|
let txData = transactions.map {
|
||||||
|
"\($0.sender)\($0.receiver)\($0.amount)\($0.type)"
|
||||||
|
}.joined()
|
||||||
|
|
||||||
|
return "\(previousHash)\(txData)\(String(timestamp))\(String(index))\(String(nonce))".sha256()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Structure pour suivre la progression du mining
|
||||||
|
struct MiningProgress {
|
||||||
|
let hashesChecked: Int
|
||||||
|
let hashRate: Double
|
||||||
|
let elapsedTime: TimeInterval
|
||||||
|
let estimatedTimeRemaining: TimeInterval?
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erreurs possibles pendant le mining
|
||||||
|
enum MiningError: Error {
|
||||||
|
case cancelled
|
||||||
|
case failed(String)
|
||||||
|
}
|
||||||
127
SwiftChainCore/Models/Blockchain.swift
Normal file
127
SwiftChainCore/Models/Blockchain.swift
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
//
|
||||||
|
// Blockchain.swift
|
||||||
|
// Blockchain
|
||||||
|
//
|
||||||
|
// Created by Victor BODINAUD on 27/02/2020.
|
||||||
|
// Copyright © 2020 Victor BODINAUD. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public class Blockchain {
|
||||||
|
var chain = [Block]()
|
||||||
|
let minDifficulty = 2
|
||||||
|
let maxDifficulty = 6
|
||||||
|
let targetBlockTime = 10.0 // en secondes
|
||||||
|
|
||||||
|
static let genesisBlock: Block = {
|
||||||
|
let block = Block()
|
||||||
|
block.previousHash = "0000genesis0000"
|
||||||
|
block.index = 0
|
||||||
|
block.timestamp = 1701388800
|
||||||
|
block.difficulty = 4
|
||||||
|
block.nonce = 12345
|
||||||
|
block.hash = "000088c1731bed4996680d2c50ea3d9b573c1507d2d61866c0deff33a7f8cf5e"
|
||||||
|
return block
|
||||||
|
}()
|
||||||
|
|
||||||
|
init() {
|
||||||
|
chain.append(Blockchain.genesisBlock)
|
||||||
|
print("Genesis block initialized -- hash: \(Blockchain.genesisBlock.hash)")
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateNewDifficulty() -> Int {
|
||||||
|
guard chain.count >= 2 else { return minDifficulty }
|
||||||
|
|
||||||
|
let lastBlock = chain.last!
|
||||||
|
let previousBlock = chain[chain.count - 2]
|
||||||
|
let timeSpent = Double(lastBlock.timestamp - previousBlock.timestamp)
|
||||||
|
|
||||||
|
if timeSpent < targetBlockTime / 2 {
|
||||||
|
return min(lastBlock.difficulty + 1, maxDifficulty)
|
||||||
|
} else if timeSpent > targetBlockTime * 2 {
|
||||||
|
return max(lastBlock.difficulty - 1, minDifficulty)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastBlock.difficulty
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateChain(_ chain: [Block]) -> Bool {
|
||||||
|
guard let firstBlock = chain.first,
|
||||||
|
firstBlock.hash == Blockchain.genesisBlock.hash
|
||||||
|
else {
|
||||||
|
print("Genesis block mismatch")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 1..<chain.count {
|
||||||
|
let block = chain[i]
|
||||||
|
let previousBlock = chain[i - 1]
|
||||||
|
|
||||||
|
if block.previousHash != previousBlock.hash {
|
||||||
|
print("Invalid chain at block \(i): incorrect previous hash")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !block.isValid() {
|
||||||
|
print("Invalid chain at block \(i): invalid block hash")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateChainTotally(_ chain: [Block]) -> Bool {
|
||||||
|
// Vérifier le genesis block
|
||||||
|
guard let firstBlock = chain.first,
|
||||||
|
firstBlock.hash == Blockchain.genesisBlock.hash
|
||||||
|
else {
|
||||||
|
print("Chain: Invalid genesis block")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier la séquence des blocs
|
||||||
|
for i in 1..<chain.count {
|
||||||
|
let block = chain[i]
|
||||||
|
let previousBlock = chain[i - 1]
|
||||||
|
|
||||||
|
// Vérifier le chaînage
|
||||||
|
if block.previousHash != previousBlock.hash {
|
||||||
|
print("Chain: Invalid block sequence at height \(i)")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier l'index
|
||||||
|
if block.index != i {
|
||||||
|
print("Chain: Invalid block index at height \(i)")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier la chronologie
|
||||||
|
if block.timestamp <= previousBlock.timestamp {
|
||||||
|
print("Chain: Invalid timestamp at height \(i)")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier la difficulté
|
||||||
|
let expectedDifficulty = calculateExpectedDifficulty(at: i, chain: chain)
|
||||||
|
if block.difficulty != expectedDifficulty {
|
||||||
|
print("Chain: Invalid difficulty at height \(i)")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier le bloc lui-même
|
||||||
|
if !block.isValid() {
|
||||||
|
print("Chain: Invalid block at height \(i)")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private func calculateExpectedDifficulty(at index: Int, chain: [Block]) -> Int {
|
||||||
|
return chain[index].difficulty
|
||||||
|
}
|
||||||
|
}
|
||||||
38
SwiftChainCore/Models/LogManager.swift
Normal file
38
SwiftChainCore/Models/LogManager.swift
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
//
|
||||||
|
// LogManager.swift
|
||||||
|
// SwiftChain
|
||||||
|
//
|
||||||
|
// Created by Victor on 05/05/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
import os.log
|
||||||
|
|
||||||
|
public struct LogManager {
|
||||||
|
public enum LogLevel: String {
|
||||||
|
case info = "INFO"
|
||||||
|
case success = "SUCCÈS"
|
||||||
|
case warning = "ATTENTION"
|
||||||
|
case error = "ERREUR"
|
||||||
|
case network = "RÉSEAU"
|
||||||
|
case blockchain = "BLOCKCHAIN"
|
||||||
|
|
||||||
|
var logType: OSLogType {
|
||||||
|
switch self {
|
||||||
|
case .info, .success: return .info
|
||||||
|
case .warning: return .debug
|
||||||
|
case .error: return .error
|
||||||
|
case .network, .blockchain: return .default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static let log = OSLog(subsystem: "com.votreapp.swiftchain", category: "blockchain")
|
||||||
|
|
||||||
|
public static func log(_ message: String, level: LogLevel) {
|
||||||
|
os_log("%{public}@: %{public}@", log: log, type: level.logType, level.rawValue, message)
|
||||||
|
|
||||||
|
// Également afficher dans la console pour le CLI
|
||||||
|
let timestamp = DateFormatter.localizedString(from: Date(), dateStyle: .none, timeStyle: .medium)
|
||||||
|
print("[\(timestamp)] [\(level.rawValue)] \(message)")
|
||||||
|
}
|
||||||
|
}
|
||||||
130
SwiftChainCore/Models/MemPool.swift
Normal file
130
SwiftChainCore/Models/MemPool.swift
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
//
|
||||||
|
// MemPool.swift
|
||||||
|
// SwiftChain
|
||||||
|
//
|
||||||
|
// Created by Victor on 27/11/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
public class MemPool {
|
||||||
|
private var pendingTransactions: [Transaction] = []
|
||||||
|
private let maxTransactionsPerBlock: Int = 10
|
||||||
|
private let accountManager: AccountManager
|
||||||
|
|
||||||
|
init(accountManager: AccountManager) {
|
||||||
|
self.accountManager = accountManager
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Ajoute une transaction au mempool après validation
|
||||||
|
*/
|
||||||
|
func addTransaction(_ transaction: Transaction) -> Bool {
|
||||||
|
if validateTransaction(transaction) {
|
||||||
|
pendingTransactions.append(transaction)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Valide une transaction avant de l'ajouter au pool
|
||||||
|
*/
|
||||||
|
private func validateTransaction(_ transaction: Transaction) -> Bool {
|
||||||
|
// Vérifications basiques
|
||||||
|
if transaction.amount <= 0 {
|
||||||
|
print("MemPool: Transaction refusée - montant invalide")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pas besoin de vérifier la signature pour les récompenses de minage
|
||||||
|
if transaction.type == "MINING_REWARD" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier la signature
|
||||||
|
if !transaction.isSignatureValid() {
|
||||||
|
print("MemPool: Transaction refusée - signature invalide")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier le solde
|
||||||
|
if !accountManager.canProcessTransaction(transaction) {
|
||||||
|
print("MemPool: Transaction refusée - solde insuffisant")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
print("MemPool: Transaction validée avec succès")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Récupère un lot de transactions prêtes pour le prochain bloc
|
||||||
|
*/
|
||||||
|
func getTransactionsForBlock() -> [Transaction] {
|
||||||
|
var validTransactions: [Transaction] = []
|
||||||
|
var remainingTransactions: [Transaction] = []
|
||||||
|
|
||||||
|
for transaction in pendingTransactions {
|
||||||
|
if validTransactions.count >= maxTransactionsPerBlock {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if validateTransaction(transaction) {
|
||||||
|
validTransactions.append(transaction)
|
||||||
|
} else {
|
||||||
|
remainingTransactions.append(transaction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update pending transactions, removing the selected ones
|
||||||
|
pendingTransactions = remainingTransactions + pendingTransactions.dropFirst(validTransactions.count)
|
||||||
|
|
||||||
|
return validTransactions
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Vérifie si il y a des transactions en attente
|
||||||
|
*/
|
||||||
|
var hasPendingTransactions: Bool {
|
||||||
|
return !pendingTransactions.isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retourne toutes les transactions en attente sans les retirer
|
||||||
|
*/
|
||||||
|
func getAllPendingTransactions() -> [Transaction] {
|
||||||
|
return pendingTransactions
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nettoie le mempool en retirant les transactions qui sont dans la chaîne
|
||||||
|
*/
|
||||||
|
func cleanupMempool(chain: [Block]) {
|
||||||
|
var remainingTransactions: [Transaction] = []
|
||||||
|
|
||||||
|
for pendingTx in pendingTransactions {
|
||||||
|
// On vérifie si la transaction est dans un des blocs
|
||||||
|
var isInChain = false
|
||||||
|
|
||||||
|
for block in chain {
|
||||||
|
if block.transactions.contains(where: { chainTx in
|
||||||
|
chainTx.sender == pendingTx.sender &&
|
||||||
|
chainTx.receiver == pendingTx.receiver &&
|
||||||
|
chainTx.amount == pendingTx.amount &&
|
||||||
|
chainTx.type == pendingTx.type
|
||||||
|
}) {
|
||||||
|
isInChain = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si la transaction n'est pas dans la chaîne, on la garde
|
||||||
|
if !isInChain {
|
||||||
|
remainingTransactions.append(pendingTx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mettre à jour les transactions en attente
|
||||||
|
print("Mempool cleanup: removed \(pendingTransactions.count - remainingTransactions.count) transactions")
|
||||||
|
pendingTransactions = remainingTransactions
|
||||||
|
}
|
||||||
|
}
|
||||||
534
SwiftChainCore/Models/Node.swift
Normal file
534
SwiftChainCore/Models/Node.swift
Normal file
@@ -0,0 +1,534 @@
|
|||||||
|
//
|
||||||
|
// Node.swift
|
||||||
|
// SwiftChain
|
||||||
|
//
|
||||||
|
// Created by Victor on 27/11/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Network
|
||||||
|
|
||||||
|
public class Node {
|
||||||
|
private let nodeId = UUID().uuidString // Identifiant unique pour chaque nœud
|
||||||
|
private var processedMessages = Set<String>() // Pour traquer les messages déjà traités
|
||||||
|
|
||||||
|
// Network properties
|
||||||
|
private var peers: [NWConnection] = []
|
||||||
|
private var port: NWEndpoint.Port
|
||||||
|
private var listener: NWListener?
|
||||||
|
private let queue = DispatchQueue(label: "com.blockchain.node")
|
||||||
|
|
||||||
|
// Blockchain components
|
||||||
|
private let blockchain: Blockchain
|
||||||
|
private let accountManager: AccountManager
|
||||||
|
private var pendingTransactions: [Transaction] = []
|
||||||
|
private let maxTransactionsPerBlock: Int = 10
|
||||||
|
|
||||||
|
public init(port: NWEndpoint.Port = 8333) {
|
||||||
|
self.port = port
|
||||||
|
self.accountManager = AccountManager()
|
||||||
|
self.blockchain = Blockchain()
|
||||||
|
setupListener()
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MessageType: String, Codable {
|
||||||
|
case transaction = "TX"
|
||||||
|
case blockchainRequest = "BC_REQ"
|
||||||
|
case blockchainResponse = "BC_RES"
|
||||||
|
case newBlock = "NEW_BLOCK"
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NetworkMessage: Codable {
|
||||||
|
let type: MessageType
|
||||||
|
let data: Data
|
||||||
|
let senderId: String // Identifiant du nœud émetteur
|
||||||
|
let messageId: String // Identifiant unique du message
|
||||||
|
|
||||||
|
init(type: MessageType, data: Data, senderId: String) {
|
||||||
|
self.type = type
|
||||||
|
self.data = data
|
||||||
|
self.senderId = senderId
|
||||||
|
self.messageId = UUID().uuidString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configuration du listener
|
||||||
|
private func setupListener() {
|
||||||
|
let parameters = NWParameters.tcp
|
||||||
|
do {
|
||||||
|
listener = try NWListener(using: parameters, on: port)
|
||||||
|
listener?.stateUpdateHandler = { [weak self] state in
|
||||||
|
switch state {
|
||||||
|
case .ready:
|
||||||
|
print("Node is listening on port \(self?.port.rawValue ?? 0)")
|
||||||
|
case .failed(let error):
|
||||||
|
print("Listener failed with error: \(error)")
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listener?.newConnectionHandler = { [weak self] connection in
|
||||||
|
self?.handleNewConnection(connection)
|
||||||
|
print("New incoming connection from: \(connection.endpoint)")
|
||||||
|
}
|
||||||
|
|
||||||
|
listener?.start(queue: queue)
|
||||||
|
} catch {
|
||||||
|
print("Failed to create listener: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func mineBlock(minerAddress: String) -> Block? {
|
||||||
|
guard let lastBlock = blockchain.chain.last else {
|
||||||
|
print("No existing blockchain")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utiliser la méthode locale au lieu de memPool
|
||||||
|
var transactions = getTransactionsForBlock()
|
||||||
|
let miningReward = Transaction(
|
||||||
|
sender: "SYSTEM",
|
||||||
|
receiver: minerAddress,
|
||||||
|
amount: 50,
|
||||||
|
type: "MINING_REWARD"
|
||||||
|
)
|
||||||
|
transactions.append(miningReward)
|
||||||
|
|
||||||
|
let newBlock = Block(
|
||||||
|
transactions: transactions,
|
||||||
|
previousHash: lastBlock.hash,
|
||||||
|
index: blockchain.chain.count,
|
||||||
|
difficulty: blockchain.calculateNewDifficulty()
|
||||||
|
)
|
||||||
|
|
||||||
|
newBlock.miner = minerAddress
|
||||||
|
newBlock.timestamp = Int(Date().timeIntervalSince1970)
|
||||||
|
let miningTime = newBlock.mineBlock()
|
||||||
|
|
||||||
|
if accountManager.processBlock(newBlock) {
|
||||||
|
blockchain.chain.append(newBlock)
|
||||||
|
broadcastBlock(newBlock)
|
||||||
|
|
||||||
|
// Nettoyer le mempool après avoir ajouté le bloc
|
||||||
|
cleanupMempool(chain: blockchain.chain)
|
||||||
|
|
||||||
|
print("""
|
||||||
|
Block \(newBlock.index) created:
|
||||||
|
Hash: \(newBlock.hash)
|
||||||
|
Previous hash: \(newBlock.previousHash)
|
||||||
|
Transactions: \(newBlock.transactions.count)
|
||||||
|
Mining time: \(String(format: "%.2f", miningTime)) seconds
|
||||||
|
""")
|
||||||
|
return newBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public interface
|
||||||
|
public func submitTransaction(_ transaction: Transaction) -> Bool {
|
||||||
|
if addTransaction(transaction) {
|
||||||
|
broadcastTransaction(transaction)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getBalance(_ address: String) -> Int {
|
||||||
|
return accountManager.getBalance(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getPendingTransactions() -> [Transaction] {
|
||||||
|
return pendingTransactions
|
||||||
|
}
|
||||||
|
|
||||||
|
public func isChainValid() -> Bool {
|
||||||
|
return blockchain.validateChain(blockchain.chain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connexion à un pair
|
||||||
|
public func connectToPeer(host: String, port: NWEndpoint.Port? = nil) {
|
||||||
|
let peerPort = port ?? self.port
|
||||||
|
let endpoint = NWEndpoint.hostPort(host: NWEndpoint.Host(host), port: peerPort)
|
||||||
|
let connection = NWConnection(to: endpoint, using: .tcp)
|
||||||
|
|
||||||
|
connection.stateUpdateHandler = { [weak self] state in
|
||||||
|
switch state {
|
||||||
|
case .ready:
|
||||||
|
print("Connected to peer: \(host)")
|
||||||
|
self?.peers.append(connection)
|
||||||
|
self?.startReceiving(connection)
|
||||||
|
// Demander la blockchain au pair
|
||||||
|
self?.requestBlockchain(from: connection)
|
||||||
|
case .failed(let error):
|
||||||
|
print("Connection failed: \(error)")
|
||||||
|
case .cancelled:
|
||||||
|
print("Connection cancelled: \(host)")
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.start(queue: queue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Méthode pour lister les pairs connectés
|
||||||
|
public func listPeers() {
|
||||||
|
if peers.isEmpty {
|
||||||
|
print("Aucun pair connecté")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
print("\nPairs connectés (\(peers.count)):")
|
||||||
|
for (index, peer) in peers.enumerated() {
|
||||||
|
print("\(index + 1). \(peer.endpoint)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Propagation d'une transaction
|
||||||
|
func broadcastTransaction(_ transaction: Transaction) {
|
||||||
|
do {
|
||||||
|
let transactionData = try JSONEncoder().encode(transaction)
|
||||||
|
let message = NetworkMessage(type: .transaction, data: transactionData, senderId: nodeId)
|
||||||
|
let messageData = try JSONEncoder().encode(message)
|
||||||
|
|
||||||
|
print("Broadcasting transaction to \(peers.count) peers")
|
||||||
|
|
||||||
|
for peer in peers {
|
||||||
|
peer.send(content: messageData, completion: .contentProcessed { error in
|
||||||
|
if let error = error {
|
||||||
|
print("Failed to send transaction: \(error)")
|
||||||
|
} else {
|
||||||
|
print("Transaction sent successfully to: \(peer.endpoint)")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
print("Failed to encode transaction: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Réception des messages
|
||||||
|
private func startReceiving(_ connection: NWConnection) {
|
||||||
|
connection.receive(minimumIncompleteLength: 1, maximumLength: 65536) { [weak self] content, _, isComplete, error in
|
||||||
|
if let data = content {
|
||||||
|
self?.handleReceivedData(data, from: connection)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let error = error {
|
||||||
|
print("Receive error: \(error)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isComplete, error == nil {
|
||||||
|
self?.startReceiving(connection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleReceivedData(_ data: Data, from connection: NWConnection) {
|
||||||
|
do {
|
||||||
|
print("Received data of size: \(data.count) bytes")
|
||||||
|
let message = try JSONDecoder().decode(NetworkMessage.self, from: data)
|
||||||
|
print("Message decoded successfully, type: \(message.type)")
|
||||||
|
|
||||||
|
// Vérifier si le message a déjà été traité
|
||||||
|
if processedMessages.contains(message.messageId) {
|
||||||
|
print("Message déjà traité, ignoré")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ajouter le message à la liste des messages traités
|
||||||
|
processedMessages.insert(message.messageId)
|
||||||
|
|
||||||
|
switch message.type {
|
||||||
|
case .transaction:
|
||||||
|
let transaction = try JSONDecoder().decode(Transaction.self, from: message.data)
|
||||||
|
print("Transaction decoded: \(transaction.sender) -> \(transaction.receiver): \(transaction.amount)")
|
||||||
|
if addTransaction(transaction) { // Utiliser la méthode locale
|
||||||
|
print("Transaction added to mempool successfully")
|
||||||
|
} else {
|
||||||
|
print("Failed to add transaction to mempool")
|
||||||
|
}
|
||||||
|
|
||||||
|
case .blockchainRequest:
|
||||||
|
// Envoyer notre blockchain au pair
|
||||||
|
sendBlockchain(to: connection)
|
||||||
|
|
||||||
|
case .blockchainResponse:
|
||||||
|
// Recevoir et traiter la blockchain
|
||||||
|
let receivedChain = try JSONDecoder().decode([Block].self, from: message.data)
|
||||||
|
handleReceivedBlockchain(receivedChain)
|
||||||
|
|
||||||
|
case .newBlock:
|
||||||
|
// Recevoir et traiter un nouveau bloc
|
||||||
|
let block = try JSONDecoder().decode(Block.self, from: message.data)
|
||||||
|
handleReceivedBlock(block)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
print("Failed to decode received data: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleNewConnection(_ connection: NWConnection) {
|
||||||
|
connection.start(queue: queue)
|
||||||
|
peers.append(connection)
|
||||||
|
startReceiving(connection)
|
||||||
|
print("New connection established with: \(connection.endpoint)")
|
||||||
|
|
||||||
|
// Envoyer notre blockchain au nouveau pair
|
||||||
|
sendBlockchain(to: connection)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func requestBlockchain(from peer: NWConnection) {
|
||||||
|
do {
|
||||||
|
let message = NetworkMessage(type: .blockchainRequest, data: Data(), senderId: nodeId)
|
||||||
|
let messageData = try JSONEncoder().encode(message)
|
||||||
|
|
||||||
|
peer.send(content: messageData, completion: .contentProcessed { error in
|
||||||
|
if let error = error {
|
||||||
|
print("Failed to request blockchain: \(error)")
|
||||||
|
} else {
|
||||||
|
print("Blockchain request sent to: \(peer.endpoint)")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
print("Failed to encode blockchain request: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func sendBlockchain(to peer: NWConnection) {
|
||||||
|
do {
|
||||||
|
let chainData = try JSONEncoder().encode(blockchain.chain)
|
||||||
|
let message = NetworkMessage(type: .blockchainResponse, data: chainData, senderId: nodeId)
|
||||||
|
let messageData = try JSONEncoder().encode(message)
|
||||||
|
|
||||||
|
peer.send(content: messageData, completion: .contentProcessed { error in
|
||||||
|
if let error = error {
|
||||||
|
print("Failed to send blockchain: \(error)")
|
||||||
|
} else {
|
||||||
|
print("Blockchain sent to: \(peer.endpoint)")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
print("Failed to encode blockchain: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleReceivedBlockchain(_ receivedChain: [Block]) {
|
||||||
|
print("Received blockchain with \(receivedChain.count) blocks")
|
||||||
|
|
||||||
|
if receivedChain.count > blockchain.chain.count {
|
||||||
|
print("Received chain is longer, validating...")
|
||||||
|
if blockchain.validateChain(receivedChain) {
|
||||||
|
// Sauvegarder l'état actuel
|
||||||
|
let oldChain = blockchain.chain
|
||||||
|
|
||||||
|
// Essayer d'appliquer la nouvelle chaîne
|
||||||
|
blockchain.chain = receivedChain
|
||||||
|
|
||||||
|
// Vérifier que toutes les transactions sont valides
|
||||||
|
var isValid = true
|
||||||
|
for block in receivedChain where !accountManager.processBlock(block) {
|
||||||
|
isValid = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if isValid {
|
||||||
|
print("Blockchain updated successfully")
|
||||||
|
cleanupMempool(chain: receivedChain)
|
||||||
|
} else {
|
||||||
|
blockchain.chain = oldChain
|
||||||
|
print("Failed to process transactions in received chain")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print("Received chain is invalid")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print("Current chain is longer or equal, keeping it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleReceivedBlock(_ block: Block) {
|
||||||
|
print("Received new block: \(block.hash)")
|
||||||
|
|
||||||
|
if blockchain.chain.contains(where: { $0.hash == block.hash }) {
|
||||||
|
print("Bloc déjà dans la chaîne")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier que le bloc suit bien notre dernier bloc
|
||||||
|
guard let lastBlock = blockchain.chain.last else {
|
||||||
|
print("No existing chain")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if block.previousHash != lastBlock.hash {
|
||||||
|
print("Le bloc ne se connecte pas à notre chaîne")
|
||||||
|
|
||||||
|
// Demander la chaîne complète aux pairs pour se synchroniser
|
||||||
|
for peer in peers {
|
||||||
|
requestBlockchain(from: peer)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !block.isValid() {
|
||||||
|
print("Block is not valid")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traiter les transactions du bloc
|
||||||
|
if !accountManager.processBlock(block) {
|
||||||
|
print("Failed to process block transactions")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nettoyer le mempool des transactions incluses dans le bloc
|
||||||
|
cleanupMempool(chain: [block])
|
||||||
|
|
||||||
|
// Ajouter le bloc à la chaîne
|
||||||
|
blockchain.chain.append(block)
|
||||||
|
print("New block added to chain")
|
||||||
|
|
||||||
|
// Propager le bloc aux autres pairs (sauf celui qui nous l'a envoyé)
|
||||||
|
broadcastBlock(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func broadcastBlock(_ block: Block) {
|
||||||
|
do {
|
||||||
|
let blockData = try JSONEncoder().encode(block)
|
||||||
|
let message = NetworkMessage(type: .newBlock, data: blockData, senderId: nodeId)
|
||||||
|
let messageData = try JSONEncoder().encode(message)
|
||||||
|
|
||||||
|
print("Broadcasting block to peers")
|
||||||
|
|
||||||
|
for peer in peers {
|
||||||
|
peer.send(content: messageData, completion: .contentProcessed { error in
|
||||||
|
if let error = error {
|
||||||
|
print("Failed to send block: \(error)")
|
||||||
|
} else {
|
||||||
|
print("Block sent successfully to: \(peer.endpoint)")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
print("Failed to encode block: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Mempool methods
|
||||||
|
|
||||||
|
extension Node {
|
||||||
|
// MemPool functionality
|
||||||
|
private func addTransaction(_ transaction: Transaction) -> Bool {
|
||||||
|
if validateTransaction(transaction) {
|
||||||
|
pendingTransactions.append(transaction)
|
||||||
|
print("Transaction added to mempool successfully")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getTransactionsForBlock() -> [Transaction] {
|
||||||
|
var validTransactions: [Transaction] = []
|
||||||
|
var remainingTransactions: [Transaction] = []
|
||||||
|
|
||||||
|
for transaction in pendingTransactions {
|
||||||
|
if validTransactions.count >= maxTransactionsPerBlock {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if validateTransaction(transaction) {
|
||||||
|
validTransactions.append(transaction)
|
||||||
|
} else {
|
||||||
|
remainingTransactions.append(transaction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update pending transactions
|
||||||
|
pendingTransactions = remainingTransactions + pendingTransactions.dropFirst(validTransactions.count)
|
||||||
|
|
||||||
|
return validTransactions
|
||||||
|
}
|
||||||
|
|
||||||
|
private func cleanupMempool(chain: [Block]) {
|
||||||
|
var remainingTransactions: [Transaction] = []
|
||||||
|
|
||||||
|
for pendingTx in pendingTransactions {
|
||||||
|
// On vérifie si la transaction est dans un des blocs
|
||||||
|
var isInChain = false
|
||||||
|
|
||||||
|
for block in chain {
|
||||||
|
if block.transactions.contains(where: { chainTx in
|
||||||
|
chainTx.sender == pendingTx.sender &&
|
||||||
|
chainTx.receiver == pendingTx.receiver &&
|
||||||
|
chainTx.amount == pendingTx.amount &&
|
||||||
|
chainTx.type == pendingTx.type
|
||||||
|
}) {
|
||||||
|
isInChain = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isInChain {
|
||||||
|
remainingTransactions.append(pendingTx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Mempool cleanup: removed \(pendingTransactions.count - remainingTransactions.count) transactions")
|
||||||
|
pendingTransactions = remainingTransactions
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasPendingTransactions: Bool {
|
||||||
|
return !pendingTransactions.isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
public func printMemPoolStatus() {
|
||||||
|
print("""
|
||||||
|
MemPool Status:
|
||||||
|
- Pending transactions: \(pendingTransactions.count)
|
||||||
|
- Maximum transactions per block: \(maxTransactionsPerBlock)
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func checkDoubleSpending(_ transaction: Transaction) -> Bool {
|
||||||
|
let address = transaction.sender
|
||||||
|
let pendingAmount = pendingTransactions
|
||||||
|
.filter { $0.sender == address }
|
||||||
|
.reduce(0) { $0 + $1.amount }
|
||||||
|
|
||||||
|
let currentBalance = accountManager.getBalance(address)
|
||||||
|
|
||||||
|
if currentBalance < (pendingAmount + transaction.amount) {
|
||||||
|
print("MemPool: Transaction refusée - tentative de double dépense détectée")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private func validateTransaction(_ transaction: Transaction) -> Bool {
|
||||||
|
if !transaction.isValid() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if transaction.type == "MINING_REWARD" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !checkDoubleSpending(transaction) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !accountManager.canProcessTransaction(transaction) {
|
||||||
|
print("MemPool: Transaction refusée - solde insuffisant")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
print("MemPool: Transaction validée avec succès")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
96
SwiftChainCore/Models/Transaction.swift
Normal file
96
SwiftChainCore/Models/Transaction.swift
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
//
|
||||||
|
// File.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Victor BODINAUD on 31/03/2021.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public class Transaction: Codable {
|
||||||
|
public let sender: String
|
||||||
|
public let receiver: String
|
||||||
|
public let amount: Int
|
||||||
|
public let type: String
|
||||||
|
public var signature: Data?
|
||||||
|
public var senderPublicKey: Data?
|
||||||
|
|
||||||
|
public init(sender: String, receiver: String, amount: Int, type: String) {
|
||||||
|
self.sender = sender
|
||||||
|
self.receiver = receiver
|
||||||
|
self.amount = amount
|
||||||
|
self.type = type
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pour encoder/décoder les Data optionnels
|
||||||
|
enum CodingKeys: String, CodingKey {
|
||||||
|
case sender, receiver, amount, type, signature, senderPublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func messageToSign() -> Data {
|
||||||
|
return "\(sender)\(receiver)\(amount)\(type)".data(using: .utf8)!
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSignatureValid() -> Bool {
|
||||||
|
guard let signature = signature,
|
||||||
|
let publicKeyData = senderPublicKey
|
||||||
|
else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if type == "MINING_REWARD" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return Wallet.verifySignature(
|
||||||
|
for: self,
|
||||||
|
signature: signature,
|
||||||
|
publicKeyData: publicKeyData
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValid() -> Bool {
|
||||||
|
// Vérifications de base
|
||||||
|
if amount <= 0 {
|
||||||
|
print("Transaction: Amount must be positive")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if sender.isEmpty || receiver.isEmpty {
|
||||||
|
print("Transaction: Invalid addresses")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérification du type
|
||||||
|
if !["TRANSFER", "MINING_REWARD"].contains(type) {
|
||||||
|
print("Transaction: Invalid type")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifications spécifiques selon le type
|
||||||
|
if type == "MINING_REWARD" {
|
||||||
|
if sender != "SYSTEM" {
|
||||||
|
print("Transaction: Mining reward must come from SYSTEM")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if amount != 50 { // La récompense doit être exactement 50
|
||||||
|
print("Transaction: Invalid mining reward amount")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true // Pas besoin de vérifier la signature pour les récompenses
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pour les transactions normales
|
||||||
|
if !isSignatureValid() {
|
||||||
|
print("Transaction: Invalid signature")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if sender == receiver {
|
||||||
|
print("Transaction: Sender cannot be receiver")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
47
SwiftChainCore/Models/Wallet.swift
Normal file
47
SwiftChainCore/Models/Wallet.swift
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
//
|
||||||
|
// Wallet.swift
|
||||||
|
// SwiftChain
|
||||||
|
//
|
||||||
|
// Created by Victor on 27/11/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import CryptoKit
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public class Wallet {
|
||||||
|
private let privateKey: Curve25519.Signing.PrivateKey
|
||||||
|
let publicKey: Curve25519.Signing.PublicKey
|
||||||
|
public let address: String
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
// Générer une nouvelle paire de clés
|
||||||
|
privateKey = Curve25519.Signing.PrivateKey()
|
||||||
|
publicKey = privateKey.publicKey
|
||||||
|
|
||||||
|
// Créer une adresse au format swift_(hash)
|
||||||
|
let pubKeyData = publicKey.rawRepresentation
|
||||||
|
let hash = SHA256.hash(data: pubKeyData)
|
||||||
|
let hashString = hash.compactMap { String(format: "%02x", $0) }.joined()
|
||||||
|
address = "swift_" + hashString.prefix(40)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signer une transaction
|
||||||
|
public func signTransaction(_ transaction: Transaction) -> Data? {
|
||||||
|
let messageData = transaction.messageToSign()
|
||||||
|
return try? privateKey.signature(for: messageData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier une signature
|
||||||
|
static func verifySignature(for transaction: Transaction, signature: Data, publicKeyData: Data) -> Bool {
|
||||||
|
guard let publicKey = try? Curve25519.Signing.PublicKey(rawRepresentation: publicKeyData) else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return publicKey.isValidSignature(signature, for: transaction.messageToSign())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtenir la clé publique en format Data
|
||||||
|
public func getPublicKeyData() -> Data {
|
||||||
|
return publicKey.rawRepresentation
|
||||||
|
}
|
||||||
|
}
|
||||||
13
SwiftChainCore/SwiftChainCore.docc/SwiftChainCore.md
Normal file
13
SwiftChainCore/SwiftChainCore.docc/SwiftChainCore.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# ``SwiftChainCore``
|
||||||
|
|
||||||
|
<!--@START_MENU_TOKEN@-->Summary<!--@END_MENU_TOKEN@-->
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
<!--@START_MENU_TOKEN@-->Text<!--@END_MENU_TOKEN@-->
|
||||||
|
|
||||||
|
## Topics
|
||||||
|
|
||||||
|
### <!--@START_MENU_TOKEN@-->Group<!--@END_MENU_TOKEN@-->
|
||||||
|
|
||||||
|
- <!--@START_MENU_TOKEN@-->``Symbol``<!--@END_MENU_TOKEN@-->
|
||||||
18
SwiftChainCore/SwiftChainCore.h
Normal file
18
SwiftChainCore/SwiftChainCore.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
//
|
||||||
|
// SwiftChainCore.h
|
||||||
|
// SwiftChainCore
|
||||||
|
//
|
||||||
|
// Created by Victor on 02/12/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
//! Project version number for SwiftChainCore.
|
||||||
|
FOUNDATION_EXPORT double SwiftChainCoreVersionNumber;
|
||||||
|
|
||||||
|
//! Project version string for SwiftChainCore.
|
||||||
|
FOUNDATION_EXPORT const unsigned char SwiftChainCoreVersionString[];
|
||||||
|
|
||||||
|
// In this header, you should import all the public headers of your framework using statements like #import <SwiftChainCore/PublicHeader.h>
|
||||||
|
|
||||||
|
|
||||||
36
SwiftChainCoreTests/SwiftChainCoreTests.swift
Normal file
36
SwiftChainCoreTests/SwiftChainCoreTests.swift
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
//
|
||||||
|
// SwiftChainCoreTests.swift
|
||||||
|
// SwiftChainCoreTests
|
||||||
|
//
|
||||||
|
// Created by Victor on 02/12/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import SwiftChainCore
|
||||||
|
|
||||||
|
final class SwiftChainCoreTests: XCTestCase {
|
||||||
|
|
||||||
|
override func setUpWithError() throws {
|
||||||
|
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDownWithError() throws {
|
||||||
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||||
|
}
|
||||||
|
|
||||||
|
func testExample() throws {
|
||||||
|
// This is an example of a functional test case.
|
||||||
|
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||||
|
// Any test you write for XCTest can be annotated as throws and async.
|
||||||
|
// Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
|
||||||
|
// Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPerformanceExample() throws {
|
||||||
|
// This is an example of a performance test case.
|
||||||
|
self.measure {
|
||||||
|
// Put the code you want to measure the time of here.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user