├── BitcoinJKit ├── en.lproj │ └── InfoPlist.strings ├── HIBitcoinInternalErrorCodes.m ├── BitcoinJKit.h ├── BitcoinJKit-Prefix.pch ├── HIBitcoinErrorCodes.m ├── HIBitcoinInternalErrorCodes.h ├── java │ ├── src │ │ └── main │ │ │ └── java │ │ │ ├── com │ │ │ └── hive │ │ │ │ └── bitcoinkit │ │ │ │ ├── NoWalletException.java │ │ │ │ ├── WrongPasswordException.java │ │ │ │ ├── ExistingWalletException.java │ │ │ │ ├── LastWalletChangeExtension.java │ │ │ │ └── BitcoinManager.java │ │ │ └── org │ │ │ └── slf4j │ │ │ └── impl │ │ │ ├── CocoaLoggerFactory.java │ │ │ ├── StaticLoggerBinder.java │ │ │ └── CocoaLogger.java │ └── pom.xml ├── HIBitcoinErrorCodes.h ├── BitcoinJKit-Info.plist ├── HIBitcoinManager.h └── HIBitcoinManager.m ├── DemoApp ├── HIBitcoinJKitDemo │ ├── en.lproj │ │ ├── InfoPlist.strings │ │ └── Credits.rtf │ ├── HIBitcoinJKitDemo-Prefix.pch │ ├── main.m │ ├── HISendWindowController.h │ ├── HIAppDelegate.h │ ├── HIBitcoinJKitDemo-Info.plist │ ├── HISendWindowController.m │ ├── HIAppDelegate.m │ └── HISendWindowController.xib └── HIBitcoinJKitDemo.xcodeproj │ └── project.pbxproj ├── .gitmodules ├── .gitignore ├── LICENSE ├── README.md └── BitcoinJKit.xcodeproj └── project.pbxproj /BitcoinJKit/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /DemoApp/HIBitcoinJKitDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /BitcoinJKit/HIBitcoinInternalErrorCodes.m: -------------------------------------------------------------------------------- 1 | #import "HIBitcoinInternalErrorCodes.h" 2 | 3 | NSInteger const kHIBitcoinManagerUnexpectedError = 0; 4 | NSInteger const kHIIllegalArgumentException = -1000; 5 | -------------------------------------------------------------------------------- /DemoApp/HIBitcoinJKitDemo/HIBitcoinJKitDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'HIBitcoinJKitDemo' target in the 'HIBitcoinJKitDemo' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /BitcoinJKit/BitcoinJKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // BitcoinJKit.h 3 | // BitcoinJKit 4 | // 5 | // Created by Bazyli Zygan on 26.07.2013. 6 | // Copyright (c) 2013 Hive Developers. All rights reserved. 7 | // 8 | 9 | #import 10 | #import -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "bitcoind"] 2 | path = bitcoind 3 | url = https://github.com/bitcoin/bitcoin.git 4 | [submodule "avian"] 5 | path = avian 6 | url = https://github.com/ReadyTalk/avian.git 7 | [submodule "bitcoinj"] 8 | path = bitcoinj 9 | url = https://code.google.com/p/bitcoinj/ 10 | -------------------------------------------------------------------------------- /DemoApp/HIBitcoinJKitDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // HIBitcoinKitDemo 4 | // 5 | // Created by Bazyli Zygan on 12.07.2013. 6 | // Copyright (c) 2013 Hive Developers. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | return NSApplicationMain(argc, (const char **)argv); 14 | } 15 | -------------------------------------------------------------------------------- /BitcoinJKit/BitcoinJKit-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'BitcoinJKit' target in the 'BitcoinJKit' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | 9 | #define kBitcoinKitFormatPreferenceKey @"BitcoinKitFormatPreferenceKey" 10 | #define kBitcoinKitFormatChangeNotification @"BitcoinKitFormatChangeNotification" -------------------------------------------------------------------------------- /BitcoinJKit/HIBitcoinErrorCodes.m: -------------------------------------------------------------------------------- 1 | #import "HIBitcoinErrorCodes.h" 2 | 3 | NSInteger const kHIBitcoinManagerUnreadableWallet = 1000; 4 | NSInteger const kHIBitcoinManagerBlockStoreError = 1001; 5 | NSInteger const kHIBitcoinManagerNoWallet = 1002; 6 | NSInteger const kHIBitcoinManagerWalletExists = 1003; 7 | NSInteger const kHIBitcoinManagerWrongPassword = 1004; 8 | NSInteger const kHIBitcoinManagerInsufficientMoney = 1005; -------------------------------------------------------------------------------- /BitcoinJKit/HIBitcoinInternalErrorCodes.h: -------------------------------------------------------------------------------- 1 | // 2 | // HIBitcoinInternalErrorCodes.h 3 | // Hive 4 | // 5 | // Created by Nikolaj Schumacher on 30.11.2013. 6 | // Copyright (c) 2013 Hive Developers. All rights reserved. 7 | // 8 | 9 | /* Unknown error: This should have been mapped to an error code. */ 10 | extern NSInteger const kHIBitcoinManagerUnexpectedError; 11 | 12 | extern NSInteger const kHIIllegalArgumentException; 13 | -------------------------------------------------------------------------------- /BitcoinJKit/java/src/main/java/com/hive/bitcoinkit/NoWalletException.java: -------------------------------------------------------------------------------- 1 | package com.hive.bitcoinkit; 2 | 3 | public class NoWalletException extends Exception 4 | { 5 | public NoWalletException(String message) 6 | { 7 | super(message); 8 | } 9 | 10 | public NoWalletException(String message, Throwable cause) 11 | { 12 | super(message, cause); 13 | } 14 | 15 | public NoWalletException(Throwable cause) 16 | { 17 | super(cause); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /BitcoinJKit/java/src/main/java/com/hive/bitcoinkit/WrongPasswordException.java: -------------------------------------------------------------------------------- 1 | package com.hive.bitcoinkit; 2 | 3 | public class WrongPasswordException extends Exception 4 | { 5 | public WrongPasswordException(String message) 6 | { 7 | super(message); 8 | } 9 | 10 | public WrongPasswordException(String message, Throwable cause) 11 | { 12 | super(message, cause); 13 | } 14 | 15 | public WrongPasswordException(Throwable cause) 16 | { 17 | super(cause); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /BitcoinJKit/java/src/main/java/com/hive/bitcoinkit/ExistingWalletException.java: -------------------------------------------------------------------------------- 1 | package com.hive.bitcoinkit; 2 | 3 | public class ExistingWalletException extends Exception 4 | { 5 | public ExistingWalletException(String message) 6 | { 7 | super(message); 8 | } 9 | 10 | public ExistingWalletException(String message, Throwable cause) 11 | { 12 | super(message, cause); 13 | } 14 | 15 | public ExistingWalletException(Throwable cause) 16 | { 17 | super(cause); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /DemoApp/HIBitcoinJKitDemo/HISendWindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HISendWindowController.h 3 | // HIBitcoinKitDemo 4 | // 5 | // Created by Bazyli Zygan on 14.07.2013. 6 | // Copyright (c) 2013 Hive Developers. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HISendWindowController : NSWindowController 12 | @property (weak) IBOutlet NSTextField *addressField; 13 | @property (weak) IBOutlet NSTextField *amountField; 14 | 15 | - (IBAction)cancelClicked:(NSButton *)sender; 16 | - (IBAction)sendClicked:(NSButton *)sender; 17 | @end 18 | -------------------------------------------------------------------------------- /DemoApp/HIBitcoinJKitDemo/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;} 2 | {\colortbl;\red255\green255\blue255;} 3 | \paperw9840\paperh8400 4 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural 5 | 6 | \f0\b\fs24 \cf0 Engineering: 7 | \b0 \ 8 | Some people\ 9 | \ 10 | 11 | \b Human Interface Design: 12 | \b0 \ 13 | Some other people\ 14 | \ 15 | 16 | \b Testing: 17 | \b0 \ 18 | Hopefully not nobody\ 19 | \ 20 | 21 | \b Documentation: 22 | \b0 \ 23 | Whoever\ 24 | \ 25 | 26 | \b With special thanks to: 27 | \b0 \ 28 | Mom\ 29 | } 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | build/* 3 | *.pbxuser 4 | !default.pbxuser 5 | *.mode1v3 6 | !default.mode1v3 7 | *.mode2v3 8 | !default.mode2v3 9 | *.perspectivev3 10 | !default.perspectivev3 11 | *.xcworkspace 12 | !default.xcworkspace 13 | xcuserdata 14 | profile 15 | *.moved-aside 16 | 17 | .DS_Store 18 | 19 | # Thumbnails 20 | ._* 21 | 22 | # Files that might appear on external disk 23 | .Spotlight-V100 24 | .Trashes 25 | 26 | # SourceTree (Mac) Configuration Files 27 | sourcetreeconfig 28 | 29 | # bitcoinj/aviary compilation temps 30 | boot-jar.o 31 | boot.jar 32 | tmp 33 | *core* 34 | *tmp* 35 | *target* 36 | classpath.jar 37 | 38 | # Podfiles 39 | Podfile.lock 40 | Pods/* 41 | -------------------------------------------------------------------------------- /BitcoinJKit/java/src/main/java/org/slf4j/impl/CocoaLoggerFactory.java: -------------------------------------------------------------------------------- 1 | package org.slf4j.impl; 2 | 3 | import org.slf4j.ILoggerFactory; 4 | import org.slf4j.Logger; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | // based on http://javaeenotes.blogspot.com/2011/12/custom-slf4j-logger-adapter.html 10 | 11 | public class CocoaLoggerFactory implements ILoggerFactory 12 | { 13 | private Map loggerMap; 14 | 15 | public CocoaLoggerFactory() 16 | { 17 | loggerMap = new HashMap(); 18 | } 19 | 20 | @Override 21 | public Logger getLogger(String name) 22 | { 23 | synchronized (loggerMap) 24 | { 25 | if (!loggerMap.containsKey(name)) 26 | { 27 | loggerMap.put(name, new CocoaLogger(name)); 28 | } 29 | 30 | return loggerMap.get(name); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /BitcoinJKit/HIBitcoinErrorCodes.h: -------------------------------------------------------------------------------- 1 | // 2 | // HIBitcoinErrorCodes.h 3 | // Hive 4 | // 5 | // Created by Nikolaj Schumacher on 30.11.2013. 6 | // Copyright (c) 2013 Hive Developers. All rights reserved. 7 | // 8 | 9 | /* The wallet file exists but could not be read. */ 10 | extern NSInteger const kHIBitcoinManagerUnreadableWallet; 11 | 12 | /* Storing a block failed (e.g. because the file is locked or the volume is full). */ 13 | extern NSInteger const kHIBitcoinManagerBlockStoreError; 14 | 15 | /* There is no wallet and it needs to be created. */ 16 | extern NSInteger const kHIBitcoinManagerNoWallet; 17 | 18 | /* A wallet could not be created because it already exists. */ 19 | extern NSInteger const kHIBitcoinManagerWalletExists; 20 | 21 | /* An operation could not be completed because a wrong password was specified. */ 22 | extern NSInteger const kHIBitcoinManagerWrongPassword; 23 | 24 | /* An operation could not be completed because not enough money was there. */ 25 | extern NSInteger const kHIBitcoinManagerInsufficientMoney; 26 | -------------------------------------------------------------------------------- /DemoApp/HIBitcoinJKitDemo/HIAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // HIAppDelegate.h 3 | // HIBitcoinKitDemo 4 | // 5 | // Created by Bazyli Zygan on 12.07.2013. 6 | // Copyright (c) 2013 Hive Developers. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HIAppDelegate : NSObject 12 | 13 | @property (assign) IBOutlet NSWindow *window; 14 | @property (weak) IBOutlet NSTextField *addressLabel; 15 | @property (weak) IBOutlet NSTextField *balanceLabel; 16 | @property (weak) IBOutlet NSTextField *connectionsLabel; 17 | @property (weak) IBOutlet NSProgressIndicator *progressIndicator; 18 | @property (weak) IBOutlet NSTextField *stateLabel; 19 | @property (weak) IBOutlet NSTableView *transactionList; 20 | @property (weak) IBOutlet NSButton *sendMoneyBtn; 21 | @property (weak) IBOutlet NSButton *importBtn; 22 | @property (weak) IBOutlet NSButton *exportBtn; 23 | - (IBAction)sendMoneyClicked:(NSButton *)sender; 24 | - (IBAction)exportWalletClicked:(NSButton *)sender; 25 | - (IBAction)importWalletClicked:(NSButton *)sender; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Hive Developers (http://www.grabhive.com/) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /BitcoinJKit/BitcoinJKit-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.hive.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | NSHumanReadableCopyright 26 | Copyright © 2013 Hive Developers. All rights reserved. 27 | NSJavaNeeded 28 | 29 | NSJavaPath 30 | 31 | booj.jar 32 | 33 | NSJavaRoot 34 | Content/Resources/ 35 | NSPrincipalClass 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /DemoApp/HIBitcoinJKitDemo/HIBitcoinJKitDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.hive.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | ${MACOSX_DEPLOYMENT_TARGET} 27 | NSHumanReadableCopyright 28 | Copyright © 2013 Hive Developers. All rights reserved. 29 | NSJavaNeeded 30 | 31 | NSMainNibFile 32 | MainMenu 33 | NSPrincipalClass 34 | NSApplication 35 | 36 | 37 | -------------------------------------------------------------------------------- /BitcoinJKit/java/src/main/java/org/slf4j/impl/StaticLoggerBinder.java: -------------------------------------------------------------------------------- 1 | package org.slf4j.impl; 2 | 3 | import org.slf4j.ILoggerFactory; 4 | import org.slf4j.spi.LoggerFactoryBinder; 5 | 6 | // based on http://javaeenotes.blogspot.com/2011/12/custom-slf4j-logger-adapter.html 7 | 8 | public class StaticLoggerBinder implements LoggerFactoryBinder 9 | { 10 | 11 | private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder(); 12 | 13 | public static final StaticLoggerBinder getSingleton() 14 | { 15 | return SINGLETON; 16 | } 17 | 18 | /** 19 | * Declare the version of the SLF4J API this implementation is 20 | * compiled against. The value of this field is usually modified 21 | * with each release. 22 | */ 23 | // To avoid constant folding by the compiler, 24 | // this field must *not* be final 25 | public static String REQUESTED_API_VERSION = "1.6.4"; // !final 26 | 27 | private static final String loggerFactoryClassStr = CocoaLoggerFactory.class.getName(); 28 | 29 | /** 30 | * The ILoggerFactory instance returned by the 31 | * {@link #getLoggerFactory} method should always be the same 32 | * object. 33 | */ 34 | private final ILoggerFactory loggerFactory; 35 | 36 | private StaticLoggerBinder() 37 | { 38 | loggerFactory = new CocoaLoggerFactory(); 39 | } 40 | 41 | public ILoggerFactory getLoggerFactory() 42 | { 43 | return loggerFactory; 44 | } 45 | 46 | public String getLoggerFactoryClassStr() 47 | { 48 | return loggerFactoryClassStr; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | BitcoinJKit.framework 2 | =================== 3 | 4 | BitcoinJKit.framework allows you to access and use bitcoinj wallets in your applications. It uses BitcoinJ. 5 | 6 | About BitcoinJKit.framework 7 | --------------------------- 8 | 9 | The BitcoinJKit.framework uses bitcoinj sources to deliver bitcoin functionality with SPV. Your application will need to request for java support because - up till now - BitcoinJKit.framework requires external JVM. That may change in the future. 10 | 11 | 12 | Build Instructions for BitcoinJKit.framework 13 | ------------------------------------------- 14 | 15 | For that you need to have java and maven installed 16 | 17 | brew install maven 18 | 19 | And you also have to remember to fetch all submodules! 20 | 21 | git submodule update --init --recursive 22 | 23 | Time to compile! 24 | 25 | How to use 26 | ---------- 27 | 28 | BitcoinJKit.framework delivers a singleton of class HIBitcoinManager. With this object you are able to access bitcoin network and manage your wallet 29 | 30 | First you need to prepare the library for launching. 31 | 32 | Set up where wallet and bitcoin network data should be kept 33 | 34 | ```objective-c 35 | [HIBitcoinManager defaultManager].dataURL = [[self applicationSupportDir] URLByAppendingPathComponent:@"com.mycompany.MyBitcoinWalletData"]; 36 | ``` 37 | 38 | Decide if you want to use a testing network (or not) 39 | 40 | ```objective-c 41 | [HIBitcoinManager defaultManager].testingNetwork = YES; 42 | ``` 43 | 44 | ...and start the network! 45 | 46 | ```objective-c 47 | [[HIBitcoinManager defaultManager] start]; 48 | ``` 49 | 50 | Now you can easily get the balance or wallet address: 51 | 52 | ```objective-c 53 | NSString *walletAddress [HIBitcoinManager defaultManager].walletAddress; 54 | uint64_t balance = [HIBitcoinManager defaultManager].balance 55 | ``` 56 | 57 | You can send coins 58 | 59 | ```objective-c 60 | [[HIBitcoinManager defaultManager] sendCoins:1000 toReceipent:receipentHashAddress comment:@"Here's some money for you!" completion:nil]; 61 | ``` 62 | 63 | And more! 64 | 65 | Demo App 66 | -------- 67 | 68 | There's a demo application included with the sources. Start it up and check out how to use BitcoinJKit.framework! 69 | 70 | License 71 | ------- 72 | 73 | BitcoinJKit.framework are available under the MIT license. -------------------------------------------------------------------------------- /BitcoinJKit/java/src/main/java/com/hive/bitcoinkit/LastWalletChangeExtension.java: -------------------------------------------------------------------------------- 1 | package com.hive.bitcoinkit; 2 | 3 | import com.google.bitcoin.core.Wallet; 4 | import com.google.bitcoin.core.WalletExtension; 5 | import java.nio.ByteBuffer; 6 | import java.util.Date; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | public class LastWalletChangeExtension implements WalletExtension 11 | { 12 | static final String EXTENSION_ID = LastWalletChangeExtension.class.getName(); 13 | private static final Logger log = LoggerFactory.getLogger(LastWalletChangeExtension.class); 14 | 15 | private Date lastWalletChangeDate; 16 | 17 | public LastWalletChangeExtension() 18 | { 19 | } 20 | 21 | public Date getLastWalletChangeDate() 22 | { 23 | return lastWalletChangeDate; 24 | } 25 | 26 | public void setLastWalletChangeDate(Date date) 27 | { 28 | lastWalletChangeDate = date; 29 | } 30 | 31 | 32 | /** Returns a Java package/class style name used to disambiguate this extension from others. */ 33 | @Override 34 | public String getWalletExtensionID() 35 | { 36 | return EXTENSION_ID; 37 | } 38 | 39 | /** 40 | * If this returns true, the mandatory flag is set when the wallet is serialized and attempts to load it without 41 | * the extension being in the wallet will throw an exception. This method should not change its result during 42 | * the objects lifetime. 43 | */ 44 | @Override 45 | public boolean isWalletExtensionMandatory() 46 | { 47 | return false; 48 | } 49 | 50 | /** Returns bytes that will be saved in the wallet. */ 51 | @Override 52 | public byte[] serializeWalletExtension() 53 | { 54 | long timestamp = (lastWalletChangeDate != null) ? lastWalletChangeDate.getTime() : 0; 55 | return ByteBuffer.allocate(8).putLong(timestamp).array(); 56 | } 57 | 58 | /** Loads the contents of this object from the wallet. */ 59 | @Override 60 | public void deserializeWalletExtension(Wallet containingWallet, byte[] data) throws Exception 61 | { 62 | ByteBuffer buffer = ByteBuffer.allocate(8); 63 | buffer.put(data).flip(); 64 | long timestamp = buffer.getLong(); 65 | lastWalletChangeDate = (timestamp > 0) ? new Date(timestamp) : null; 66 | } 67 | 68 | @Override 69 | public String toString() { 70 | return "LastWalletChangeExtension: date = " + lastWalletChangeDate; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /BitcoinJKit/java/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | com.google 8 | bitcoinj-parent 9 | 0.11 10 | ../../bitcoinj/pom.xml 11 | 12 | 13 | 4.0.0 14 | BitcoinJKit 15 | org.BitcoinJKit 16 | BitcoinJKit Java bridge 17 | 0.11 18 | BitcoinJKit Java bridge. 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-shade-plugin 25 | 1.6 26 | 27 | 28 | false 29 | 30 | 31 | 32 | *:* 33 | 34 | META-INF/*.SF 35 | META-INF/*.DSA 36 | META-INF/*.RSA 37 | 38 | 39 | 40 | 41 | 42 | com.hive.bitcoinkit.BitcoinManager 43 | 44 | 45 | 46 | 47 | 48 | package 49 | 50 | shade 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | com.google 61 | bitcoinj 62 | 0.11 63 | 64 | 65 | -------------------------------------------------------------------------------- /DemoApp/HIBitcoinJKitDemo/HISendWindowController.m: -------------------------------------------------------------------------------- 1 | // 2 | // HISendWindowController.m 3 | // HIBitcoinKitDemo 4 | // 5 | // Created by Bazyli Zygan on 14.07.2013. 6 | // Copyright (c) 2013 Hive Developers. All rights reserved. 7 | // 8 | #import 9 | #import "HISendWindowController.h" 10 | 11 | 12 | @interface HISendWindowController () 13 | 14 | @end 15 | 16 | @implementation HISendWindowController 17 | 18 | - (id)initWithWindow:(NSWindow *)window 19 | { 20 | self = [super initWithWindow:window]; 21 | if (self) { 22 | // Initialization code here. 23 | } 24 | 25 | return self; 26 | } 27 | 28 | - (void)windowDidLoad 29 | { 30 | [super windowDidLoad]; 31 | 32 | // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. 33 | } 34 | 35 | - (IBAction)cancelClicked:(NSButton *)sender 36 | { 37 | [NSApp endSheet:self.window]; 38 | [self.window close]; 39 | } 40 | 41 | - (IBAction)sendClicked:(NSButton *)sender 42 | { 43 | // Sanity check first 44 | NSString *address = _addressField.stringValue; 45 | CGFloat amount = [_amountField.stringValue floatValue]; 46 | NSLog(@"Balance %llu and amount %llu", [[HIBitcoinManager defaultManager] balance], (long long)(amount * 100000000)); 47 | if (amount <= 0 || 48 | [[HIBitcoinManager defaultManager] balance] < (long long)(amount * 100000000) || 49 | ![[HIBitcoinManager defaultManager] isAddressValid:address]) 50 | { 51 | NSAlert *alert = [[NSAlert alloc] init]; 52 | [alert setMessageText:@"Cannot send money"]; 53 | if (amount <= 0) 54 | [alert setInformativeText:@"Your amount is invalid"]; 55 | else if ([[HIBitcoinManager defaultManager] balance] < (long long)(amount * 100000000)) 56 | [alert setInformativeText:@"You can't send more than you own"]; 57 | else if (![[HIBitcoinManager defaultManager] isAddressValid:address]) 58 | [alert setInformativeText:@"Given receipent address is invalid"]; 59 | [alert addButtonWithTitle:@"Ok"]; 60 | 61 | [alert runModal]; 62 | } 63 | else 64 | { 65 | [[HIBitcoinManager defaultManager] sendCoins:(amount * 100000000) toReceipent:address comment:nil completion:^(NSString *hash) 66 | { 67 | if (hash) 68 | { 69 | [self cancelClicked:sender]; 70 | } 71 | else 72 | { 73 | NSAlert *alert = [[NSAlert alloc] init]; 74 | [alert setMessageText:@"Cannot send money"]; 75 | [alert setInformativeText:@"Failed to send money"]; 76 | [alert addButtonWithTitle:@"Ok"]; 77 | [alert runModal]; 78 | } 79 | }]; 80 | } 81 | } 82 | @end 83 | -------------------------------------------------------------------------------- /DemoApp/HIBitcoinJKitDemo/HIAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // HIAppDelegate.m 3 | // HIBitcoinKitDemo 4 | // 5 | // Created by Bazyli Zygan on 12.07.2013. 6 | // Copyright (c) 2013 Hive Developers. All rights reserved. 7 | // 8 | 9 | 10 | #import 11 | #import "HIAppDelegate.h" 12 | #import "HISendWindowController.h" 13 | 14 | @interface HIAppDelegate () 15 | { 16 | NSArray *_transactions; 17 | NSDateFormatter *_dF; 18 | HISendWindowController *_sendController; 19 | } 20 | - (void)transactionUpdated:(NSNotification *)not; 21 | - (void)sendClosed:(id)sender; 22 | @end 23 | 24 | @implementation HIAppDelegate 25 | 26 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 27 | { 28 | // Configure columns in tableview 29 | _dF = [[NSDateFormatter alloc] init]; 30 | [_dF setDateStyle:NSDateFormatterFullStyle]; 31 | [_dF setTimeStyle:NSDateFormatterFullStyle]; 32 | [_transactionList.tableColumns[0] setIdentifier:@"category"]; 33 | [_transactionList.tableColumns[1] setIdentifier:@"amount"]; 34 | [_transactionList.tableColumns[2] setIdentifier:@"address"]; 35 | [_transactionList.tableColumns[3] setIdentifier:@"time"]; 36 | 37 | [[HIBitcoinManager defaultManager] addObserver:self forKeyPath:@"connections" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:NULL]; 38 | [[HIBitcoinManager defaultManager] addObserver:self forKeyPath:@"balance" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:NULL]; 39 | [[HIBitcoinManager defaultManager] addObserver:self forKeyPath:@"syncProgress" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:NULL]; 40 | [[HIBitcoinManager defaultManager] addObserver:self forKeyPath:@"isRunning" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:NULL]; 41 | [_progressIndicator startAnimation:self]; 42 | [HIBitcoinManager defaultManager].testingNetwork = YES; 43 | // [HIBitcoinManager defaultManager].enableMining = YES; 44 | [[HIBitcoinManager defaultManager] start]; 45 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(transactionUpdated:) name:kHIBitcoinManagerTransactionChangedNotification object:nil]; 46 | } 47 | 48 | - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender 49 | { 50 | if ([HIBitcoinManager defaultManager].isRunning) 51 | return NSTerminateNow; 52 | 53 | return NSTerminateCancel; 54 | } 55 | 56 | - (void)applicationWillTerminate:(NSNotification *)notification 57 | { 58 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 59 | [_progressIndicator stopAnimation:self]; 60 | [[HIBitcoinManager defaultManager] stop]; 61 | [[HIBitcoinManager defaultManager] removeObserver:self forKeyPath:@"connections"]; 62 | [[HIBitcoinManager defaultManager] removeObserver:self forKeyPath:@"balance"]; 63 | [[HIBitcoinManager defaultManager] removeObserver:self forKeyPath:@"syncProgress"]; 64 | [[HIBitcoinManager defaultManager] removeObserver:self forKeyPath:@"isRunning"]; 65 | } 66 | 67 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 68 | { 69 | if (object == [HIBitcoinManager defaultManager]) 70 | { 71 | if ([keyPath compare:@"balance"] == NSOrderedSame) 72 | { 73 | _balanceLabel.stringValue = [NSString stringWithFormat:@"%.4f ฿", (CGFloat)[HIBitcoinManager defaultManager].balance / 100000000.0]; 74 | } 75 | else if ([keyPath compare:@"isRunning"] == NSOrderedSame) 76 | { 77 | if ([HIBitcoinManager defaultManager].isRunning) 78 | { 79 | _stateLabel.stringValue = @"Synchronizing..."; 80 | _addressLabel.stringValue = [HIBitcoinManager defaultManager].walletAddress; 81 | [_sendMoneyBtn setEnabled:YES]; 82 | [_importBtn setEnabled:YES]; 83 | [_exportBtn setEnabled:YES]; 84 | 85 | // We have to refresh transaction list here 86 | _transactions = [[HIBitcoinManager defaultManager] allTransactions]; 87 | [_transactionList reloadData]; 88 | } 89 | } 90 | else if ([keyPath compare:@"syncProgress"] == NSOrderedSame) 91 | { 92 | if ([HIBitcoinManager defaultManager].syncProgress > 0) 93 | { 94 | [_progressIndicator setIndeterminate:NO]; 95 | [_progressIndicator setDoubleValue:[HIBitcoinManager defaultManager].syncProgress]; 96 | } 97 | else 98 | { 99 | [_progressIndicator setIndeterminate:YES]; 100 | } 101 | 102 | if ([HIBitcoinManager defaultManager].syncProgress == 1.0) 103 | { 104 | [_progressIndicator stopAnimation:self]; 105 | _stateLabel.stringValue = @"Synchronized"; 106 | } 107 | else 108 | { 109 | [_progressIndicator startAnimation:self]; 110 | if ([HIBitcoinManager defaultManager].isRunning) 111 | _stateLabel.stringValue = @"Synchronizing..."; 112 | 113 | } 114 | } 115 | } 116 | } 117 | 118 | - (void)transactionUpdated:(NSNotification *)not 119 | { 120 | // In here we simply reload all transactions. 121 | // Real apps can do it in more inteligent fashion 122 | _transactions = [[HIBitcoinManager defaultManager] allTransactions]; 123 | [_transactionList reloadData]; 124 | } 125 | 126 | #pragma mark - TableView methods 127 | 128 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView 129 | { 130 | return _transactions.count; 131 | } 132 | 133 | - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex 134 | { 135 | NSDictionary *transaction = _transactions[rowIndex]; 136 | 137 | 138 | if ([[aTableColumn identifier] compare:@"category"] == NSOrderedSame) 139 | return transaction[@"details"][0][@"category"]; 140 | else if ([[aTableColumn identifier] compare:@"amount"] == NSOrderedSame) 141 | return [NSString stringWithFormat:@"%.4f ฿", [((NSNumber *)transaction[@"amount"]) longLongValue] / 100000000.0]; 142 | else if ([[aTableColumn identifier] compare:@"address"] == NSOrderedSame) 143 | return transaction[@"details"][0][@"address"]; 144 | else if ([[aTableColumn identifier] compare:@"time"] == NSOrderedSame) 145 | return [_dF stringFromDate:transaction[@"time"]]; 146 | return nil; 147 | } 148 | 149 | 150 | - (IBAction)sendMoneyClicked:(NSButton *)sender 151 | { 152 | _sendController= [[HISendWindowController alloc] initWithWindowNibName:@"HISendWindowController"]; 153 | [NSApp beginSheet:_sendController.window modalForWindow:self.window modalDelegate:self didEndSelector:@selector(sendClosed:) contextInfo:NULL]; 154 | } 155 | 156 | - (IBAction)exportWalletClicked:(NSButton *)sender 157 | { 158 | NSSavePanel *sp = [NSSavePanel savePanel]; 159 | sp.title = @"Select where to save your wallet dump"; 160 | sp.prompt = @"Dump"; 161 | sp.allowedFileTypes = @[@"dat"]; 162 | if (NSFileHandlingPanelOKButton == [sp runModal]) 163 | { 164 | NSAlert *alert = [[NSAlert alloc] init]; 165 | [alert setMessageText:@"Wallet export"]; 166 | if ([[HIBitcoinManager defaultManager] exportWalletTo:sp.URL]) 167 | { 168 | [alert setInformativeText:@"Export has been successful"]; 169 | } 170 | else 171 | { 172 | [alert setInformativeText:@"Export has failed"]; 173 | } 174 | [alert addButtonWithTitle:@"Ok"]; 175 | [alert runModal]; 176 | } 177 | 178 | } 179 | 180 | - (IBAction)importWalletClicked:(NSButton *)sender 181 | { 182 | NSOpenPanel *op = [NSOpenPanel openPanel]; 183 | op.title = @"Select dump file to import"; 184 | op.prompt = @"Import"; 185 | op.allowedFileTypes = @[@"dat"]; 186 | if (NSFileHandlingPanelOKButton == [op runModal]) 187 | { 188 | NSAlert *alert = [[NSAlert alloc] init]; 189 | [alert setMessageText:@"Wallet import"]; 190 | if ([[HIBitcoinManager defaultManager] importWalletFrom:op.URL]) 191 | { 192 | [alert setInformativeText:@"Import has been successful"]; 193 | } 194 | else 195 | { 196 | [alert setInformativeText:@"Import has failed"]; 197 | } 198 | [alert addButtonWithTitle:@"Ok"]; 199 | [alert runModal]; 200 | } 201 | } 202 | 203 | - (void)sendClosed:(id)sender 204 | { 205 | _sendController = nil; 206 | } 207 | @end 208 | -------------------------------------------------------------------------------- /BitcoinJKit/java/src/main/java/org/slf4j/impl/CocoaLogger.java: -------------------------------------------------------------------------------- 1 | package org.slf4j.impl; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.helpers.FormattingTuple; 5 | import org.slf4j.helpers.MarkerIgnoringBase; 6 | import org.slf4j.helpers.MessageFormatter; 7 | 8 | // based on http://javaeenotes.blogspot.com/2011/12/custom-slf4j-logger-adapter.html and JDK14LoggerAdapter 9 | 10 | public class CocoaLogger extends MarkerIgnoringBase implements Logger 11 | { 12 | public static final int HILoggerLevelNotSet = -1; 13 | public static final int HILoggerLevelDebug = 1; 14 | public static final int HILoggerLevelInfo = 2; 15 | public static final int HILoggerLevelWarn = 3; 16 | public static final int HILoggerLevelError = 4; 17 | 18 | private static String SELF = CocoaLogger.class.getName(); 19 | private static String SUPER = MarkerIgnoringBase.class.getName(); 20 | 21 | private static int globalLevel = HILoggerLevelDebug; 22 | private int level = HILoggerLevelNotSet; 23 | 24 | public static int getGlobalLevel() 25 | { 26 | return globalLevel; 27 | } 28 | 29 | public static void setGlobalLevel(int newLevel) 30 | { 31 | globalLevel = newLevel; 32 | } 33 | 34 | CocoaLogger(String name) 35 | { 36 | this.name = name; 37 | } 38 | 39 | public int getLevel() 40 | { 41 | return (level == HILoggerLevelNotSet) ? globalLevel : level; 42 | } 43 | 44 | public void setLevel(int newLevel) 45 | { 46 | level = newLevel; 47 | } 48 | 49 | public boolean isTraceEnabled() 50 | { 51 | return getLevel() <= HILoggerLevelDebug; 52 | } 53 | 54 | public void trace(String msg) 55 | { 56 | if (isTraceEnabled()) 57 | { 58 | log(SELF, HILoggerLevelDebug, msg, null); 59 | } 60 | } 61 | 62 | public void trace(String format, Object arg) 63 | { 64 | if (isTraceEnabled()) 65 | { 66 | FormattingTuple ft = MessageFormatter.format(format, arg); 67 | log(SELF, HILoggerLevelDebug, ft.getMessage(), ft.getThrowable()); 68 | } 69 | } 70 | 71 | public void trace(String format, Object arg1, Object arg2) 72 | { 73 | if (isTraceEnabled()) 74 | { 75 | FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); 76 | log(SELF, HILoggerLevelDebug, ft.getMessage(), ft.getThrowable()); 77 | } 78 | } 79 | 80 | public void trace(String format, Object... argArray) 81 | { 82 | if (isTraceEnabled()) 83 | { 84 | FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray); 85 | log(SELF, HILoggerLevelDebug, ft.getMessage(), ft.getThrowable()); 86 | } 87 | } 88 | 89 | public void trace(String msg, Throwable t) 90 | { 91 | if (isTraceEnabled()) 92 | { 93 | log(SELF, HILoggerLevelDebug, msg, t); 94 | } 95 | } 96 | 97 | public boolean isDebugEnabled() 98 | { 99 | return getLevel() <= HILoggerLevelDebug; 100 | } 101 | 102 | public void debug(String msg) 103 | { 104 | if (isDebugEnabled()) 105 | { 106 | log(SELF, HILoggerLevelDebug, msg, null); 107 | } 108 | } 109 | 110 | public void debug(String format, Object arg) 111 | { 112 | if (isDebugEnabled()) 113 | { 114 | FormattingTuple ft = MessageFormatter.format(format, arg); 115 | log(SELF, HILoggerLevelDebug, ft.getMessage(), ft.getThrowable()); 116 | } 117 | } 118 | 119 | public void debug(String format, Object arg1, Object arg2) 120 | { 121 | if (isDebugEnabled()) 122 | { 123 | FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); 124 | log(SELF, HILoggerLevelDebug, ft.getMessage(), ft.getThrowable()); 125 | } 126 | } 127 | 128 | public void debug(String format, Object... argArray) 129 | { 130 | if (isDebugEnabled()) 131 | { 132 | FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray); 133 | log(SELF, HILoggerLevelDebug, ft.getMessage(), ft.getThrowable()); 134 | } 135 | } 136 | 137 | public void debug(String msg, Throwable t) 138 | { 139 | if (isDebugEnabled()) 140 | { 141 | log(SELF, HILoggerLevelDebug, msg, t); 142 | } 143 | } 144 | 145 | public boolean isInfoEnabled() 146 | { 147 | return getLevel() <= HILoggerLevelInfo; 148 | } 149 | 150 | public void info(String msg) 151 | { 152 | if (isInfoEnabled()) 153 | { 154 | log(SELF, HILoggerLevelInfo, msg, null); 155 | } 156 | } 157 | 158 | public void info(String format, Object arg) 159 | { 160 | if (isInfoEnabled()) 161 | { 162 | FormattingTuple ft = MessageFormatter.format(format, arg); 163 | log(SELF, HILoggerLevelInfo, ft.getMessage(), ft.getThrowable()); 164 | } 165 | } 166 | 167 | public void info(String format, Object arg1, Object arg2) 168 | { 169 | if (isInfoEnabled()) 170 | { 171 | FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); 172 | log(SELF, HILoggerLevelInfo, ft.getMessage(), ft.getThrowable()); 173 | } 174 | } 175 | 176 | public void info(String format, Object... argArray) 177 | { 178 | if (isInfoEnabled()) 179 | { 180 | FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray); 181 | log(SELF, HILoggerLevelInfo, ft.getMessage(), ft.getThrowable()); 182 | } 183 | } 184 | 185 | public void info(String msg, Throwable t) 186 | { 187 | if (isInfoEnabled()) 188 | { 189 | log(SELF, HILoggerLevelInfo, msg, t); 190 | } 191 | } 192 | 193 | public boolean isWarnEnabled() 194 | { 195 | return getLevel() <= HILoggerLevelWarn; 196 | } 197 | 198 | public void warn(String msg) 199 | { 200 | if (isWarnEnabled()) 201 | { 202 | log(SELF, HILoggerLevelWarn, msg, null); 203 | } 204 | } 205 | 206 | public void warn(String format, Object arg) 207 | { 208 | if (isWarnEnabled()) 209 | { 210 | FormattingTuple ft = MessageFormatter.format(format, arg); 211 | log(SELF, HILoggerLevelWarn, ft.getMessage(), ft.getThrowable()); 212 | } 213 | } 214 | 215 | public void warn(String format, Object arg1, Object arg2) 216 | { 217 | if (isWarnEnabled()) 218 | { 219 | FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); 220 | log(SELF, HILoggerLevelWarn, ft.getMessage(), ft.getThrowable()); 221 | } 222 | } 223 | 224 | public void warn(String format, Object... argArray) 225 | { 226 | if (isWarnEnabled()) 227 | { 228 | FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray); 229 | log(SELF, HILoggerLevelWarn, ft.getMessage(), ft.getThrowable()); 230 | } 231 | } 232 | 233 | public void warn(String msg, Throwable t) 234 | { 235 | if (isWarnEnabled()) 236 | { 237 | log(SELF, HILoggerLevelWarn, msg, t); 238 | } 239 | } 240 | 241 | public boolean isErrorEnabled() 242 | { 243 | return getLevel() <= HILoggerLevelError; 244 | } 245 | 246 | public void error(String msg) 247 | { 248 | if (isErrorEnabled()) 249 | { 250 | log(SELF, HILoggerLevelError, msg, null); 251 | } 252 | } 253 | 254 | public void error(String format, Object arg) 255 | { 256 | if (isErrorEnabled()) 257 | { 258 | FormattingTuple ft = MessageFormatter.format(format, arg); 259 | log(SELF, HILoggerLevelError, ft.getMessage(), ft.getThrowable()); 260 | } 261 | } 262 | 263 | public void error(String format, Object arg1, Object arg2) 264 | { 265 | if (isErrorEnabled()) 266 | { 267 | FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); 268 | log(SELF, HILoggerLevelError, ft.getMessage(), ft.getThrowable()); 269 | } 270 | } 271 | 272 | public void error(String format, Object... arguments) 273 | { 274 | if (isErrorEnabled()) 275 | { 276 | FormattingTuple ft = MessageFormatter.arrayFormat(format, arguments); 277 | log(SELF, HILoggerLevelError, ft.getMessage(), ft.getThrowable()); 278 | } 279 | } 280 | 281 | public void error(String msg, Throwable t) 282 | { 283 | if (isErrorEnabled()) 284 | { 285 | log(SELF, HILoggerLevelError, msg, t); 286 | } 287 | } 288 | 289 | private void log(String callerFQCN, int level, String msg, Throwable t) 290 | { 291 | String fileName = null; 292 | String methodName = null; 293 | int lineNumber = 0; 294 | 295 | StackTraceElement callerData = getCallerData(callerFQCN); 296 | if (callerData != null) 297 | { 298 | fileName = callerData.getFileName(); 299 | lineNumber = callerData.getLineNumber(); 300 | 301 | String className = callerData.getClassName().replaceAll(".*\\.", ""); 302 | methodName = "[" + className + " " + callerData.getMethodName() + "]"; 303 | } 304 | 305 | if (msg != null) 306 | { 307 | receiveLogFromJVM(fileName, methodName, lineNumber, level, msg); 308 | } 309 | 310 | if (t != null) 311 | { 312 | receiveLogFromJVM(fileName, methodName, lineNumber, level, "Exception logged: " + t); 313 | } 314 | } 315 | 316 | // TODO 317 | private StackTraceElement getCallerData(String callerFQCN) 318 | { 319 | StackTraceElement[] steArray = new Throwable().getStackTrace(); 320 | 321 | int selfIndex = -1; 322 | for (int i = 0; i < steArray.length; i++) 323 | { 324 | final String className = steArray[i].getClassName(); 325 | if (className.equals(callerFQCN) || className.equals(SUPER)) 326 | { 327 | selfIndex = i; 328 | break; 329 | } 330 | } 331 | 332 | int found = -1; 333 | for (int i = selfIndex + 1; i < steArray.length; i++) 334 | { 335 | final String className = steArray[i].getClassName(); 336 | if (!(className.equals(callerFQCN) || className.equals(SUPER))) 337 | { 338 | found = i; 339 | break; 340 | } 341 | } 342 | 343 | if (found != -1) 344 | { 345 | return steArray[found]; 346 | } 347 | else 348 | { 349 | return null; 350 | } 351 | } 352 | 353 | public native void receiveLogFromJVM(String fileName, String methodName, int lineNumber, int level, String msg); 354 | } 355 | -------------------------------------------------------------------------------- /BitcoinJKit/HIBitcoinManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // HIBitcoinManager.h 3 | // BitcoinKit 4 | // 5 | // Created by Bazyli Zygan on 11.07.2013. 6 | // Extended by Jonas Schnelli 2013 7 | 8 | // Copyright (c) 2013 Hive Developers. All rights reserved. 9 | // 10 | 11 | #import 12 | 13 | // define the nanobtc type 14 | typedef int64_t nanobtc_t; 15 | 16 | extern NSString * const kHIBitcoinManagerTransactionChangedNotification; //<<< Transaction list update notification. Sent object is a NSString representation of the updated hash 17 | extern NSString * const kHIBitcoinManagerStartedNotification; //<<< Manager start notification. Informs that manager is now ready to use 18 | extern NSString * const kHIBitcoinManagerStoppedNotification; //<<< Manager stop notification. Informs that manager is now stopped and can't be used anymore 19 | 20 | #define kHI_PREPARE_SEND_COINS_DID_FAIL_ENC -1 21 | #define kHI_PREPARE_SEND_COINS_DID_FAIL_NOT_ENOUGHT_FUNDS -2 22 | #define kHI_PREPARE_SEND_COINS_DID_FAIL_UNKNOWN -100 23 | 24 | #define kHIBitcoinManagerCoinsReceivedNotification @"kJHIBitcoinManagerCoinsReceivedNotification" 25 | 26 | /** HIBitcoinManager is a class responsible for managing all Bitcoin actions app should do 27 | * 28 | * Word of warning. One should not create this object. All access should be done 29 | * via defaultManager class method that returns application-wide singleton to it. 30 | * 31 | * All properties are KVC enabled so one can register as an observer to them to monitor the changes. 32 | */ 33 | @interface HIBitcoinManager : NSObject 34 | 35 | @property (nonatomic, copy) NSString *appSupportDirectoryIdentifier; //<<< Specifies the support directory identifier. Warning! All changes to it has to be performed BEFORE start. 36 | @property (nonatomic, copy) NSString *appName; //<<< Specifies an App Name. The name will be used for data file/folder creation. Warning! All changes to it has to be performed BEFORE initialize. 37 | @property (nonatomic, copy) NSURL *dataURL; //<<< Specifies an URL path to a directory where HIBitcoinManager should store its data. Warning! All changes to it has to be performed BEFORE initialize. 38 | @property (nonatomic, assign) BOOL testingNetwork; //<<< Specifies if a manager is running on the testing network. Warning! All changes to it has to be performed BEFORE initialize. 39 | @property (nonatomic, readonly) NSUInteger connections; //<<< Currently active connections to bitcoin network 40 | @property (nonatomic, readonly) BOOL isRunning; //<<< Flag indicating if NPBitcoinManager is currently running and connecting with the network 41 | @property (nonatomic, readonly) uint64_t balance; //<<< Actual balance of the wallet 42 | @property (nonatomic, readonly) uint64_t balanceUnconfirmed; //<<< Actual balance of the wallet 43 | @property (nonatomic, readonly) double syncProgress; //<<< Double value indicating the progress of network sync. Values are from 0.0 to 1.0. 44 | @property (nonatomic, readonly) long currentBlockCount; //<<< Double value indicating the progress of network sync. Values are from 0.0 to 1.0. 45 | @property (nonatomic, readonly) long totalBlocks; //<<< Double value indicating the progress of network sync. Values are from 0.0 to 1.0. 46 | @property (nonatomic, readonly) NSUInteger peerCount; //<<< Integer value indicating how many peers are connected. 47 | @property (nonatomic, readonly, getter = walletAddress) NSString *walletAddress; //<<< Returns wallets main address. Creates one if none exists yet 48 | @property (nonatomic, readonly, getter = allWalletAddresses) NSArray *allWalletAddresses; //<<< Returns all wallet addresses. 49 | @property (nonatomic, readonly) NSString *walletFileBase64String; //<<< Returns the wallet file as base64 string. 50 | 51 | @property (nonatomic, readonly, getter = isWalletEncrypted) BOOL isWalletEncrypted; //<<< Returns YES if wallet is encrypted. NO - otherwise 52 | @property (nonatomic, readonly, getter = isWalletLocked) BOOL isWalletLocked; //<<< Returns YES if wallet is currently locked. NO - otherwise 53 | @property (nonatomic, readonly, getter = transactionCount) NSUInteger transactionCount; //<<< Returns global transaction cound for current wallet 54 | @property (nonatomic, readonly, getter = lastBlockCreationTime) NSDate* lastBlockCreationTime; //<<< Returns the creation time of the last block in the SPVBlockStore 55 | @property (nonatomic, assign) BOOL disableListening; //<<< Flag disabling listening on public IP address. To be used i.e. with tor proxy not to reveal real IP address. Warning! All changes to it has to be performed BEFORE initialize. 56 | 57 | // Block that will be called when an exception is thrown on a background thread in JVM (e.g. while processing an 58 | // incoming transaction or other blockchain update). If not set, the exception will just be thrown and will crash your 59 | // app unless you install a global uncaught exception handler. 60 | // Note: exceptions that are thrown while processing calls made from the Cocoa side will ignore this handler and will 61 | // simply be thrown directly in the same thread. 62 | @property (nonatomic, copy) void(^exceptionHandler)(NSException *exception); 63 | 64 | @property (nonatomic, copy, readonly) NSString *decimalSeparator; 65 | @property (nonatomic, copy, readonly) NSArray *availableFormats; 66 | @property (nonatomic, copy) NSString *preferredFormat; 67 | @property (nonatomic, copy) NSLocale *locale; 68 | 69 | /** Class method returning application singleton to the manager. 70 | * 71 | * Please note not to create HIBitcoinManager objects in any other way. 72 | * This is due to bitcoind implementation that uses global variables that 73 | * currently allows us to create only one instance of this object. 74 | * Which should be more than enough anyway. 75 | * 76 | * @returns Initialized and ready manager object. 77 | */ 78 | + (HIBitcoinManager *)defaultManager; 79 | 80 | /** Starts the manager initializing all data and starting network sync. 81 | * 82 | * One should start the manager only once. After configuring the singleton. 83 | */ 84 | - (void)initialize:(NSError **)error; 85 | - (void)loadWallet:(NSError **)error; 86 | - (void)startBlockchain:(NSError **)error; 87 | 88 | /** Stops the manager and stores all up-to-date information in data folder 89 | * 90 | * One should stop the manager only once. At the shutdown procedure. 91 | * This is due to bitcoind implementation that uses too many globals. 92 | */ 93 | - (void)stop; 94 | 95 | - (void)resyncBlockchain:(NSError **)error; 96 | 97 | /** Returns transaction definition based on transaction hash 98 | * 99 | * @param hash NSString representation of transaction hash 100 | * 101 | * @returns NSDictionary definition of found transansaction. nil if not found 102 | */ 103 | - (NSDictionary *)transactionForHash:(NSString *)hash; 104 | 105 | /** Returns transaction definition based on transaction hash 106 | * 107 | * WARNING: Because transaction are kept in maps in bitcoind the only way 108 | * to find an element at requested index is to iterate through all of elements 109 | * in front. DO NOT USE too often or your app will get absurdely slow 110 | * 111 | * @param index Index of the searched transaction 112 | * 113 | * @returns NSDictionary definition of found transansaction. nil if not found 114 | */ 115 | - (NSDictionary *)transactionAtIndex:(NSUInteger)index; 116 | 117 | /** Returns an array of definitions of all transactions 118 | * 119 | * @param max amount of transactions, use 0 for unlimited 120 | * 121 | * @returns Array of all transactions to this wallet 122 | */ 123 | - (NSArray *)allTransactions:(int)max; 124 | 125 | /** Returns array of transactions from given range 126 | * 127 | * @param range Range of requested transactions 128 | * 129 | * @returns An array of transactions from requested range 130 | */ 131 | - (NSArray *)transactionsWithRange:(NSRange)range; 132 | 133 | /** Checks if given address is valid address 134 | * 135 | * @param address Address string to be checked 136 | * 137 | * @returns YES if address is valid. NO - otherwise 138 | */ 139 | - (BOOL)isAddressValid:(NSString *)address; 140 | 141 | /** Creates a new wallet protected with a password. 142 | * 143 | * Only call this if start returned kHIBitcoinManagerNoWallet. 144 | * It will fail if a wallet already exists. 145 | * 146 | * @param password The user password as an UTF-16-encoded string. 147 | */ 148 | - (void)createWalletWithPassword:(NSData *)password 149 | error:(NSError **)error; 150 | 151 | /** Creates a new ECKey 152 | * 153 | * @returns the new address string or nil if failed 154 | */ 155 | - (NSString *)addKey; 156 | 157 | /** Sends amount of coins to receipent 158 | * 159 | * @param coins Amount of coins to be sent in satoshis 160 | * @param receipent Receipent address hash 161 | * @param comment optional comment string that will be bound to the transaction 162 | * @param complection Completion block where notification about created transaction hash will be sent 163 | * 164 | * @returns the fee in nanobtc as a long 165 | * 166 | */ 167 | - (void)prepareSendCoins:(nanobtc_t)coins toReceipent:(NSString *)receipent comment:(NSString *)comment password:(NSData *)password returnFee:(nanobtc_t *)feeRetVal error:(NSError **)error; 168 | 169 | - (NSString *)commitPreparedTransaction:(NSError **)error; 170 | - (void)clearSendRequest:(NSError **)error; 171 | 172 | /** save the wallet to the given wallet store file 173 | * 174 | * @returns YES if save was successful, NO - otherwise 175 | */ 176 | - (BOOL)saveWallet; 177 | 178 | /** Encrypts wallet with given passphrase 179 | * 180 | * @param passphrase NSString value of the passphrase to encrypt wallet with 181 | * 182 | * @returns YES if encryption was successful, NO - otherwise 183 | */ 184 | - (void)changeWalletPassword:(NSData *)fromPassword 185 | toPassword:(NSData *)toPassword 186 | error:(NSError **)error; 187 | 188 | /** Removes wallet encryption with given passphrase 189 | * 190 | * @param passphrase NSString value of the passphrase to decrypt wallet with 191 | * 192 | * @returns YES if encryption was successful, NO - otherwise 193 | */ 194 | - (void)removeEncryption:(NSData *)password error:(NSError **)error; 195 | 196 | /** Changes the encryption passphrase for the wallet 197 | * 198 | * @param oldpasswd Old passphrase that wallet is currently encrypted with 199 | * @param newpasswd New passphrase that wallet should be encrypted with 200 | * 201 | * @returns YES if recryption was successful, NO - otherwise 202 | */ 203 | - (BOOL)changeWalletEncryptionKeyFrom:(NSString *)oldpasswd to:(NSString *)newpasswd; 204 | 205 | /** Unlocks wallet 206 | * 207 | * @param passwd Passphrase that wallet is locked with 208 | * 209 | * @returns YES if unlock was successful, NO - otherwise 210 | */ 211 | - (BOOL)unlockWalletWith:(NSString *)passwd; 212 | 213 | /** Locks wallet */ 214 | - (void)lockWallet; 215 | 216 | /** Exports wallet to given file URL 217 | * 218 | * @param exportURL NSURL to local file where wallet should be dumped to 219 | * 220 | * @returns YES if dump was successful. NO - otherwise 221 | */ 222 | - (BOOL)exportWalletWithPassphase:(NSString *)passphrase To:(NSURL *)exportURL; 223 | 224 | /** Import wallet from given file URL 225 | * 226 | * @param importURL NSURL to local file from which to import wallet data 227 | * 228 | * @returns YES if import was successful. NO - otherwise 229 | */ 230 | - (BOOL)importWalletFrom:(NSURL *)importURL; 231 | 232 | /** Formts nano btc (satoshis) to a nice NSString with the standard BTC unit 233 | * 234 | * @param NSInteger nanoBTC; 235 | * 236 | * @returns YES if import was successful. NO - otherwise 237 | */ 238 | - (NSString *)formatNanobtc:(nanobtc_t)nanoBtc; 239 | - (NSString *)formatNanobtc:(nanobtc_t)nanoBtcValue withDesignator:(BOOL)designator; 240 | - (nanobtc_t)nanoBtcFromString:(NSString *)userAmount format:(NSString *)format; 241 | 242 | /** Checks if the wallet is encryped 243 | * 244 | * @returns YES if wallet is encryped. NO - otherwise 245 | */ 246 | - (BOOL)isWalletEncrypted; 247 | 248 | - (NSString *)walletFilename; 249 | 250 | @end 251 | 252 | -------------------------------------------------------------------------------- /DemoApp/HIBitcoinJKitDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D958B0C917A2A8FD00B0EDD5 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D91FE7C617900FCC001B37D8 /* Cocoa.framework */; }; 11 | D958B0CF17A2A8FD00B0EDD5 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D958B0CD17A2A8FD00B0EDD5 /* InfoPlist.strings */; }; 12 | D958B0D517A2A8FD00B0EDD5 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = D958B0D317A2A8FD00B0EDD5 /* Credits.rtf */; }; 13 | E5D2F45817F444EF00A214F4 /* HIAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E5D2F45317F444EF00A214F4 /* HIAppDelegate.m */; }; 14 | E5D2F45917F444EF00A214F4 /* HISendWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = E5D2F45517F444EF00A214F4 /* HISendWindowController.m */; }; 15 | E5D2F45A17F444EF00A214F4 /* HISendWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E5D2F45617F444EF00A214F4 /* HISendWindowController.xib */; }; 16 | E5D2F45B17F444EF00A214F4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E5D2F45717F444EF00A214F4 /* main.m */; }; 17 | E5D2F46017F4456300A214F4 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = E5D2F45F17F4456300A214F4 /* MainMenu.xib */; }; 18 | E5D2F46817F4463000A214F4 /* BitcoinJKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E5D2F46717F4461300A214F4 /* BitcoinJKit.framework */; }; 19 | E5D2F46917F4463600A214F4 /* BitcoinJKit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = E5D2F46717F4461300A214F4 /* BitcoinJKit.framework */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXContainerItemProxy section */ 23 | E5D2F46617F4461300A214F4 /* PBXContainerItemProxy */ = { 24 | isa = PBXContainerItemProxy; 25 | containerPortal = D91FE7E31790100A001B37D8 /* BitcoinJKit.xcodeproj */; 26 | proxyType = 2; 27 | remoteGlobalIDString = D958B09D17A29B0500B0EDD5; 28 | remoteInfo = BitcoinJKit; 29 | }; 30 | /* End PBXContainerItemProxy section */ 31 | 32 | /* Begin PBXCopyFilesBuildPhase section */ 33 | D958B0E517A2A91B00B0EDD5 /* CopyFiles */ = { 34 | isa = PBXCopyFilesBuildPhase; 35 | buildActionMask = 12; 36 | dstPath = ""; 37 | dstSubfolderSpec = 10; 38 | files = ( 39 | E5D2F46917F4463600A214F4 /* BitcoinJKit.framework in CopyFiles */, 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXCopyFilesBuildPhase section */ 44 | 45 | /* Begin PBXFileReference section */ 46 | D91FE7C617900FCC001B37D8 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 47 | D91FE7C917900FCC001B37D8 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 48 | D91FE7CA17900FCC001B37D8 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; 49 | D91FE7CB17900FCC001B37D8 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 50 | D91FE7E31790100A001B37D8 /* BitcoinJKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = BitcoinJKit.xcodeproj; path = ../BitcoinJKit.xcodeproj; sourceTree = ""; }; 51 | D958B0C817A2A8FD00B0EDD5 /* HIBitcoinJKitDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HIBitcoinJKitDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | D958B0CC17A2A8FD00B0EDD5 /* HIBitcoinJKitDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "HIBitcoinJKitDemo-Info.plist"; sourceTree = ""; }; 53 | D958B0CE17A2A8FD00B0EDD5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 54 | D958B0D017A2A8FD00B0EDD5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 55 | D958B0D217A2A8FD00B0EDD5 /* HIBitcoinJKitDemo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "HIBitcoinJKitDemo-Prefix.pch"; sourceTree = ""; }; 56 | D958B0D417A2A8FD00B0EDD5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = ""; }; 57 | E5D2F45217F444EF00A214F4 /* HIAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HIAppDelegate.h; sourceTree = ""; }; 58 | E5D2F45317F444EF00A214F4 /* HIAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HIAppDelegate.m; sourceTree = ""; }; 59 | E5D2F45417F444EF00A214F4 /* HISendWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HISendWindowController.h; sourceTree = ""; }; 60 | E5D2F45517F444EF00A214F4 /* HISendWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HISendWindowController.m; sourceTree = ""; }; 61 | E5D2F45617F444EF00A214F4 /* HISendWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HISendWindowController.xib; sourceTree = ""; }; 62 | E5D2F45717F444EF00A214F4 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 63 | E5D2F45F17F4456300A214F4 /* MainMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = ""; }; 64 | /* End PBXFileReference section */ 65 | 66 | /* Begin PBXFrameworksBuildPhase section */ 67 | D958B0C517A2A8FD00B0EDD5 /* Frameworks */ = { 68 | isa = PBXFrameworksBuildPhase; 69 | buildActionMask = 2147483647; 70 | files = ( 71 | D958B0C917A2A8FD00B0EDD5 /* Cocoa.framework in Frameworks */, 72 | E5D2F46817F4463000A214F4 /* BitcoinJKit.framework in Frameworks */, 73 | ); 74 | runOnlyForDeploymentPostprocessing = 0; 75 | }; 76 | /* End PBXFrameworksBuildPhase section */ 77 | 78 | /* Begin PBXGroup section */ 79 | D91FE7BA17900FCC001B37D8 = { 80 | isa = PBXGroup; 81 | children = ( 82 | D958B0CA17A2A8FD00B0EDD5 /* HIBitcoinJKitDemo */, 83 | D91FE7C517900FCC001B37D8 /* Frameworks */, 84 | D91FE7C417900FCC001B37D8 /* Products */, 85 | ); 86 | sourceTree = ""; 87 | }; 88 | D91FE7C417900FCC001B37D8 /* Products */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | D958B0C817A2A8FD00B0EDD5 /* HIBitcoinJKitDemo.app */, 92 | ); 93 | name = Products; 94 | sourceTree = ""; 95 | }; 96 | D91FE7C517900FCC001B37D8 /* Frameworks */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | D91FE7E31790100A001B37D8 /* BitcoinJKit.xcodeproj */, 100 | D91FE7C617900FCC001B37D8 /* Cocoa.framework */, 101 | D91FE7C817900FCC001B37D8 /* Other Frameworks */, 102 | ); 103 | name = Frameworks; 104 | sourceTree = ""; 105 | }; 106 | D91FE7C817900FCC001B37D8 /* Other Frameworks */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | D91FE7C917900FCC001B37D8 /* AppKit.framework */, 110 | D91FE7CA17900FCC001B37D8 /* CoreData.framework */, 111 | D91FE7CB17900FCC001B37D8 /* Foundation.framework */, 112 | ); 113 | name = "Other Frameworks"; 114 | sourceTree = ""; 115 | }; 116 | D958B0CA17A2A8FD00B0EDD5 /* HIBitcoinJKitDemo */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | E5D2F45F17F4456300A214F4 /* MainMenu.xib */, 120 | E5D2F45217F444EF00A214F4 /* HIAppDelegate.h */, 121 | E5D2F45317F444EF00A214F4 /* HIAppDelegate.m */, 122 | E5D2F45417F444EF00A214F4 /* HISendWindowController.h */, 123 | E5D2F45517F444EF00A214F4 /* HISendWindowController.m */, 124 | E5D2F45617F444EF00A214F4 /* HISendWindowController.xib */, 125 | E5D2F45717F444EF00A214F4 /* main.m */, 126 | D958B0CB17A2A8FD00B0EDD5 /* Supporting Files */, 127 | ); 128 | path = HIBitcoinJKitDemo; 129 | sourceTree = ""; 130 | }; 131 | D958B0CB17A2A8FD00B0EDD5 /* Supporting Files */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | D958B0CC17A2A8FD00B0EDD5 /* HIBitcoinJKitDemo-Info.plist */, 135 | D958B0CD17A2A8FD00B0EDD5 /* InfoPlist.strings */, 136 | D958B0D017A2A8FD00B0EDD5 /* main.m */, 137 | D958B0D217A2A8FD00B0EDD5 /* HIBitcoinJKitDemo-Prefix.pch */, 138 | D958B0D317A2A8FD00B0EDD5 /* Credits.rtf */, 139 | ); 140 | name = "Supporting Files"; 141 | sourceTree = ""; 142 | }; 143 | E5D2F46317F4461300A214F4 /* Products */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | E5D2F46717F4461300A214F4 /* BitcoinJKit.framework */, 147 | ); 148 | name = Products; 149 | sourceTree = ""; 150 | }; 151 | /* End PBXGroup section */ 152 | 153 | /* Begin PBXNativeTarget section */ 154 | D958B0C717A2A8FD00B0EDD5 /* HIBitcoinJKitDemo */ = { 155 | isa = PBXNativeTarget; 156 | buildConfigurationList = D958B0E317A2A8FD00B0EDD5 /* Build configuration list for PBXNativeTarget "HIBitcoinJKitDemo" */; 157 | buildPhases = ( 158 | D958B0C417A2A8FD00B0EDD5 /* Sources */, 159 | D958B0C517A2A8FD00B0EDD5 /* Frameworks */, 160 | D958B0C617A2A8FD00B0EDD5 /* Resources */, 161 | D958B0E517A2A91B00B0EDD5 /* CopyFiles */, 162 | ); 163 | buildRules = ( 164 | ); 165 | dependencies = ( 166 | ); 167 | name = HIBitcoinJKitDemo; 168 | productName = HIBitcoinJKitDemo; 169 | productReference = D958B0C817A2A8FD00B0EDD5 /* HIBitcoinJKitDemo.app */; 170 | productType = "com.apple.product-type.application"; 171 | }; 172 | /* End PBXNativeTarget section */ 173 | 174 | /* Begin PBXProject section */ 175 | D91FE7BB17900FCC001B37D8 /* Project object */ = { 176 | isa = PBXProject; 177 | attributes = { 178 | CLASSPREFIX = HI; 179 | LastUpgradeCheck = 0460; 180 | ORGANIZATIONNAME = "Hive Developers"; 181 | }; 182 | buildConfigurationList = D91FE7BE17900FCC001B37D8 /* Build configuration list for PBXProject "HIBitcoinJKitDemo" */; 183 | compatibilityVersion = "Xcode 3.2"; 184 | developmentRegion = English; 185 | hasScannedForEncodings = 0; 186 | knownRegions = ( 187 | en, 188 | ); 189 | mainGroup = D91FE7BA17900FCC001B37D8; 190 | productRefGroup = D91FE7C417900FCC001B37D8 /* Products */; 191 | projectDirPath = ""; 192 | projectReferences = ( 193 | { 194 | ProductGroup = E5D2F46317F4461300A214F4 /* Products */; 195 | ProjectRef = D91FE7E31790100A001B37D8 /* BitcoinJKit.xcodeproj */; 196 | }, 197 | ); 198 | projectRoot = ""; 199 | targets = ( 200 | D958B0C717A2A8FD00B0EDD5 /* HIBitcoinJKitDemo */, 201 | ); 202 | }; 203 | /* End PBXProject section */ 204 | 205 | /* Begin PBXReferenceProxy section */ 206 | E5D2F46717F4461300A214F4 /* BitcoinJKit.framework */ = { 207 | isa = PBXReferenceProxy; 208 | fileType = wrapper.framework; 209 | path = BitcoinJKit.framework; 210 | remoteRef = E5D2F46617F4461300A214F4 /* PBXContainerItemProxy */; 211 | sourceTree = BUILT_PRODUCTS_DIR; 212 | }; 213 | /* End PBXReferenceProxy section */ 214 | 215 | /* Begin PBXResourcesBuildPhase section */ 216 | D958B0C617A2A8FD00B0EDD5 /* Resources */ = { 217 | isa = PBXResourcesBuildPhase; 218 | buildActionMask = 2147483647; 219 | files = ( 220 | D958B0CF17A2A8FD00B0EDD5 /* InfoPlist.strings in Resources */, 221 | D958B0D517A2A8FD00B0EDD5 /* Credits.rtf in Resources */, 222 | E5D2F45A17F444EF00A214F4 /* HISendWindowController.xib in Resources */, 223 | E5D2F46017F4456300A214F4 /* MainMenu.xib in Resources */, 224 | ); 225 | runOnlyForDeploymentPostprocessing = 0; 226 | }; 227 | /* End PBXResourcesBuildPhase section */ 228 | 229 | /* Begin PBXSourcesBuildPhase section */ 230 | D958B0C417A2A8FD00B0EDD5 /* Sources */ = { 231 | isa = PBXSourcesBuildPhase; 232 | buildActionMask = 2147483647; 233 | files = ( 234 | E5D2F45917F444EF00A214F4 /* HISendWindowController.m in Sources */, 235 | E5D2F45B17F444EF00A214F4 /* main.m in Sources */, 236 | E5D2F45817F444EF00A214F4 /* HIAppDelegate.m in Sources */, 237 | ); 238 | runOnlyForDeploymentPostprocessing = 0; 239 | }; 240 | /* End PBXSourcesBuildPhase section */ 241 | 242 | /* Begin PBXVariantGroup section */ 243 | D958B0CD17A2A8FD00B0EDD5 /* InfoPlist.strings */ = { 244 | isa = PBXVariantGroup; 245 | children = ( 246 | D958B0CE17A2A8FD00B0EDD5 /* en */, 247 | ); 248 | name = InfoPlist.strings; 249 | sourceTree = ""; 250 | }; 251 | D958B0D317A2A8FD00B0EDD5 /* Credits.rtf */ = { 252 | isa = PBXVariantGroup; 253 | children = ( 254 | D958B0D417A2A8FD00B0EDD5 /* en */, 255 | ); 256 | name = Credits.rtf; 257 | sourceTree = ""; 258 | }; 259 | /* End PBXVariantGroup section */ 260 | 261 | /* Begin XCBuildConfiguration section */ 262 | D91FE7DE17900FCC001B37D8 /* Debug */ = { 263 | isa = XCBuildConfiguration; 264 | buildSettings = { 265 | ALWAYS_SEARCH_USER_PATHS = NO; 266 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 267 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 268 | CLANG_CXX_LIBRARY = "libc++"; 269 | CLANG_ENABLE_OBJC_ARC = YES; 270 | CLANG_WARN_CONSTANT_CONVERSION = YES; 271 | CLANG_WARN_EMPTY_BODY = YES; 272 | CLANG_WARN_ENUM_CONVERSION = YES; 273 | CLANG_WARN_INT_CONVERSION = YES; 274 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 275 | COPY_PHASE_STRIP = NO; 276 | GCC_C_LANGUAGE_STANDARD = gnu99; 277 | GCC_DYNAMIC_NO_PIC = NO; 278 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 279 | GCC_OPTIMIZATION_LEVEL = 0; 280 | GCC_PREPROCESSOR_DEFINITIONS = ( 281 | "DEBUG=1", 282 | "$(inherited)", 283 | ); 284 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 285 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 286 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 287 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 288 | GCC_WARN_UNUSED_VARIABLE = YES; 289 | MACOSX_DEPLOYMENT_TARGET = 10.8; 290 | ONLY_ACTIVE_ARCH = YES; 291 | SDKROOT = macosx; 292 | }; 293 | name = Debug; 294 | }; 295 | D91FE7DF17900FCC001B37D8 /* Release */ = { 296 | isa = XCBuildConfiguration; 297 | buildSettings = { 298 | ALWAYS_SEARCH_USER_PATHS = NO; 299 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 300 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 301 | CLANG_CXX_LIBRARY = "libc++"; 302 | CLANG_ENABLE_OBJC_ARC = YES; 303 | CLANG_WARN_CONSTANT_CONVERSION = YES; 304 | CLANG_WARN_EMPTY_BODY = YES; 305 | CLANG_WARN_ENUM_CONVERSION = YES; 306 | CLANG_WARN_INT_CONVERSION = YES; 307 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 308 | COPY_PHASE_STRIP = YES; 309 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 310 | GCC_C_LANGUAGE_STANDARD = gnu99; 311 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 312 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 313 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 314 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 315 | GCC_WARN_UNUSED_VARIABLE = YES; 316 | MACOSX_DEPLOYMENT_TARGET = 10.8; 317 | SDKROOT = macosx; 318 | }; 319 | name = Release; 320 | }; 321 | D958B0DC17A2A8FD00B0EDD5 /* Debug */ = { 322 | isa = XCBuildConfiguration; 323 | buildSettings = { 324 | COMBINE_HIDPI_IMAGES = YES; 325 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 326 | GCC_PREFIX_HEADER = "HIBitcoinJKitDemo/HIBitcoinJKitDemo-Prefix.pch"; 327 | INFOPLIST_FILE = "HIBitcoinJKitDemo/HIBitcoinJKitDemo-Info.plist"; 328 | PRODUCT_NAME = "$(TARGET_NAME)"; 329 | WRAPPER_EXTENSION = app; 330 | }; 331 | name = Debug; 332 | }; 333 | D958B0DD17A2A8FD00B0EDD5 /* Release */ = { 334 | isa = XCBuildConfiguration; 335 | buildSettings = { 336 | COMBINE_HIDPI_IMAGES = YES; 337 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 338 | GCC_PREFIX_HEADER = "HIBitcoinJKitDemo/HIBitcoinJKitDemo-Prefix.pch"; 339 | INFOPLIST_FILE = "HIBitcoinJKitDemo/HIBitcoinJKitDemo-Info.plist"; 340 | PRODUCT_NAME = "$(TARGET_NAME)"; 341 | WRAPPER_EXTENSION = app; 342 | }; 343 | name = Release; 344 | }; 345 | /* End XCBuildConfiguration section */ 346 | 347 | /* Begin XCConfigurationList section */ 348 | D91FE7BE17900FCC001B37D8 /* Build configuration list for PBXProject "HIBitcoinJKitDemo" */ = { 349 | isa = XCConfigurationList; 350 | buildConfigurations = ( 351 | D91FE7DE17900FCC001B37D8 /* Debug */, 352 | D91FE7DF17900FCC001B37D8 /* Release */, 353 | ); 354 | defaultConfigurationIsVisible = 0; 355 | defaultConfigurationName = Release; 356 | }; 357 | D958B0E317A2A8FD00B0EDD5 /* Build configuration list for PBXNativeTarget "HIBitcoinJKitDemo" */ = { 358 | isa = XCConfigurationList; 359 | buildConfigurations = ( 360 | D958B0DC17A2A8FD00B0EDD5 /* Debug */, 361 | D958B0DD17A2A8FD00B0EDD5 /* Release */, 362 | ); 363 | defaultConfigurationIsVisible = 0; 364 | defaultConfigurationName = Release; 365 | }; 366 | /* End XCConfigurationList section */ 367 | }; 368 | rootObject = D91FE7BB17900FCC001B37D8 /* Project object */; 369 | } 370 | -------------------------------------------------------------------------------- /BitcoinJKit.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D901F49917AF9DAB0071780D /* JavaVM.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D901F49817AF9DAA0071780D /* JavaVM.framework */; }; 11 | D901F50017B279EA0071780D /* boot.jar in Resources */ = {isa = PBXBuildFile; fileRef = D901F49A17AF9DDB0071780D /* boot.jar */; }; 12 | D958B09E17A29B0500B0EDD5 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D91FE4F1178EB1F1001B37D8 /* Cocoa.framework */; }; 13 | D958B0A417A29B0500B0EDD5 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D958B0A217A29B0500B0EDD5 /* InfoPlist.strings */; }; 14 | D958B0BC17A2A40600B0EDD5 /* HIBitcoinManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D958B0BA17A2A40600B0EDD5 /* HIBitcoinManager.m */; }; 15 | D958B0BE17A2A41A00B0EDD5 /* BitcoinJKit.h in Headers */ = {isa = PBXBuildFile; fileRef = D958B0A617A29B0500B0EDD5 /* BitcoinJKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 16 | E56F90B917ECC48600135193 /* HIBitcoinManager.h in Headers */ = {isa = PBXBuildFile; fileRef = E56F90B817ECC48600135193 /* HIBitcoinManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17 | E5E45294189F84B600FAC936 /* HIBitcoinErrorCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = E5E45292189F84B600FAC936 /* HIBitcoinErrorCodes.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18 | E5E45295189F84B600FAC936 /* HIBitcoinErrorCodes.m in Sources */ = {isa = PBXBuildFile; fileRef = E5E45293189F84B600FAC936 /* HIBitcoinErrorCodes.m */; }; 19 | E5E45298189F84F900FAC936 /* HIBitcoinInternalErrorCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = E5E45296189F84F900FAC936 /* HIBitcoinInternalErrorCodes.h */; }; 20 | E5E45299189F84F900FAC936 /* HIBitcoinInternalErrorCodes.m in Sources */ = {isa = PBXBuildFile; fileRef = E5E45297189F84F900FAC936 /* HIBitcoinInternalErrorCodes.m */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | D901F49817AF9DAA0071780D /* JavaVM.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaVM.framework; path = System/Library/Frameworks/JavaVM.framework; sourceTree = SDKROOT; }; 25 | D901F49A17AF9DDB0071780D /* boot.jar */ = {isa = PBXFileReference; lastKnownFileType = archive.jar; path = boot.jar; sourceTree = SOURCE_ROOT; }; 26 | D901F4EC17B2479C0071780D /* BitcoinManager.java */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.java; name = BitcoinManager.java; path = java/src/main/java/com/hive/bitcoinkit/BitcoinManager.java; sourceTree = ""; }; 27 | D91FE4F1178EB1F1001B37D8 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 28 | D91FE4F4178EB1F1001B37D8 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 29 | D91FE4F5178EB1F1001B37D8 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; 30 | D91FE4F6178EB1F1001B37D8 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 31 | D958B09D17A29B0500B0EDD5 /* BitcoinJKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BitcoinJKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | D958B0A117A29B0500B0EDD5 /* BitcoinJKit-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "BitcoinJKit-Info.plist"; sourceTree = ""; }; 33 | D958B0A317A29B0500B0EDD5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 34 | D958B0A517A29B0500B0EDD5 /* BitcoinJKit-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BitcoinJKit-Prefix.pch"; sourceTree = ""; }; 35 | D958B0A617A29B0500B0EDD5 /* BitcoinJKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BitcoinJKit.h; sourceTree = ""; }; 36 | D958B0BA17A2A40600B0EDD5 /* HIBitcoinManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HIBitcoinManager.m; sourceTree = ""; }; 37 | E56F90B817ECC48600135193 /* HIBitcoinManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HIBitcoinManager.h; sourceTree = ""; }; 38 | E5E45292189F84B600FAC936 /* HIBitcoinErrorCodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HIBitcoinErrorCodes.h; sourceTree = ""; }; 39 | E5E45293189F84B600FAC936 /* HIBitcoinErrorCodes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HIBitcoinErrorCodes.m; sourceTree = ""; }; 40 | E5E45296189F84F900FAC936 /* HIBitcoinInternalErrorCodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HIBitcoinInternalErrorCodes.h; sourceTree = ""; }; 41 | E5E45297189F84F900FAC936 /* HIBitcoinInternalErrorCodes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HIBitcoinInternalErrorCodes.m; sourceTree = ""; }; 42 | /* End PBXFileReference section */ 43 | 44 | /* Begin PBXFrameworksBuildPhase section */ 45 | D958B09917A29B0500B0EDD5 /* Frameworks */ = { 46 | isa = PBXFrameworksBuildPhase; 47 | buildActionMask = 2147483647; 48 | files = ( 49 | D901F49917AF9DAB0071780D /* JavaVM.framework in Frameworks */, 50 | D958B09E17A29B0500B0EDD5 /* Cocoa.framework in Frameworks */, 51 | ); 52 | runOnlyForDeploymentPostprocessing = 0; 53 | }; 54 | /* End PBXFrameworksBuildPhase section */ 55 | 56 | /* Begin PBXGroup section */ 57 | D91FE4E4178EB1F1001B37D8 = { 58 | isa = PBXGroup; 59 | children = ( 60 | D958B09F17A29B0500B0EDD5 /* BitcoinJKit */, 61 | D91FE4F0178EB1F1001B37D8 /* Frameworks */, 62 | D91FE4EF178EB1F1001B37D8 /* Products */, 63 | ); 64 | sourceTree = ""; 65 | }; 66 | D91FE4EF178EB1F1001B37D8 /* Products */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | D958B09D17A29B0500B0EDD5 /* BitcoinJKit.framework */, 70 | ); 71 | name = Products; 72 | sourceTree = ""; 73 | }; 74 | D91FE4F0178EB1F1001B37D8 /* Frameworks */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | D901F49817AF9DAA0071780D /* JavaVM.framework */, 78 | D91FE4F1178EB1F1001B37D8 /* Cocoa.framework */, 79 | D91FE4F3178EB1F1001B37D8 /* Other Frameworks */, 80 | ); 81 | name = Frameworks; 82 | sourceTree = ""; 83 | }; 84 | D91FE4F3178EB1F1001B37D8 /* Other Frameworks */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | D91FE4F4178EB1F1001B37D8 /* AppKit.framework */, 88 | D91FE4F5178EB1F1001B37D8 /* CoreData.framework */, 89 | D91FE4F6178EB1F1001B37D8 /* Foundation.framework */, 90 | ); 91 | name = "Other Frameworks"; 92 | sourceTree = ""; 93 | }; 94 | D958B09F17A29B0500B0EDD5 /* BitcoinJKit */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | D901F4EC17B2479C0071780D /* BitcoinManager.java */, 98 | D901F49A17AF9DDB0071780D /* boot.jar */, 99 | D958B0A617A29B0500B0EDD5 /* BitcoinJKit.h */, 100 | E56F90B817ECC48600135193 /* HIBitcoinManager.h */, 101 | D958B0BA17A2A40600B0EDD5 /* HIBitcoinManager.m */, 102 | E5E45292189F84B600FAC936 /* HIBitcoinErrorCodes.h */, 103 | E5E45293189F84B600FAC936 /* HIBitcoinErrorCodes.m */, 104 | E5E45296189F84F900FAC936 /* HIBitcoinInternalErrorCodes.h */, 105 | E5E45297189F84F900FAC936 /* HIBitcoinInternalErrorCodes.m */, 106 | D958B0A017A29B0500B0EDD5 /* Supporting Files */, 107 | ); 108 | path = BitcoinJKit; 109 | sourceTree = ""; 110 | }; 111 | D958B0A017A29B0500B0EDD5 /* Supporting Files */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | D958B0A117A29B0500B0EDD5 /* BitcoinJKit-Info.plist */, 115 | D958B0A217A29B0500B0EDD5 /* InfoPlist.strings */, 116 | D958B0A517A29B0500B0EDD5 /* BitcoinJKit-Prefix.pch */, 117 | ); 118 | name = "Supporting Files"; 119 | sourceTree = ""; 120 | }; 121 | /* End PBXGroup section */ 122 | 123 | /* Begin PBXHeadersBuildPhase section */ 124 | D958B09A17A29B0500B0EDD5 /* Headers */ = { 125 | isa = PBXHeadersBuildPhase; 126 | buildActionMask = 2147483647; 127 | files = ( 128 | D958B0BE17A2A41A00B0EDD5 /* BitcoinJKit.h in Headers */, 129 | E5E45298189F84F900FAC936 /* HIBitcoinInternalErrorCodes.h in Headers */, 130 | E5E45294189F84B600FAC936 /* HIBitcoinErrorCodes.h in Headers */, 131 | E56F90B917ECC48600135193 /* HIBitcoinManager.h in Headers */, 132 | ); 133 | runOnlyForDeploymentPostprocessing = 0; 134 | }; 135 | /* End PBXHeadersBuildPhase section */ 136 | 137 | /* Begin PBXNativeTarget section */ 138 | D958B09C17A29B0500B0EDD5 /* BitcoinJKit */ = { 139 | isa = PBXNativeTarget; 140 | buildConfigurationList = D958B0AB17A29B0500B0EDD5 /* Build configuration list for PBXNativeTarget "BitcoinJKit" */; 141 | buildPhases = ( 142 | D958B0AC17A29C2C00B0EDD5 /* ShellScript */, 143 | D958B09817A29B0500B0EDD5 /* Sources */, 144 | D958B09917A29B0500B0EDD5 /* Frameworks */, 145 | D958B09A17A29B0500B0EDD5 /* Headers */, 146 | D958B09B17A29B0500B0EDD5 /* Resources */, 147 | ); 148 | buildRules = ( 149 | ); 150 | dependencies = ( 151 | ); 152 | name = BitcoinJKit; 153 | productName = BitcoinJKit; 154 | productReference = D958B09D17A29B0500B0EDD5 /* BitcoinJKit.framework */; 155 | productType = "com.apple.product-type.framework"; 156 | }; 157 | /* End PBXNativeTarget section */ 158 | 159 | /* Begin PBXProject section */ 160 | D91FE4E5178EB1F1001B37D8 /* Project object */ = { 161 | isa = PBXProject; 162 | attributes = { 163 | LastUpgradeCheck = 0460; 164 | ORGANIZATIONNAME = "Hive Developers"; 165 | }; 166 | buildConfigurationList = D91FE4E8178EB1F1001B37D8 /* Build configuration list for PBXProject "BitcoinJKit" */; 167 | compatibilityVersion = "Xcode 3.2"; 168 | developmentRegion = English; 169 | hasScannedForEncodings = 0; 170 | knownRegions = ( 171 | en, 172 | ); 173 | mainGroup = D91FE4E4178EB1F1001B37D8; 174 | productRefGroup = D91FE4EF178EB1F1001B37D8 /* Products */; 175 | projectDirPath = ""; 176 | projectRoot = ""; 177 | targets = ( 178 | D958B09C17A29B0500B0EDD5 /* BitcoinJKit */, 179 | ); 180 | }; 181 | /* End PBXProject section */ 182 | 183 | /* Begin PBXResourcesBuildPhase section */ 184 | D958B09B17A29B0500B0EDD5 /* Resources */ = { 185 | isa = PBXResourcesBuildPhase; 186 | buildActionMask = 2147483647; 187 | files = ( 188 | D958B0A417A29B0500B0EDD5 /* InfoPlist.strings in Resources */, 189 | D901F50017B279EA0071780D /* boot.jar in Resources */, 190 | ); 191 | runOnlyForDeploymentPostprocessing = 0; 192 | }; 193 | /* End PBXResourcesBuildPhase section */ 194 | 195 | /* Begin PBXShellScriptBuildPhase section */ 196 | D958B0AC17A29C2C00B0EDD5 /* ShellScript */ = { 197 | isa = PBXShellScriptBuildPhase; 198 | buildActionMask = 2147483647; 199 | files = ( 200 | ); 201 | inputPaths = ( 202 | ); 203 | outputPaths = ( 204 | ); 205 | runOnlyForDeploymentPostprocessing = 0; 206 | shellPath = /bin/sh; 207 | shellScript = "\ncd ${SRCROOT}\n\n# make sure homebrew bin path is included\nexport PATH=$PATH:/usr/local/bin\n\n# Check if we have the classes compiled with avian\n\nif [ ! -f boot.jar ] || [ \"$(find . -name '*.java' -newer boot.jar)\" ]; then\n\necho \"REBUILDING boot.jar...\"\n\n\ncd BitcoinJKit/java\n\n# Build bitcoinj with bitcoinkit bridge\nmvn clean package -Dmaven.test.skip=true || exit 1\n\ncd ../../\n\ncp BitcoinJKit/java/target/BitcoinJKit-*.jar boot.jar\n\n\nfi\n"; 208 | }; 209 | /* End PBXShellScriptBuildPhase section */ 210 | 211 | /* Begin PBXSourcesBuildPhase section */ 212 | D958B09817A29B0500B0EDD5 /* Sources */ = { 213 | isa = PBXSourcesBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | E5E45299189F84F900FAC936 /* HIBitcoinInternalErrorCodes.m in Sources */, 217 | D958B0BC17A2A40600B0EDD5 /* HIBitcoinManager.m in Sources */, 218 | E5E45295189F84B600FAC936 /* HIBitcoinErrorCodes.m in Sources */, 219 | ); 220 | runOnlyForDeploymentPostprocessing = 0; 221 | }; 222 | /* End PBXSourcesBuildPhase section */ 223 | 224 | /* Begin PBXVariantGroup section */ 225 | D958B0A217A29B0500B0EDD5 /* InfoPlist.strings */ = { 226 | isa = PBXVariantGroup; 227 | children = ( 228 | D958B0A317A29B0500B0EDD5 /* en */, 229 | ); 230 | name = InfoPlist.strings; 231 | sourceTree = ""; 232 | }; 233 | /* End PBXVariantGroup section */ 234 | 235 | /* Begin XCBuildConfiguration section */ 236 | D91FE501178EB1F1001B37D8 /* Debug */ = { 237 | isa = XCBuildConfiguration; 238 | buildSettings = { 239 | ALWAYS_SEARCH_USER_PATHS = NO; 240 | ARCHS = "$(ARCHS_STANDARD)"; 241 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 242 | CLANG_CXX_LIBRARY = "libc++"; 243 | CLANG_WARN_CONSTANT_CONVERSION = YES; 244 | CLANG_WARN_EMPTY_BODY = YES; 245 | CLANG_WARN_ENUM_CONVERSION = YES; 246 | CLANG_WARN_INT_CONVERSION = YES; 247 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 248 | COPY_PHASE_STRIP = NO; 249 | GCC_C_LANGUAGE_STANDARD = gnu99; 250 | GCC_DYNAMIC_NO_PIC = NO; 251 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 252 | GCC_OPTIMIZATION_LEVEL = 0; 253 | GCC_PREPROCESSOR_DEFINITIONS = ( 254 | "DEBUG=1", 255 | "$(inherited)", 256 | ); 257 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 258 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 259 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 260 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 261 | GCC_WARN_UNUSED_VARIABLE = YES; 262 | MACOSX_DEPLOYMENT_TARGET = 10.7; 263 | ONLY_ACTIVE_ARCH = YES; 264 | SDKROOT = macosx10.9; 265 | }; 266 | name = Debug; 267 | }; 268 | D91FE502178EB1F1001B37D8 /* Release */ = { 269 | isa = XCBuildConfiguration; 270 | buildSettings = { 271 | ALWAYS_SEARCH_USER_PATHS = NO; 272 | ARCHS = "$(ARCHS_STANDARD)"; 273 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 274 | CLANG_CXX_LIBRARY = "libc++"; 275 | CLANG_WARN_CONSTANT_CONVERSION = YES; 276 | CLANG_WARN_EMPTY_BODY = YES; 277 | CLANG_WARN_ENUM_CONVERSION = YES; 278 | CLANG_WARN_INT_CONVERSION = YES; 279 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 280 | COPY_PHASE_STRIP = YES; 281 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 282 | GCC_C_LANGUAGE_STANDARD = gnu99; 283 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 284 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 285 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 286 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 287 | GCC_WARN_UNUSED_VARIABLE = YES; 288 | MACOSX_DEPLOYMENT_TARGET = 10.7; 289 | SDKROOT = macosx10.9; 290 | }; 291 | name = Release; 292 | }; 293 | D958B0A917A29B0500B0EDD5 /* Debug */ = { 294 | isa = XCBuildConfiguration; 295 | buildSettings = { 296 | COMBINE_HIDPI_IMAGES = YES; 297 | DYLIB_COMPATIBILITY_VERSION = 1; 298 | DYLIB_CURRENT_VERSION = 1; 299 | FRAMEWORK_VERSION = A; 300 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 301 | GCC_PREFIX_HEADER = "BitcoinJKit/BitcoinJKit-Prefix.pch"; 302 | GCC_VERSION = ""; 303 | INFOPLIST_FILE = "BitcoinJKit/BitcoinJKit-Info.plist"; 304 | INSTALL_PATH = "@executable_path/../Frameworks"; 305 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 306 | MACOSX_DEPLOYMENT_TARGET = 10.6; 307 | PRODUCT_NAME = "$(TARGET_NAME)"; 308 | SKIP_INSTALL = YES; 309 | WRAPPER_EXTENSION = framework; 310 | }; 311 | name = Debug; 312 | }; 313 | D958B0AA17A29B0500B0EDD5 /* Release */ = { 314 | isa = XCBuildConfiguration; 315 | buildSettings = { 316 | COMBINE_HIDPI_IMAGES = YES; 317 | DYLIB_COMPATIBILITY_VERSION = 1; 318 | DYLIB_CURRENT_VERSION = 1; 319 | FRAMEWORK_VERSION = A; 320 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 321 | GCC_PREFIX_HEADER = "BitcoinJKit/BitcoinJKit-Prefix.pch"; 322 | GCC_VERSION = ""; 323 | INFOPLIST_FILE = "BitcoinJKit/BitcoinJKit-Info.plist"; 324 | INSTALL_PATH = "@executable_path/../Frameworks"; 325 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 326 | MACOSX_DEPLOYMENT_TARGET = 10.6; 327 | PRODUCT_NAME = "$(TARGET_NAME)"; 328 | SKIP_INSTALL = YES; 329 | WRAPPER_EXTENSION = framework; 330 | }; 331 | name = Release; 332 | }; 333 | /* End XCBuildConfiguration section */ 334 | 335 | /* Begin XCConfigurationList section */ 336 | D91FE4E8178EB1F1001B37D8 /* Build configuration list for PBXProject "BitcoinJKit" */ = { 337 | isa = XCConfigurationList; 338 | buildConfigurations = ( 339 | D91FE501178EB1F1001B37D8 /* Debug */, 340 | D91FE502178EB1F1001B37D8 /* Release */, 341 | ); 342 | defaultConfigurationIsVisible = 0; 343 | defaultConfigurationName = Release; 344 | }; 345 | D958B0AB17A29B0500B0EDD5 /* Build configuration list for PBXNativeTarget "BitcoinJKit" */ = { 346 | isa = XCConfigurationList; 347 | buildConfigurations = ( 348 | D958B0A917A29B0500B0EDD5 /* Debug */, 349 | D958B0AA17A29B0500B0EDD5 /* Release */, 350 | ); 351 | defaultConfigurationIsVisible = 0; 352 | defaultConfigurationName = Release; 353 | }; 354 | /* End XCConfigurationList section */ 355 | }; 356 | rootObject = D91FE4E5178EB1F1001B37D8 /* Project object */; 357 | } 358 | -------------------------------------------------------------------------------- /DemoApp/HIBitcoinJKitDemo/HISendWindowController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1070 5 | 12E55 6 | 3084 7 | 1187.39 8 | 626.00 9 | 10 | com.apple.InterfaceBuilder.CocoaPlugin 11 | 3084 12 | 13 | 14 | NSButton 15 | NSButtonCell 16 | NSCustomObject 17 | NSTextField 18 | NSTextFieldCell 19 | NSView 20 | NSWindowTemplate 21 | 22 | 23 | com.apple.InterfaceBuilder.CocoaPlugin 24 | 25 | 26 | PluginDependencyRecalculationVersion 27 | 28 | 29 | 30 | 31 | HISendWindowController 32 | 33 | 34 | FirstResponder 35 | 36 | 37 | NSApplication 38 | 39 | 40 | 23 41 | 2 42 | {{167, 107}, {404, 118}} 43 | -1535638528 44 | Window 45 | NSPanel 46 | 47 | 48 | 49 | 50 | 256 51 | 52 | 53 | 54 | 268 55 | {{17, 88}, {61, 17}} 56 | 57 | 58 | 59 | _NS:1535 60 | YES 61 | 62 | 68157504 63 | 272630784 64 | Receiver: 65 | 66 | LucidaGrande 67 | 13 68 | 1044 69 | 70 | _NS:1535 71 | 72 | 73 | 6 74 | System 75 | controlColor 76 | 77 | 3 78 | MC42NjY2NjY2NjY3AA 79 | 80 | 81 | 82 | 6 83 | System 84 | controlTextColor 85 | 86 | 3 87 | MAA 88 | 89 | 90 | 91 | NO 92 | 93 | 94 | 95 | 268 96 | {{17, 61}, {59, 17}} 97 | 98 | 99 | 100 | _NS:1535 101 | YES 102 | 103 | 68157504 104 | 272630784 105 | Amount: 106 | 107 | _NS:1535 108 | 109 | 110 | 111 | 112 | NO 113 | 114 | 115 | 116 | 268 117 | {{89, 83}, {295, 22}} 118 | 119 | 120 | 121 | _NS:9 122 | YES 123 | 124 | -1804599231 125 | 272630784 126 | 127 | 128 | _NS:9 129 | 130 | YES 131 | 132 | 6 133 | System 134 | textBackgroundColor 135 | 136 | 3 137 | MQA 138 | 139 | 140 | 141 | 6 142 | System 143 | textColor 144 | 145 | 146 | 147 | NO 148 | 149 | 150 | 151 | 268 152 | {{89, 58}, {295, 22}} 153 | 154 | 155 | 156 | _NS:9 157 | YES 158 | 159 | -1804599231 160 | 272630784 161 | 162 | 163 | _NS:9 164 | 165 | YES 166 | 167 | 168 | 169 | NO 170 | 171 | 172 | 173 | 268 174 | {{314, 13}, {71, 32}} 175 | 176 | 177 | 178 | _NS:9 179 | YES 180 | 181 | 67108864 182 | 134217728 183 | Send 184 | 185 | _NS:9 186 | 187 | -2038284288 188 | 129 189 | 190 | DQ 191 | 200 192 | 25 193 | 194 | NO 195 | 196 | 197 | 198 | 268 199 | {{233, 13}, {82, 32}} 200 | 201 | 202 | 203 | _NS:9 204 | YES 205 | 206 | 67108864 207 | 134217728 208 | Cancel 209 | 210 | _NS:9 211 | 212 | -2038284288 213 | 129 214 | 215 | Gw 216 | 200 217 | 25 218 | 219 | NO 220 | 221 | 222 | {404, 118} 223 | 224 | 225 | 226 | _NS:21 227 | 228 | {{0, 0}, {1680, 1028}} 229 | {10000000000000, 10000000000000} 230 | YES 231 | 232 | 233 | 234 | 235 | 236 | 237 | window 238 | 239 | 240 | 241 | 7 242 | 243 | 244 | 245 | addressField 246 | 247 | 248 | 249 | 23 250 | 251 | 252 | 253 | amountField 254 | 255 | 256 | 257 | 24 258 | 259 | 260 | 261 | cancelClicked: 262 | 263 | 264 | 265 | 25 266 | 267 | 268 | 269 | sendClicked: 270 | 271 | 272 | 273 | 26 274 | 275 | 276 | 277 | delegate 278 | 279 | 280 | 281 | 8 282 | 283 | 284 | 285 | 286 | 287 | 0 288 | 289 | 290 | 291 | 292 | 293 | -2 294 | 295 | 296 | File's Owner 297 | 298 | 299 | -1 300 | 301 | 302 | First Responder 303 | 304 | 305 | -3 306 | 307 | 308 | Application 309 | 310 | 311 | 5 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 6 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 9 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 10 341 | 342 | 343 | 344 | 345 | 13 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 14 354 | 355 | 356 | 357 | 358 | 15 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 16 367 | 368 | 369 | 370 | 371 | 17 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 18 380 | 381 | 382 | 383 | 384 | 19 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 20 393 | 394 | 395 | 396 | 397 | 21 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 22 406 | 407 | 408 | 409 | 410 | 411 | 412 | com.apple.InterfaceBuilder.CocoaPlugin 413 | com.apple.InterfaceBuilder.CocoaPlugin 414 | com.apple.InterfaceBuilder.CocoaPlugin 415 | com.apple.InterfaceBuilder.CocoaPlugin 416 | com.apple.InterfaceBuilder.CocoaPlugin 417 | com.apple.InterfaceBuilder.CocoaPlugin 418 | com.apple.InterfaceBuilder.CocoaPlugin 419 | com.apple.InterfaceBuilder.CocoaPlugin 420 | com.apple.InterfaceBuilder.CocoaPlugin 421 | com.apple.InterfaceBuilder.CocoaPlugin 422 | com.apple.InterfaceBuilder.CocoaPlugin 423 | com.apple.InterfaceBuilder.CocoaPlugin 424 | com.apple.InterfaceBuilder.CocoaPlugin 425 | com.apple.InterfaceBuilder.CocoaPlugin 426 | com.apple.InterfaceBuilder.CocoaPlugin 427 | 428 | com.apple.InterfaceBuilder.CocoaPlugin 429 | com.apple.InterfaceBuilder.CocoaPlugin 430 | 431 | 432 | 433 | 434 | 435 | 26 436 | 437 | 438 | 439 | 440 | HISendWindowController 441 | NSWindowController 442 | 443 | NSButton 444 | NSButton 445 | 446 | 447 | 448 | cancelClicked: 449 | NSButton 450 | 451 | 452 | sendClicked: 453 | NSButton 454 | 455 | 456 | 457 | NSTextField 458 | NSTextField 459 | 460 | 461 | 462 | addressField 463 | NSTextField 464 | 465 | 466 | amountField 467 | NSTextField 468 | 469 | 470 | 471 | IBProjectSource 472 | ./Classes/HISendWindowController.h 473 | 474 | 475 | 476 | 477 | 0 478 | IBCocoaFramework 479 | 480 | com.apple.InterfaceBuilder.CocoaPlugin.macosx 481 | 482 | 483 | YES 484 | 3 485 | 486 | 487 | -------------------------------------------------------------------------------- /BitcoinJKit/java/src/main/java/com/hive/bitcoinkit/BitcoinManager.java: -------------------------------------------------------------------------------- 1 | package com.hive.bitcoinkit; 2 | 3 | import static com.google.bitcoin.core.Utils.bytesToHexString; 4 | 5 | import com.google.bitcoin.core.*; 6 | import com.google.bitcoin.crypto.KeyCrypter; 7 | import com.google.bitcoin.crypto.KeyCrypterException; 8 | import com.google.bitcoin.crypto.KeyCrypterScrypt; 9 | import com.google.bitcoin.net.discovery.DnsDiscovery; 10 | import com.google.bitcoin.params.MainNetParams; 11 | import com.google.bitcoin.params.RegTestParams; 12 | import com.google.bitcoin.params.TestNet3Params; 13 | import com.google.bitcoin.script.Script; 14 | import com.google.bitcoin.store.BlockStore; 15 | import com.google.bitcoin.store.BlockStoreException; 16 | import com.google.bitcoin.store.SPVBlockStore; 17 | import com.google.bitcoin.store.UnreadableWalletException; 18 | import com.google.bitcoin.store.WalletProtobufSerializer; 19 | import com.google.bitcoin.utils.BriefLogFormatter; 20 | import com.google.bitcoin.utils.Threading; 21 | import com.google.common.util.concurrent.*; 22 | 23 | import org.spongycastle.util.encoders.Base64; 24 | import org.spongycastle.crypto.params.KeyParameter; 25 | 26 | import java.io.ByteArrayOutputStream; 27 | import java.io.ByteArrayInputStream; 28 | import java.io.File; 29 | import java.io.FileInputStream; 30 | import java.io.IOException; 31 | import java.math.BigInteger; 32 | import java.net.InetAddress; 33 | import java.nio.charset.Charset; 34 | import java.util.List; 35 | import java.util.concurrent.TimeUnit; 36 | import java.util.HashSet; 37 | import java.text.SimpleDateFormat; 38 | import java.util.Arrays; 39 | import java.util.Date; 40 | import java.util.TimeZone; 41 | import java.util.concurrent.TimeUnit; 42 | 43 | import org.slf4j.Logger; 44 | import org.slf4j.LoggerFactory; 45 | import org.slf4j.impl.CocoaLogger; 46 | import java.nio.CharBuffer; 47 | 48 | public class BitcoinManager implements PeerEventListener, Thread.UncaughtExceptionHandler, TransactionConfidence.Listener { 49 | private NetworkParameters networkParams; 50 | private Wallet wallet; 51 | private String dataDirectory; 52 | private String appName = "bitcoinkit"; 53 | private PeerGroup peerGroup; 54 | private BlockStore blockStore; 55 | private BlockChain chain; 56 | private File walletFile; 57 | private int blocksToDownload; 58 | private int storedChainHeight; 59 | private int broadcastMinTransactions = -1; 60 | private HashSet trackedTransactions; 61 | 62 | private Wallet.SendRequest pendingSendRequest; 63 | 64 | private static final Logger log = LoggerFactory.getLogger(BitcoinManager.class); 65 | 66 | /* --- Initialization & configuration --- */ 67 | 68 | public BitcoinManager() 69 | { 70 | Threading.uncaughtExceptionHandler = this; 71 | trackedTransactions = new HashSet(); 72 | ((CocoaLogger) log).setLevel(CocoaLogger.HILoggerLevelDebug); 73 | } 74 | 75 | /* --- Thread.UncaughtExceptionHandler --- */ 76 | 77 | public void uncaughtException(Thread thread, Throwable exception) 78 | { 79 | onException(exception); 80 | } 81 | 82 | public String getExceptionStackTrace(Throwable exception) 83 | { 84 | StringBuilder buffer = new StringBuilder(); 85 | 86 | for (StackTraceElement line : exception.getStackTrace()) 87 | { 88 | buffer.append("at " + line.toString() + "\n"); 89 | } 90 | 91 | return buffer.toString(); 92 | } 93 | 94 | public void setTestingNetwork(boolean testing) 95 | { 96 | if (testing) 97 | { 98 | broadcastMinTransactions = 1; 99 | this.networkParams = TestNet3Params.get(); 100 | } 101 | else 102 | { 103 | broadcastMinTransactions = -1; // std 104 | this.networkParams = MainNetParams.get(); 105 | } 106 | } 107 | 108 | public void setDataDirectory(String path) 109 | { 110 | dataDirectory = path; 111 | } 112 | 113 | public String getDataDirectory() 114 | { 115 | return dataDirectory; 116 | } 117 | 118 | public String getWalletPath() 119 | { 120 | return dataDirectory + "/"+ appName +".wallet"; 121 | } 122 | 123 | public void setAppName(String newAppName) 124 | { 125 | appName = newAppName; 126 | } 127 | 128 | public String getAppName() 129 | { 130 | return appName; 131 | } 132 | 133 | public String getWalletAddress() 134 | { 135 | ECKey ecKey = wallet.getKeys().get(0); 136 | return ecKey.toAddress(networkParams).toString(); 137 | } 138 | 139 | public String getAllWalletAddressesJSON() 140 | { 141 | StringBuffer conns = new StringBuffer(); 142 | conns.append("["); 143 | for(ECKey key: wallet.getKeys()) 144 | { 145 | conns.append("\"" + key.toAddress(networkParams).toString() + "\","); 146 | } 147 | if(conns.substring(conns.length() -1).equals(",")) 148 | { 149 | conns.deleteCharAt(conns.length() -1); 150 | } 151 | conns.append("]"); 152 | return conns.toString(); 153 | } 154 | 155 | public BigInteger getBalance(int type) 156 | { 157 | if(type == 0) 158 | { 159 | return wallet.getBalance(Wallet.BalanceType.AVAILABLE); 160 | } 161 | else 162 | { 163 | return wallet.getBalance(Wallet.BalanceType.ESTIMATED); 164 | } 165 | } 166 | 167 | public String getBalanceString(int type) 168 | { 169 | if (wallet != null) 170 | return getBalance(type).toString(); 171 | 172 | return null; 173 | } 174 | 175 | private String getJSONFromTransaction(Transaction tx) 176 | { 177 | if (tx != null) 178 | { 179 | try { 180 | String confidence = "building"; 181 | StringBuffer conns = new StringBuffer(); 182 | int connCount = 0; 183 | 184 | if (tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.PENDING) 185 | confidence = "pending"; 186 | 187 | conns.append("["); 188 | 189 | if (tx.getInputs().size() > 0 && tx.getValue(wallet).compareTo(BigInteger.ZERO) > 0) 190 | { 191 | TransactionInput in = tx.getInput(0); 192 | if (connCount > 0) 193 | conns.append(", "); 194 | 195 | conns.append("{ "); 196 | try { 197 | Script scriptSig = in.getScriptSig(); 198 | if (scriptSig.getChunks().size() == 2) 199 | conns.append("\"address\": \"" + scriptSig.getFromAddress(networkParams).toString() + "\""); 200 | 201 | conns.append(" ,\"category\": \"received\" }"); 202 | 203 | connCount++; 204 | } catch (Exception e) { 205 | 206 | } 207 | } 208 | 209 | if (tx.getOutputs().size() > 0 && tx.getValue(wallet).compareTo(BigInteger.ZERO) < 0) 210 | { 211 | TransactionOutput out = tx.getOutput(0); 212 | 213 | if (connCount > 0) 214 | conns.append(", "); 215 | 216 | conns.append("{ "); 217 | try { 218 | Script scriptPubKey = out.getScriptPubKey(); 219 | if (scriptPubKey.isSentToAddress()) 220 | conns.append(" \"address\": \"" + scriptPubKey.getToAddress(networkParams).toString() + "\""); 221 | 222 | conns.append(" ,\"category\": \"sent\" }"); 223 | 224 | connCount++; 225 | } catch (Exception e) { 226 | 227 | } 228 | } 229 | conns.append("]"); 230 | //else if (tx.get) 231 | return "{ \"amount\": " + tx.getValue(wallet) + 232 | ", \"txid\": \"" + tx.getHashAsString() + "\"" + 233 | ", \"time\": \"" + tx.getUpdateTime() + "\"" + 234 | ", \"confidence\": \"" +confidence + "\"" + 235 | ", \"details\": " + conns.toString() + 236 | "}"; 237 | 238 | } catch (ScriptException e) { 239 | // TODO Auto-generated catch block 240 | e.printStackTrace(); 241 | } 242 | } 243 | 244 | return null; 245 | } 246 | 247 | public int getTransactionCount() 248 | { 249 | if(wallet == null) 250 | { 251 | return 0; 252 | } 253 | return wallet.getTransactionsByTime().size(); 254 | } 255 | 256 | public String getAllTransactions(int max) 257 | { 258 | long transactionCount = getTransactionCount(); 259 | 260 | if(max > 0 && transactionCount > max) 261 | { 262 | // cut transactions 263 | return getTransactions(0, max); 264 | } 265 | return getTransactions(0, getTransactionCount()); 266 | } 267 | 268 | public String getTransaction(String tx) 269 | { 270 | if(wallet == null) 271 | { 272 | return null; 273 | } 274 | Sha256Hash hash = new Sha256Hash(tx); 275 | return getJSONFromTransaction(wallet.getTransaction(hash)); 276 | } 277 | 278 | public String getTransaction(int idx) 279 | { 280 | return getJSONFromTransaction(wallet.getTransactionsByTime().get(idx)); 281 | } 282 | 283 | public String getTransactions(int from, int count) 284 | { 285 | if(wallet == null) 286 | { 287 | return null; 288 | } 289 | List transactions = wallet.getTransactionsByTime(); 290 | 291 | if (from >= transactions.size()) 292 | return null; 293 | 294 | int to = (from + count < transactions.size()) ? from + count : transactions.size(); 295 | 296 | StringBuffer txs = new StringBuffer(); 297 | txs.append("[\n"); 298 | boolean first = true; 299 | for (; from < to; from++) 300 | { 301 | if (first) 302 | first = false; 303 | else 304 | txs.append("\n,"); 305 | 306 | txs.append(getJSONFromTransaction(transactions.get(from))); 307 | } 308 | txs.append("]\n"); 309 | 310 | return txs.toString(); 311 | } 312 | 313 | public String addKey() 314 | { 315 | boolean couldCreateKey = wallet.addKey(new ECKey()); 316 | if(couldCreateKey) 317 | { 318 | ECKey ecKey = wallet.getKeys().get(wallet.getKeys().size()-1); 319 | return ecKey.toAddress(networkParams).toString(); 320 | } 321 | return null; 322 | } 323 | 324 | public void clearSendRequest() 325 | { 326 | if(pendingSendRequest != null && pendingSendRequest.aesKey != null) 327 | { 328 | wipeAesKey(pendingSendRequest.aesKey); 329 | } 330 | pendingSendRequest = null; 331 | } 332 | 333 | public String commitSendRequest() 334 | { 335 | if(pendingSendRequest == null) 336 | { 337 | return ""; 338 | } 339 | 340 | try { 341 | wallet.commitTx(pendingSendRequest.tx); 342 | ListenableFuture future; 343 | if(broadcastMinTransactions < 0) 344 | { 345 | future = peerGroup.broadcastTransaction(pendingSendRequest.tx); 346 | } 347 | else 348 | { 349 | future = peerGroup.broadcastTransaction(pendingSendRequest.tx, broadcastMinTransactions); 350 | } 351 | 352 | Futures.addCallback(future, new FutureCallback() { 353 | public void onSuccess(Transaction transaction) { 354 | onTransactionSuccess(pendingSendRequest.tx.getHashAsString()); 355 | wipeAesKey(pendingSendRequest.aesKey); 356 | } 357 | 358 | public void onFailure(Throwable throwable) { 359 | onTransactionFailed(); 360 | wipeAesKey(pendingSendRequest.aesKey); 361 | } 362 | }); 363 | 364 | return pendingSendRequest.tx.getHashAsString(); 365 | 366 | } catch (Exception e) { 367 | return ""; 368 | } 369 | 370 | } 371 | 372 | /** 373 | * creates and stores a sendRequest and return the required fee 374 | */ 375 | public String createSendRequest(String amount, final String sendToAddressString) throws AddressFormatException, WrongPasswordException, InsufficientMoneyException 376 | { 377 | return createSendRequest(amount, sendToAddressString, null); 378 | } 379 | public String createSendRequest(String amount, final String sendToAddressString, char[] utf16Password) throws AddressFormatException, WrongPasswordException, InsufficientMoneyException 380 | { 381 | 382 | log.debug("creating send request " + amount); 383 | 384 | clearSendRequest(); 385 | 386 | KeyParameter aesKey = null; 387 | try { 388 | BigInteger value = new BigInteger(amount); 389 | Address sendToAddress = new Address(networkParams, sendToAddressString); 390 | 391 | pendingSendRequest = Wallet.SendRequest.to(sendToAddress, value); 392 | 393 | if (utf16Password != null && wallet != null && wallet.isEncrypted()) 394 | { 395 | aesKey = aesKeyForPassword(utf16Password); 396 | pendingSendRequest.aesKey = aesKey; 397 | } 398 | 399 | wallet.completeTx(pendingSendRequest); 400 | return pendingSendRequest.fee.toString(); 401 | 402 | } 403 | catch (InsufficientMoneyException e) 404 | { 405 | throw e; 406 | } 407 | catch (KeyCrypterException e) 408 | { 409 | wipeAesKey(aesKey); 410 | throw new WrongPasswordException(e); 411 | } 412 | catch (Exception e) 413 | { 414 | wipeAesKey(aesKey); 415 | onTransactionFailed(); 416 | } 417 | return null; 418 | } 419 | 420 | public boolean isAddressValid(String address) 421 | { 422 | try { 423 | Address addr = new Address(networkParams, address); 424 | return (addr != null); 425 | } 426 | catch (Exception e) 427 | { 428 | return false; 429 | } 430 | } 431 | 432 | /** 433 | * Returns the whole wallet file as base64 string to store into OS keychain, etc. 434 | */ 435 | public String getWalletFileBase64String() 436 | { 437 | if(wallet == null) 438 | { 439 | return null; 440 | } 441 | 442 | ByteArrayOutputStream stream = new ByteArrayOutputStream(); 443 | String base64Wallet = null; 444 | try { 445 | wallet.saveToFileStream(stream); 446 | base64Wallet = new String(Base64.encode(stream.toByteArray()), Charset.forName("UTF-8")); 447 | } catch (IOException e) { 448 | //TODO 449 | e.printStackTrace(); 450 | } 451 | 452 | return base64Wallet; 453 | } 454 | 455 | /** 456 | * returns the seconds (timestamp) of the last block creation time 457 | */ 458 | public long getLastBlockCreationTime() 459 | { 460 | if(chain != null) 461 | { 462 | StoredBlock chainHead = chain.getChainHead(); 463 | if(chainHead != null) 464 | { 465 | Block blockHeader = chainHead.getHeader(); 466 | if(blockHeader != null) 467 | { 468 | return blockHeader.getTimeSeconds(); 469 | } 470 | } 471 | } 472 | return 0; 473 | } 474 | 475 | /** 476 | * check if wallet is encrypted 477 | */ 478 | public boolean isWalletEncrypted() 479 | { 480 | if(wallet != null) 481 | { 482 | return wallet.isEncrypted(); 483 | } 484 | return false; 485 | } 486 | 487 | /** 488 | * encrypt your wallet 489 | */ 490 | private void encryptWallet(char[] utf16Password, Wallet wallet) 491 | { 492 | KeyCrypterScrypt keyCrypter = new KeyCrypterScrypt(); 493 | KeyParameter aesKey = deriveKeyAndWipePassword(utf16Password, keyCrypter); 494 | try 495 | { 496 | wallet.encrypt(keyCrypter, aesKey); 497 | } 498 | finally 499 | { 500 | wipeAesKey(aesKey); 501 | } 502 | } 503 | 504 | private KeyParameter aesKeyForPassword(char[] utf16Password) throws WrongPasswordException 505 | { 506 | KeyCrypter keyCrypter = wallet.getKeyCrypter(); 507 | if (keyCrypter == null) 508 | { 509 | throw new WrongPasswordException("Wallet is not protected."); 510 | } 511 | return deriveKeyAndWipePassword(utf16Password, keyCrypter); 512 | } 513 | 514 | private KeyParameter deriveKeyAndWipePassword(char[] utf16Password, KeyCrypter keyCrypter) 515 | { 516 | try 517 | { 518 | return keyCrypter.deriveKey(CharBuffer.wrap(utf16Password)); 519 | } 520 | finally 521 | { 522 | Arrays.fill(utf16Password, '\0'); 523 | } 524 | } 525 | 526 | private void wipeAesKey(KeyParameter aesKey) 527 | { 528 | if (aesKey != null) 529 | { 530 | Arrays.fill(aesKey.getKey(), (byte) 0); 531 | } 532 | } 533 | 534 | /** 535 | * decrypt your wallet 536 | */ 537 | private void decryptWallet(char[] oldUtf16Password) throws WrongPasswordException 538 | { 539 | KeyParameter oldAesKey = aesKeyForPassword(oldUtf16Password); 540 | try 541 | { 542 | wallet.decrypt(oldAesKey); 543 | } 544 | catch (KeyCrypterException e) 545 | { 546 | throw new WrongPasswordException(e); 547 | } 548 | finally 549 | { 550 | wipeAesKey(oldAesKey); 551 | } 552 | } 553 | 554 | public void changeWalletPassword(char[] oldUtf16Password, char[] newUtf16Password) throws WrongPasswordException 555 | { 556 | updateLastWalletChange(wallet); 557 | 558 | if (isWalletEncrypted()) 559 | { 560 | decryptWallet(oldUtf16Password); 561 | } 562 | 563 | encryptWallet(newUtf16Password, wallet); 564 | } 565 | 566 | /** 567 | * decrypt your wallet 568 | */ 569 | public String getWalletDump(String passphrase) 570 | { 571 | try 572 | { 573 | StringBuilder walletDump = new StringBuilder(2048); 574 | List keys = wallet.getKeys();// toStringWithPrivate() 575 | for(ECKey key: keys) 576 | { 577 | ECKey keyToPlayWith = key; 578 | 579 | if(wallet.isEncrypted() && passphrase != null) 580 | { 581 | org.spongycastle.crypto.params.KeyParameter keyParams = wallet.getKeyCrypter().deriveKey(passphrase); 582 | 583 | ECKey decryptedKey = key.decrypt(wallet.getKeyCrypter(),keyParams); 584 | if(decryptedKey != null) 585 | { 586 | keyToPlayWith = decryptedKey; 587 | } 588 | } 589 | 590 | walletDump.append(keyToPlayWith.toStringWithPrivate()+"\n"); 591 | } 592 | 593 | return walletDump.toString(); 594 | } 595 | catch (Exception e) 596 | { 597 | return null; 598 | } 599 | } 600 | 601 | /** 602 | * save your wallet 603 | */ 604 | public void saveWallet() 605 | { 606 | if(wallet != null) 607 | { 608 | try 609 | { 610 | wallet.saveToFile(walletFile); 611 | } 612 | catch (Exception e) 613 | { 614 | // TODO: error case 615 | } 616 | } 617 | } 618 | 619 | public void createWallet(char[] utf16Password) throws IOException, BlockStoreException, ExistingWalletException 620 | { 621 | if (walletFile == null) 622 | { 623 | walletFile = new File(dataDirectory + "/"+ appName +".wallet"); 624 | } 625 | else if (walletFile.exists()) 626 | { 627 | throw new ExistingWalletException("Trying to create a wallet even though one exists: " + walletFile); 628 | } 629 | 630 | wallet = new Wallet(networkParams); 631 | wallet.addExtension(new LastWalletChangeExtension()); 632 | updateLastWalletChange(wallet); 633 | wallet.addKey(new ECKey()); 634 | 635 | if (utf16Password != null) 636 | { 637 | encryptWallet(utf16Password, wallet); 638 | } 639 | 640 | wallet.saveToFile(walletFile); 641 | 642 | useWallet(wallet); 643 | } 644 | 645 | private void useWallet(Wallet wallet) throws IOException 646 | { 647 | this.wallet = wallet; 648 | 649 | //make wallet autosave 650 | wallet.autosaveToFile(walletFile, 1, TimeUnit.SECONDS, null); 651 | 652 | // We want to know when the balance changes. 653 | wallet.addEventListener(new AbstractWalletEventListener() { 654 | @Override 655 | public void onCoinsReceived(Wallet w, Transaction tx, BigInteger prevBalance, BigInteger newBalance) { 656 | assert !newBalance.equals(BigInteger.ZERO); 657 | 658 | // TODO: check if the isPending thing is required 659 | if (!tx.isPending()) return; 660 | 661 | onHICoinsReceived(tx.getHashAsString()); 662 | } 663 | 664 | @Override 665 | public void onWalletChanged(Wallet wallet) { 666 | onHIWalletChanged(); 667 | } 668 | 669 | @Override 670 | public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx) 671 | { 672 | onTransactionChanged(tx.getHashAsString()); 673 | } 674 | 675 | }); 676 | } 677 | 678 | 679 | /* --- Keeping last wallet change date --- */ 680 | 681 | public void updateLastWalletChange(Wallet wallet) 682 | { 683 | LastWalletChangeExtension ext = 684 | (LastWalletChangeExtension) wallet.getExtensions().get(LastWalletChangeExtension.EXTENSION_ID); 685 | 686 | ext.setLastWalletChangeDate(new Date()); 687 | } 688 | 689 | public Date getLastWalletChange() 690 | { 691 | if (wallet == null) 692 | { 693 | return null; 694 | } 695 | 696 | LastWalletChangeExtension ext = 697 | (LastWalletChangeExtension) wallet.getExtensions().get(LastWalletChangeExtension.EXTENSION_ID); 698 | 699 | return ext.getLastWalletChangeDate(); 700 | } 701 | 702 | public long getLastWalletChangeTimestamp() 703 | { 704 | Date date = getLastWalletChange(); 705 | return (date != null) ? date.getTime() : 0; 706 | } 707 | 708 | 709 | /** 710 | * start the bitcoinj app layer 711 | */ 712 | public void loadWallet() throws NoWalletException,UnreadableWalletException, IOException 713 | { 714 | if(wallet == null) 715 | { 716 | // if no wallet is loaded, try to load the default wallet 717 | try { 718 | if (walletFile == null) 719 | { 720 | walletFile = new File(dataDirectory + "/"+ appName +".wallet"); 721 | } 722 | if (walletFile.exists()) 723 | { 724 | wallet = Wallet.loadFromFile(walletFile); 725 | wallet.addExtension(new LastWalletChangeExtension()); 726 | useWallet(wallet); 727 | } 728 | else { 729 | throw new NoWalletException("No wallet file found at: " + walletFile); 730 | } 731 | } catch (UnreadableWalletException e) { 732 | throw e; 733 | } 734 | } 735 | } 736 | 737 | /** 738 | * start the bitcoinj app layer 739 | */ 740 | public void startBlockchain() throws BlockStoreException, NoWalletException,UnreadableWalletException, IOException 741 | { 742 | 743 | File chainFile = new File(dataDirectory + "/" + appName + ".spvchain"); 744 | storedChainHeight = 0; 745 | 746 | loadWallet(); 747 | 748 | if(!chainFile.exists()) 749 | { 750 | wallet.clearTransactions(0); 751 | } 752 | 753 | // get the oldest key (for the checkpoint file) 754 | long oldestKey = 0; 755 | for(ECKey key: wallet.getKeys()) 756 | { 757 | long keyAge = key.getCreationTimeSeconds(); 758 | if(oldestKey == 0 || keyAge < oldestKey) 759 | { 760 | oldestKey = keyAge; 761 | } 762 | } 763 | 764 | String oldestKeyString = String.valueOf(oldestKey); 765 | System.err.println("+++oldest key: "+oldestKeyString); 766 | 767 | // Load the block chain, if there is one stored locally. If it's going to be freshly created, checkpoint it. 768 | boolean chainExistedAlready = chainFile.exists(); 769 | blockStore = new SPVBlockStore(networkParams, chainFile); 770 | if (!chainExistedAlready && oldestKey > 0) { 771 | File checkpointsFile = new File(dataDirectory + "/" + appName + ".checkpoints"); 772 | if (checkpointsFile.exists()) { 773 | System.err.println("+++using the checkpoint file"); 774 | try { 775 | FileInputStream stream = new FileInputStream(checkpointsFile); 776 | CheckpointManager.checkpoint(networkParams, stream, blockStore, oldestKey); 777 | } 778 | catch (Exception e) { 779 | // TODO 780 | } 781 | } 782 | } 783 | 784 | chain = new BlockChain(networkParams, wallet, blockStore); 785 | // Connect to the localhost node. One minute timeout since we won't try any other peers 786 | 787 | 788 | peerGroup = new PeerGroup(networkParams, chain); 789 | 790 | try { 791 | if (networkParams == RegTestParams.get()) { 792 | peerGroup.addAddress(InetAddress.getLocalHost()); 793 | } else { 794 | peerGroup.addPeerDiscovery(new DnsDiscovery(networkParams)); 795 | } 796 | } 797 | catch (Exception e) { 798 | // TODO 799 | } 800 | peerGroup.addEventListener(new AbstractPeerEventListener() { 801 | @Override 802 | public void onPeerConnected(Peer peer, int peerCount) { 803 | super.onPeerConnected(peer, peerCount); 804 | onPeerCountChanged(peerCount); 805 | 806 | // inform app about the expected height 807 | onSynchronizationUpdate(-1, -1, peerGroup.getMostCommonChainHeight()); 808 | } 809 | 810 | @Override 811 | public void onPeerDisconnected(Peer peer, int peerCount) { 812 | super.onPeerDisconnected(peer, peerCount); 813 | onPeerCountChanged(peerCount); 814 | } 815 | }); 816 | 817 | peerGroup.addWallet(wallet); 818 | 819 | 820 | // inform the app over the current chains height; if there is a chain and already loaded blocks 821 | if(chain != null) 822 | { 823 | StoredBlock chainHead = chain.getChainHead(); 824 | if(chainHead != null) 825 | { 826 | storedChainHeight = chainHead.getHeight(); 827 | onSynchronizationUpdate(0.0, storedChainHeight, -1); 828 | } 829 | } 830 | 831 | peerGroup.start(); 832 | 833 | // inform about the balance 834 | onBalanceChanged(); 835 | 836 | peerGroup.startBlockChainDownload(this); 837 | } 838 | 839 | /** 840 | * stop the bitcoinj app layer 841 | */ 842 | public void stop() 843 | { 844 | try { 845 | System.out.print("Shutting down ... "); 846 | peerGroup.stopAndWait(); 847 | wallet.saveToFile(walletFile); 848 | blockStore.close(); 849 | System.out.print("done "); 850 | } catch (Exception e) { 851 | throw new RuntimeException(e); 852 | } 853 | } 854 | 855 | /** 856 | * TODO 857 | */ 858 | public void walletExport(String path) 859 | { 860 | 861 | } 862 | 863 | /* Implementing native callbacks here */ 864 | 865 | public native void onTransactionChanged(String txid); 866 | 867 | public native void onTransactionFailed(); 868 | 869 | public native void onTransactionSuccess(String txid); 870 | 871 | public native void onHICoinsReceived(String txid); 872 | 873 | public native void onHIWalletChanged(); 874 | 875 | public native void onSynchronizationUpdate(double progress, long blockCount, long blockHeight); 876 | 877 | public native void onPeerCountChanged(int peersConnected); 878 | 879 | public native void onBalanceChanged(); 880 | 881 | public native void onException(Throwable exception); 882 | 883 | /* Implementing peer listener */ 884 | public void onBlocksDownloaded(Peer peer, Block block, int blocksLeft) 885 | { 886 | int downloadedSoFar = blocksToDownload - blocksLeft; 887 | if (blocksToDownload == 0) 888 | { 889 | onSynchronizationUpdate(1.0, storedChainHeight+downloadedSoFar, -1); 890 | } 891 | else 892 | { 893 | // report after every 100 block 894 | if(blocksLeft % 100 == 0) 895 | { 896 | long currentChainHeight = -1; // -1 = no change (by default) 897 | StoredBlock chainHead = chain.getChainHead(); 898 | if(chainHead != null) 899 | { 900 | currentChainHeight = chainHead.getHeight(); 901 | } 902 | 903 | double progress = (double)downloadedSoFar / (double)blocksToDownload; 904 | onSynchronizationUpdate(progress, currentChainHeight, -1); 905 | } 906 | } 907 | } 908 | 909 | public void onChainDownloadStarted(Peer peer, int blocksLeft) 910 | { 911 | long currentChainHeight = -1; // -1 = no change (by default) 912 | StoredBlock chainHead = chain.getChainHead(); 913 | if(chainHead != null) 914 | { 915 | currentChainHeight = chainHead.getHeight(); 916 | } 917 | 918 | blocksToDownload = blocksLeft; 919 | if (blocksToDownload == 0) 920 | onSynchronizationUpdate(1.0, currentChainHeight, -1); 921 | else 922 | onSynchronizationUpdate(0.0, -1, -1); 923 | } 924 | 925 | public void onPeerConnected(Peer peer, int peerCount) 926 | { 927 | } 928 | 929 | public void onPeerDisconnected(Peer peer, int peerCount) 930 | { 931 | } 932 | 933 | public Message onPreMessageReceived(Peer peer, Message m) 934 | { 935 | return m; 936 | } 937 | 938 | public void onTransaction(Peer peer, Transaction t) 939 | { 940 | 941 | } 942 | 943 | /* --- TransactionConfidence.Listener --- */ 944 | 945 | private void trackPendingTransactions(Wallet wallet) 946 | { 947 | // we won't receive onCoinsReceived again for transactions that we already know about, 948 | // so we need to listen to confidence changes again after a restart 949 | for (Transaction tx : wallet.getPendingTransactions()) 950 | { 951 | trackTransaction(tx); 952 | } 953 | } 954 | 955 | private void trackTransaction(Transaction tx) 956 | { 957 | if (!trackedTransactions.contains(tx)) 958 | { 959 | log.debug("Tracking transaction " + tx.getHashAsString()); 960 | 961 | tx.getConfidence().addEventListener(this); 962 | trackedTransactions.add(tx); 963 | } 964 | } 965 | 966 | private void stopTrackingTransaction(Transaction tx) 967 | { 968 | if (trackedTransactions.contains(tx)) 969 | { 970 | log.debug("Stopped tracking transaction " + tx.getHashAsString()); 971 | 972 | tx.getConfidence().removeEventListener(this); 973 | trackedTransactions.remove(tx); 974 | } 975 | } 976 | 977 | public void onConfidenceChanged(final Transaction tx, TransactionConfidence.Listener.ChangeReason reason) 978 | { 979 | if (!tx.isPending()) 980 | { 981 | // coins were confirmed (appeared in a block) - we don't need to listen anymore 982 | stopTrackingTransaction(tx); 983 | } 984 | 985 | // update the UI 986 | onTransactionChanged(tx.getHashAsString()); 987 | } 988 | 989 | public List getData(Peer peer, GetDataMessage m) 990 | { 991 | return null; 992 | } 993 | } 994 | -------------------------------------------------------------------------------- /BitcoinJKit/HIBitcoinManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // HIBitcoinManager.m 3 | // BitcoinKit 4 | // 5 | // Created by Bazyli Zygan on 26.07.2013. 6 | // Extended by Jonas Schnelli 2013 7 | // 8 | // Copyright (c) 2013 Hive Developers. All rights reserved. 9 | // 10 | 11 | #import "HIBitcoinManager.h" 12 | #import "HIBitcoinErrorCodes.h" 13 | #import "HIBitcoinInternalErrorCodes.h" 14 | 15 | #import 16 | 17 | @interface HIBitcoinManager () 18 | { 19 | JavaVM *_vm; 20 | JNIEnv *_jniEnv; 21 | JavaVMInitArgs _vmArgs; 22 | jobject _managerObject; 23 | jclass _managerClass; 24 | NSDateFormatter *_dateFormatter; 25 | BOOL _sending; 26 | 27 | uint64_t _lastBalance; 28 | NSTimer *_balanceChecker; 29 | 30 | NSString *_appSupportDirectoryIdentifier; 31 | } 32 | 33 | @property (nonatomic, strong) NSArray *availableBitcoinFormats; 34 | 35 | - (jclass)jClassForClass:(NSString *)class; 36 | - (void)onBalanceChanged; 37 | - (void)onSynchronizationChanged:(double)progress blockCount:(long)blockCount totalBlocks:(long)totalBlocks; 38 | - (void)onPeerCountChanged:(int)peerCount; 39 | - (void)onTransactionChanged:(NSString *)txid; 40 | - (void)onTransactionSucceeded:(NSString *)txid; 41 | - (void)onCoinsReceived:(NSString *)txid; 42 | - (void)onWalletChanged; 43 | - (void)onTransactionFailed; 44 | - (void)handleJavaException:(jthrowable)exception useExceptionHandler:(BOOL)useHandler error:(NSError **)returnedError; 45 | - (void)checkBalance:(NSTimer *)timer; 46 | 47 | - (uint64_t)balance:(int)type; 48 | 49 | @property (strong) void(^sendCompletionBlock)(NSString *hash); 50 | 51 | @end 52 | 53 | #pragma mark - Helper functions for conversion 54 | 55 | NSString * NSStringFromJString(JNIEnv *env, jstring javaString) 56 | { 57 | const char *chars = (*env)->GetStringUTFChars(env, javaString, NULL); 58 | NSString *objcString = [NSString stringWithUTF8String:chars]; 59 | (*env)->ReleaseStringUTFChars(env, javaString, chars); 60 | 61 | return objcString; 62 | } 63 | 64 | jstring JStringFromNSString(JNIEnv *env, NSString *string) 65 | { 66 | return (*env)->NewStringUTF(env, [string UTF8String]); 67 | } 68 | 69 | jarray JCharArrayFromNSData(JNIEnv *env, NSData *data) 70 | { 71 | jsize length = (jsize)(data.length / sizeof(jchar)); 72 | jcharArray charArray = (*env)->NewCharArray(env, length); 73 | (*env)->SetCharArrayRegion(env, charArray, 0, length, data.bytes); 74 | return charArray; 75 | } 76 | 77 | JNIEXPORT void JNICALL onBalanceChanged (JNIEnv *env, jobject thisobject) 78 | { 79 | NSAutoreleasePool *pool = [NSAutoreleasePool new]; 80 | [[HIBitcoinManager defaultManager] onBalanceChanged]; 81 | [pool release]; 82 | } 83 | 84 | 85 | JNIEXPORT void JNICALL onPeerCountChanged (JNIEnv *env, jobject thisobject, jint peerCount) 86 | { 87 | NSAutoreleasePool *pool = [NSAutoreleasePool new]; 88 | [[HIBitcoinManager defaultManager] onPeerCountChanged:(int)peerCount]; 89 | [pool release]; 90 | } 91 | 92 | JNIEXPORT void JNICALL onSynchronizationUpdate (JNIEnv *env, jobject thisobject, jdouble progress, jlong blockCount, jlong totalBlocks) 93 | { 94 | NSAutoreleasePool *pool = [NSAutoreleasePool new]; 95 | [[HIBitcoinManager defaultManager] onSynchronizationChanged:(double)progress blockCount:blockCount totalBlocks:totalBlocks]; 96 | [pool release]; 97 | } 98 | 99 | JNIEXPORT void JNICALL onTransactionChanged (JNIEnv *env, jobject thisobject, jstring txid) 100 | { 101 | NSAutoreleasePool *pool = [NSAutoreleasePool new]; 102 | if (txid) 103 | { 104 | const char *txc = (*env)->GetStringUTFChars(env, txid, NULL); 105 | 106 | NSString *bStr = [NSString stringWithUTF8String:txc]; 107 | (*env)->ReleaseStringUTFChars(env, txid, txc); 108 | [[HIBitcoinManager defaultManager] onTransactionChanged:bStr]; 109 | 110 | } 111 | 112 | [pool release]; 113 | } 114 | 115 | 116 | JNIEXPORT void JNICALL onWalletChanged (JNIEnv *env, jobject thisobject) 117 | { 118 | NSAutoreleasePool *pool = [NSAutoreleasePool new]; 119 | [[HIBitcoinManager defaultManager] onWalletChanged]; 120 | [pool release]; 121 | } 122 | 123 | JNIEXPORT void JNICALL onCoinsReceived (JNIEnv *env, jobject thisobject, jstring txid) 124 | { 125 | NSAutoreleasePool *pool = [NSAutoreleasePool new]; 126 | if (txid) 127 | { 128 | const char *txc = (*env)->GetStringUTFChars(env, txid, NULL); 129 | 130 | NSString *bStr = [NSString stringWithUTF8String:txc]; 131 | (*env)->ReleaseStringUTFChars(env, txid, txc); 132 | [[HIBitcoinManager defaultManager] onCoinsReceived:bStr]; 133 | 134 | } 135 | 136 | [pool release]; 137 | } 138 | 139 | JNIEXPORT void JNICALL onTransactionSucceeded (JNIEnv *env, jobject thisobject, jstring txid) 140 | { 141 | NSAutoreleasePool *pool = [NSAutoreleasePool new]; 142 | if (txid) 143 | { 144 | const char *txc = (*env)->GetStringUTFChars(env, txid, NULL); 145 | 146 | NSString *bStr = [NSString stringWithUTF8String:txc]; 147 | (*env)->ReleaseStringUTFChars(env, txid, txc); 148 | [[HIBitcoinManager defaultManager] onTransactionSucceeded:bStr]; 149 | } 150 | 151 | [pool release]; 152 | } 153 | 154 | JNIEXPORT void JNICALL onException(JNIEnv *env, jobject thisobject, jthrowable jexception) 155 | { 156 | NSAutoreleasePool *pool = [NSAutoreleasePool new]; 157 | [[HIBitcoinManager defaultManager] handleJavaException:jexception useExceptionHandler:YES error:NULL]; 158 | [pool release]; 159 | } 160 | 161 | JNIEXPORT void JNICALL onTransactionFailed (JNIEnv *env, jobject thisobject) 162 | { 163 | NSAutoreleasePool *pool = [NSAutoreleasePool new]; 164 | [[HIBitcoinManager defaultManager] onTransactionFailed]; 165 | 166 | [pool release]; 167 | } 168 | 169 | JNIEXPORT void JNICALL receiveLogFromJVM(JNIEnv *env, jobject thisobject, jstring fileName, jstring methodName, 170 | int lineNumber, jint level, jstring msg) 171 | { 172 | NSAutoreleasePool *pool = [NSAutoreleasePool new]; 173 | const char *fileNameString = (*env)->GetStringUTFChars(env, fileName, NULL); 174 | const char *methodNameString = (*env)->GetStringUTFChars(env, methodName, NULL); 175 | 176 | NSLog(@"%@", (NSString *)NSStringFromJString(env, msg)); 177 | 178 | (*env)->ReleaseStringUTFChars(env, fileName, fileNameString); 179 | (*env)->ReleaseStringUTFChars(env, methodName, methodNameString); 180 | [pool release]; 181 | } 182 | 183 | static JNINativeMethod methods[] = { 184 | {"onBalanceChanged", "()V", (void *)&onBalanceChanged}, 185 | {"onTransactionChanged", "(Ljava/lang/String;)V", (void *)&onTransactionChanged}, 186 | {"onHICoinsReceived", "(Ljava/lang/String;)V", (void *)&onCoinsReceived}, 187 | {"onHIWalletChanged", "()V", (void *)&onWalletChanged}, 188 | {"onTransactionSuccess", "(Ljava/lang/String;)V", (void *)&onTransactionSucceeded}, 189 | {"onTransactionFailed", "()V", (void *)&onTransactionFailed}, 190 | {"onPeerCountChanged", "(I)V", (void *)&onPeerCountChanged}, 191 | {"onSynchronizationUpdate", "(DJJ)V", (void *)&onSynchronizationUpdate} 192 | }; 193 | 194 | NSString * const kHIBitcoinManagerTransactionChangedNotification = @"kJHIBitcoinManagerTransactionChangedNotification"; 195 | NSString * const kHIBitcoinManagerStartedNotification = @"kJHIBitcoinManagerStartedNotification"; 196 | NSString * const kHIBitcoinManagerStoppedNotification = @"kJHIBitcoinManagerStoppedNotification"; 197 | 198 | #define kHIDefaultAppSupportIdentifier @"com.Hive.BitcoinJKit" 199 | #define kHIDefaultAppName @"bitcoinkit" 200 | 201 | static HIBitcoinManager *_defaultManager = nil; 202 | 203 | @implementation HIBitcoinManager 204 | 205 | @synthesize appName = _appName; 206 | @synthesize dataURL = _dataURL; 207 | @synthesize connections = _connections; 208 | @synthesize isRunning = _isRunning; 209 | @synthesize balance = _balance; 210 | @synthesize syncProgress = _syncProgress; 211 | @synthesize peerCount = _peerCount; 212 | @synthesize testingNetwork = _testingNetwork; 213 | @synthesize walletAddress; 214 | @synthesize currentBlockCount=_currentBlockCount; 215 | @synthesize totalBlocks=_totalBlocks; 216 | 217 | + (HIBitcoinManager *)defaultManager 218 | { 219 | static dispatch_once_t oncePredicate; 220 | if (!_defaultManager) 221 | dispatch_once(&oncePredicate, ^{ 222 | _defaultManager = [[self alloc] init]; 223 | }); 224 | 225 | return _defaultManager; 226 | } 227 | 228 | - (jclass)jClassForClass:(NSString *)class 229 | { 230 | jclass cls = (*_jniEnv)->FindClass(_jniEnv, [class UTF8String]); 231 | 232 | if ((*_jniEnv)->ExceptionCheck(_jniEnv)) 233 | { 234 | (*_jniEnv)->ExceptionDescribe(_jniEnv); 235 | (*_jniEnv)->ExceptionClear(_jniEnv); 236 | 237 | @throw [NSException exceptionWithName:@"Java exception" reason:@"Java VM raised an exception" userInfo:@{@"class": class}]; 238 | } 239 | return cls; 240 | } 241 | 242 | - (jmethodID)jMethodWithName:(char *)name signature:(char *)signature 243 | { 244 | jmethodID method = (*_jniEnv)->GetMethodID(_jniEnv, _managerClass, name, signature); 245 | 246 | if (method == NULL) 247 | { 248 | @throw [NSException exceptionWithName:@"Java exception" 249 | reason:[NSString stringWithFormat:@"Method not found: %s (%s)", name, signature] 250 | userInfo:nil]; 251 | } 252 | 253 | return method; 254 | } 255 | 256 | - (BOOL)callBooleanMethodWithName:(char *)name signature:(char *)signature, ... 257 | { 258 | jmethodID method = [self jMethodWithName:name signature:signature]; 259 | 260 | va_list args; 261 | va_start(args, signature); 262 | jboolean result = (*_jniEnv)->CallBooleanMethodV(_jniEnv, _managerObject, method, args); 263 | va_end(args); 264 | 265 | [self handleJavaExceptions:NULL]; 266 | 267 | return result; 268 | } 269 | 270 | - (int)callIntegerMethodWithName:(char *)name signature:(char *)signature, ... 271 | { 272 | jmethodID method = [self jMethodWithName:name signature:signature]; 273 | 274 | va_list args; 275 | va_start(args, signature); 276 | jint result = (*_jniEnv)->CallIntMethodV(_jniEnv, _managerObject, method, args); 277 | va_end(args); 278 | 279 | [self handleJavaExceptions:NULL]; 280 | 281 | return result; 282 | } 283 | 284 | - (long)callLongMethodWithName:(char *)name signature:(char *)signature, ... 285 | { 286 | jmethodID method = [self jMethodWithName:name signature:signature]; 287 | 288 | va_list args; 289 | va_start(args, signature); 290 | jlong result = (*_jniEnv)->CallLongMethodV(_jniEnv, _managerObject, method, args); 291 | va_end(args); 292 | 293 | [self handleJavaExceptions:NULL]; 294 | 295 | return result; 296 | } 297 | 298 | - (jobject)callObjectMethodWithName:(char *)name error:(NSError **)error signature:(char *)signature, ... 299 | { 300 | jmethodID method = [self jMethodWithName:name signature:signature]; 301 | 302 | va_list args; 303 | va_start(args, signature); 304 | jobject result = (*_jniEnv)->CallObjectMethodV(_jniEnv, _managerObject, method, args); 305 | va_end(args); 306 | 307 | [self handleJavaExceptions:error]; 308 | 309 | return result; 310 | } 311 | 312 | - (void)callVoidMethodWithName:(char *)name error:(NSError **)error signature:(char *)signature, ... 313 | { 314 | jmethodID method = [self jMethodWithName:name signature:signature]; 315 | 316 | va_list args; 317 | va_start(args, signature); 318 | (*_jniEnv)->CallVoidMethodV(_jniEnv, _managerObject, method, args); 319 | va_end(args); 320 | 321 | [self handleJavaExceptions:error]; 322 | } 323 | 324 | 325 | - (void)handleJavaExceptions:(NSError **)error 326 | { 327 | if ((*_jniEnv)->ExceptionCheck(_jniEnv)) 328 | { 329 | // get the exception object 330 | jthrowable exception = (*_jniEnv)->ExceptionOccurred(_jniEnv); 331 | 332 | [self handleJavaException:exception useExceptionHandler:NO error:error]; 333 | } 334 | else if (error) 335 | { 336 | *error = nil; 337 | } 338 | } 339 | 340 | - (void)handleJavaException:(jthrowable)exception useExceptionHandler:(BOOL)useHandler error:(NSError **)returnedError 341 | { 342 | BOOL callerWantsToHandleErrors = returnedError != nil; 343 | 344 | if (callerWantsToHandleErrors) 345 | { 346 | *returnedError = nil; 347 | } 348 | 349 | // exception has to be cleared if it exists 350 | (*_jniEnv)->ExceptionClear(_jniEnv); 351 | 352 | // try to get exception details from Java 353 | // note: we need to do this on the main thread - if this is called from a background thread, 354 | // the toString() call returns nil and throws a new exception (java.lang.StackOverflowException) 355 | dispatch_block_t processException = ^{ 356 | NSError *error = [NSError errorWithDomain:@"BitcoinKit" 357 | code:[self errorCodeForJavaException:exception] 358 | userInfo:[self createUserInfoForJavaException:exception]]; 359 | 360 | NSString *exceptionClass = [self getJavaExceptionClassName:exception]; 361 | 362 | NSLog(@"Java exception caught (%@): %@\n%@", 363 | exceptionClass, 364 | error.userInfo[NSLocalizedFailureReasonErrorKey], 365 | error.userInfo[@"stackTrace"] ?: @""); 366 | 367 | if (callerWantsToHandleErrors && error.code != kHIBitcoinManagerUnexpectedError) 368 | { 369 | *returnedError = error; 370 | } 371 | else 372 | { 373 | NSException *exception = [NSException exceptionWithName:@"Java exception" 374 | reason:error.userInfo[NSLocalizedFailureReasonErrorKey] 375 | userInfo:error.userInfo]; 376 | if (useHandler && self.exceptionHandler) 377 | { 378 | self.exceptionHandler(exception); 379 | } 380 | else 381 | { 382 | @throw exception; 383 | } 384 | } 385 | }; 386 | 387 | if (dispatch_get_current_queue() != dispatch_get_main_queue()) 388 | { 389 | // run the above code synchronously on the main thread, 390 | // otherwise Java GC can clean up the exception object and we get a memory access error 391 | dispatch_sync(dispatch_get_main_queue(), processException); 392 | } 393 | else 394 | { 395 | // if this is the main thread, we can't use dispatch_sync or the whole thing will lock up 396 | processException(); 397 | } 398 | } 399 | 400 | - (NSInteger)errorCodeForJavaException:(jthrowable)exception 401 | { 402 | NSString *exceptionClass = [self getJavaExceptionClassName:exception]; 403 | if ([exceptionClass isEqual:@"com.google.bitcoin.core.InsufficientMoneyException"]) 404 | { 405 | return kHIBitcoinManagerInsufficientMoney; 406 | } 407 | if ([exceptionClass isEqual:@"com.google.bitcoin.store.UnreadableWalletException"]) 408 | { 409 | return kHIBitcoinManagerUnreadableWallet; 410 | } 411 | else if ([exceptionClass isEqual:@"com.google.bitcoin.store.BlockStoreException"]) 412 | { 413 | return kHIBitcoinManagerBlockStoreError; 414 | } 415 | else if ([exceptionClass isEqual:@"java.lang.IllegalArgumentException"]) 416 | { 417 | return kHIIllegalArgumentException; 418 | } 419 | else if ([exceptionClass isEqual:@"com.hive.bitcoinkit.NoWalletException"]) 420 | { 421 | return kHIBitcoinManagerNoWallet; 422 | } 423 | else if ([exceptionClass isEqual:@"com.hive.bitcoinkit.ExistingWalletException"]) 424 | { 425 | return kHIBitcoinManagerWalletExists; 426 | } 427 | else if ([exceptionClass isEqual:@"com.hive.bitcoinkit.WrongPasswordException"]) 428 | { 429 | return kHIBitcoinManagerWrongPassword; 430 | } 431 | else 432 | { 433 | return kHIBitcoinManagerUnexpectedError; 434 | } 435 | } 436 | 437 | - (NSString *)getJavaExceptionClassName:(jthrowable)exception 438 | { 439 | jclass exceptionClass = (*_jniEnv)->GetObjectClass(_jniEnv, exception); 440 | jmethodID getClassMethod = (*_jniEnv)->GetMethodID(_jniEnv, exceptionClass, "getClass", "()Ljava/lang/Class;"); 441 | jobject classObject = (*_jniEnv)->CallObjectMethod(_jniEnv, exception, getClassMethod); 442 | jobject class = (*_jniEnv)->GetObjectClass(_jniEnv, classObject); 443 | jmethodID getNameMethod = (*_jniEnv)->GetMethodID(_jniEnv, class, "getName", "()Ljava/lang/String;"); 444 | jstring name = (*_jniEnv)->CallObjectMethod(_jniEnv, exceptionClass, getNameMethod); 445 | return NSStringFromJString(_jniEnv, name); 446 | } 447 | 448 | - (NSDictionary *)createUserInfoForJavaException:(jthrowable)exception 449 | { 450 | NSMutableDictionary *userInfo = [NSMutableDictionary new]; 451 | userInfo[NSLocalizedFailureReasonErrorKey] = [self getJavaExceptionMessage:exception] ?: @"Java VM raised an exception"; 452 | 453 | NSString *stackTrace = [self getJavaExceptionStackTrace:exception]; 454 | if (stackTrace) 455 | { 456 | userInfo[@"stackTrace"] = stackTrace; 457 | } 458 | return userInfo; 459 | } 460 | 461 | - (NSString *)getJavaExceptionMessage:(jthrowable)exception 462 | { 463 | jclass exceptionClass = (*_jniEnv)->GetObjectClass(_jniEnv, exception); 464 | 465 | if (exceptionClass) 466 | { 467 | jmethodID toStringMethod = (*_jniEnv)->GetMethodID(_jniEnv, exceptionClass, "toString", "()Ljava/lang/String;"); 468 | 469 | if (toStringMethod) 470 | { 471 | jstring description = (*_jniEnv)->CallObjectMethod(_jniEnv, exception, toStringMethod); 472 | 473 | if ((*_jniEnv)->ExceptionCheck(_jniEnv)) 474 | { 475 | (*_jniEnv)->ExceptionDescribe(_jniEnv); 476 | (*_jniEnv)->ExceptionClear(_jniEnv); 477 | } 478 | 479 | if (description) 480 | { 481 | return NSStringFromJString(_jniEnv, description); 482 | } 483 | } 484 | } 485 | 486 | return nil; 487 | } 488 | 489 | - (NSString *)getJavaExceptionStackTrace:(jthrowable)exception 490 | { 491 | jmethodID stackTraceMethod = (*_jniEnv)->GetMethodID(_jniEnv, _managerClass, "getExceptionStackTrace", 492 | "(Ljava/lang/Throwable;)Ljava/lang/String;"); 493 | 494 | if (stackTraceMethod) 495 | { 496 | jstring stackTrace = (*_jniEnv)->CallObjectMethod(_jniEnv, _managerObject, stackTraceMethod, exception); 497 | 498 | if ((*_jniEnv)->ExceptionCheck(_jniEnv)) 499 | { 500 | (*_jniEnv)->ExceptionDescribe(_jniEnv); 501 | (*_jniEnv)->ExceptionClear(_jniEnv); 502 | } 503 | 504 | if (stackTrace) 505 | { 506 | return NSStringFromJString(_jniEnv, stackTrace); 507 | } 508 | } 509 | 510 | return nil; 511 | } 512 | 513 | - (void)setAppSupportDirectoryIdentifier:(NSString *)appSupportDirectoryIdentifier 514 | { 515 | if(_appSupportDirectoryIdentifier != appSupportDirectoryIdentifier) 516 | { 517 | [_appSupportDirectoryIdentifier release]; 518 | _appSupportDirectoryIdentifier = [appSupportDirectoryIdentifier retain]; 519 | 520 | // set the new data url 521 | self.dataURL = [[[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:self.appSupportDirectoryIdentifier]; 522 | } 523 | } 524 | 525 | - (NSString *)appSupportDirectoryIdentifier 526 | { 527 | return _appSupportDirectoryIdentifier; 528 | } 529 | 530 | - (void)createWalletWithPassword:(NSData *)password 531 | error:(NSError **)error 532 | { 533 | jarray charArray = JCharArrayFromNSData(_jniEnv, password); 534 | 535 | *error = nil; 536 | [self callVoidMethodWithName:"createWallet" 537 | error:error 538 | signature:"([C)V", charArray]; 539 | 540 | [self zeroCharArray:charArray size:(jsize)(password.length / sizeof(jchar))]; 541 | 542 | if (!*error) 543 | { 544 | [self didStart]; 545 | } 546 | } 547 | 548 | - (id)init 549 | { 550 | self = [super init]; 551 | if (self) 552 | { 553 | self.locale = [NSLocale currentLocale]; 554 | self.availableBitcoinFormats = @[@"BTC", @"mBTC", @"µBTC"];; 555 | 556 | self.appSupportDirectoryIdentifier = kHIDefaultAppSupportIdentifier; 557 | self.appName = kHIDefaultAppName; 558 | 559 | _dateFormatter = [[NSDateFormatter alloc] init]; 560 | _dateFormatter.locale = [[[NSLocale alloc] initWithLocaleIdentifier:@"en_GB"] autorelease]; 561 | _dateFormatter.dateFormat = @"EEE MMM dd HH:mm:ss zzz yyyy"; 562 | _connections = 0; 563 | _balance = 0; 564 | _sending = NO; 565 | _syncProgress = 0.0; 566 | _testingNetwork = NO; 567 | _isRunning = NO; 568 | 569 | self.dataURL = [[[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:self.appSupportDirectoryIdentifier]; 570 | 571 | _vmArgs.version = JNI_VERSION_1_2; 572 | _vmArgs.nOptions = 1; 573 | _vmArgs.ignoreUnrecognized = JNI_TRUE; 574 | 575 | JavaVMOption options[_vmArgs.nOptions]; 576 | _vmArgs.options = options; 577 | 578 | // options[0].optionString = (char*) "-Xbootclasspath:[bootJar]"; 579 | NSBundle *myBundle = [NSBundle bundleWithIdentifier:@"com.hive.BitcoinJKit"]; 580 | options[0].optionString = (char *)[[NSString stringWithFormat:@"-Djava.class.path=%@", [myBundle pathForResource:@"boot" ofType:@"jar"]] UTF8String]; 581 | 582 | JavaVM* vm; 583 | void *env; 584 | JNI_CreateJavaVM(&vm, &env, &_vmArgs); 585 | _jniEnv = (JNIEnv *)(env); 586 | 587 | 588 | // We need to create the manager object 589 | _managerClass = [self jClassForClass:@"com/hive/bitcoinkit/BitcoinManager"]; 590 | (*_jniEnv)->RegisterNatives(_jniEnv, _managerClass, methods, sizeof(methods)/sizeof(methods[0])); 591 | 592 | JNINativeMethod loggerMethod; 593 | loggerMethod.name = "receiveLogFromJVM"; 594 | loggerMethod.signature = "(Ljava/lang/String;Ljava/lang/String;IILjava/lang/String;)V"; 595 | loggerMethod.fnPtr = &receiveLogFromJVM; 596 | 597 | jclass loggerClass = [self jClassForClass:@"org/slf4j/impl/CocoaLogger"]; 598 | (*_jniEnv)->RegisterNatives(_jniEnv, loggerClass, &loggerMethod, 1); 599 | 600 | jmethodID constructorM = (*_jniEnv)->GetMethodID(_jniEnv, _managerClass, "", "()V"); 601 | if (constructorM) 602 | { 603 | _managerObject = (*_jniEnv)->NewObject(_jniEnv, _managerClass, constructorM); 604 | } 605 | 606 | //_balanceChecker = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(checkBalance:) userInfo:nil repeats:YES]; 607 | } 608 | 609 | return self; 610 | } 611 | 612 | - (void)dealloc 613 | { 614 | [self stop]; 615 | self.sendCompletionBlock = nil; 616 | [super dealloc]; 617 | } 618 | 619 | - (void)resyncBlockchain:(NSError **)error 620 | { 621 | NSString *blockchainFilename = [_appName stringByAppendingString:@".spvchain"]; 622 | NSString *blockchainFilePath = [_dataURL.path stringByAppendingPathComponent:blockchainFilename]; 623 | if([[NSFileManager defaultManager] fileExistsAtPath:blockchainFilePath]) 624 | { 625 | [[NSFileManager defaultManager] removeItemAtPath:blockchainFilePath error:nil]; 626 | } 627 | 628 | [self startBlockchain:error]; 629 | } 630 | 631 | - (void)initialize:(NSError **)error 632 | { 633 | [[NSFileManager defaultManager] createDirectoryAtURL:_dataURL withIntermediateDirectories:YES attributes:0 error:NULL]; 634 | 635 | if(!_testingNetwork) 636 | { 637 | // check if there is a need to copy the checkpoint file 638 | NSString *checkpointFilename = [_appName stringByAppendingString:@".checkpoints"]; 639 | NSString *checkpointFilePath = [_dataURL.path stringByAppendingPathComponent:checkpointFilename]; 640 | 641 | if(![[NSFileManager defaultManager] fileExistsAtPath:checkpointFilePath]) 642 | { 643 | // copy checkpoint file from the bundle 644 | [[NSFileManager defaultManager] copyItemAtPath:[[NSBundle mainBundle] pathForResource:@"checkpoints" ofType:@""] toPath:checkpointFilePath error:nil]; 645 | } 646 | } 647 | 648 | [self callVoidMethodWithName:"setTestingNetwork" error:NULL signature:"(Z)V", _testingNetwork]; 649 | 650 | // Now set the folder 651 | [self callVoidMethodWithName:"setDataDirectory" error:NULL signature:"(Ljava/lang/String;)V", 652 | JStringFromNSString(_jniEnv, self.dataURL.path)]; 653 | 654 | // Now set the app name 655 | [self callVoidMethodWithName:"setAppName" error:NULL signature:"(Ljava/lang/String;)V", 656 | JStringFromNSString(_jniEnv, _appName)]; 657 | } 658 | 659 | - (void)loadWallet:(NSError **)error 660 | { 661 | // We're ready! Let's start 662 | [self callVoidMethodWithName:"loadWallet" error:error signature:"()V"]; 663 | } 664 | 665 | - (void)startBlockchain:(NSError **)error 666 | { 667 | // We're ready! Let's start 668 | [self callVoidMethodWithName:"startBlockchain" error:error signature:"()V"]; 669 | 670 | if (!error || !*error) 671 | { 672 | [self didStart]; 673 | } 674 | } 675 | 676 | - (void)stop 677 | { 678 | [_balanceChecker invalidate]; 679 | _balanceChecker = nil; 680 | jclass mgrClass = [self jClassForClass:@"com/hive/bitcoinkit/BitcoinManager"]; 681 | // We're ready! Let's start 682 | jmethodID stopM = (*_jniEnv)->GetMethodID(_jniEnv, mgrClass, "stop", "()V"); 683 | 684 | if (stopM == NULL) 685 | return; 686 | 687 | (*_jniEnv)->CallVoidMethod(_jniEnv, _managerObject, stopM); 688 | if ((*_jniEnv)->ExceptionCheck(_jniEnv)) 689 | { 690 | (*_jniEnv)->ExceptionDescribe(_jniEnv); 691 | (*_jniEnv)->ExceptionClear(_jniEnv); 692 | } 693 | 694 | [self willChangeValueForKey:@"isRunning"]; 695 | _isRunning = NO; 696 | [self didChangeValueForKey:@"isRunning"]; 697 | [[NSNotificationCenter defaultCenter] postNotificationName:kHIBitcoinManagerStoppedNotification object:self]; 698 | } 699 | 700 | #pragma mark - Wallet Stack 701 | 702 | - (NSString *)addKey 703 | { 704 | jclass mgrClass = [self jClassForClass:@"com/hive/bitcoinkit/BitcoinManager"]; 705 | // We're ready! Let's start 706 | jmethodID addKeyM = (*_jniEnv)->GetMethodID(_jniEnv, mgrClass, "addKey", "()Ljava/lang/String;"); 707 | 708 | if (addKeyM == NULL) 709 | return nil; 710 | 711 | jstring newKeyString = (*_jniEnv)->CallObjectMethod(_jniEnv, _managerObject, addKeyM); 712 | 713 | if (newKeyString) 714 | { 715 | const char *newKeyStringC = (*_jniEnv)->GetStringUTFChars(_jniEnv, newKeyString, NULL); 716 | 717 | NSString *newKeyStringNS = [NSString stringWithUTF8String:newKeyStringC]; 718 | (*_jniEnv)->ReleaseStringUTFChars(_jniEnv, newKeyString, newKeyStringC); 719 | 720 | return newKeyStringNS; 721 | } 722 | 723 | return nil; 724 | } 725 | 726 | - (void)didStart 727 | { 728 | [self willChangeValueForKey:@"isRunning"]; 729 | _isRunning = YES; 730 | [self didChangeValueForKey:@"isRunning"]; 731 | 732 | [[NSNotificationCenter defaultCenter] postNotificationName:kHIBitcoinManagerStartedNotification object:self]; 733 | 734 | [self willChangeValueForKey:@"walletAddress"]; 735 | [self didChangeValueForKey:@"walletAddress"]; 736 | } 737 | 738 | - (NSArray *)allWalletAddresses 739 | { 740 | jclass mgrClass = [self jClassForClass:@"com/hive/bitcoinkit/BitcoinManager"]; 741 | jmethodID allAddressesM = (*_jniEnv)->GetMethodID(_jniEnv, mgrClass, "getAllWalletAddressesJSON", "()Ljava/lang/String;"); 742 | 743 | if (allAddressesM) 744 | { 745 | jstring wa = (*_jniEnv)->CallObjectMethod(_jniEnv, _managerObject, allAddressesM); 746 | 747 | const char *waStr = (*_jniEnv)->GetStringUTFChars(_jniEnv, wa, NULL); 748 | 749 | NSString *str = [NSString stringWithUTF8String:waStr]; 750 | (*_jniEnv)->ReleaseStringUTFChars(_jniEnv, wa, waStr); 751 | 752 | NSArray *addresses = [NSJSONSerialization JSONObjectWithData:[str dataUsingEncoding:NSUTF8StringEncoding] options:0 error:NULL]; 753 | 754 | return addresses; 755 | } 756 | 757 | return nil; 758 | } 759 | 760 | - (NSString *)walletAddress 761 | { 762 | jclass mgrClass = [self jClassForClass:@"com/hive/bitcoinkit/BitcoinManager"]; 763 | jmethodID walletM = (*_jniEnv)->GetMethodID(_jniEnv, mgrClass, "getWalletAddress", "()Ljava/lang/String;"); 764 | 765 | if (walletM) 766 | { 767 | jstring wa = (*_jniEnv)->CallObjectMethod(_jniEnv, _managerObject, walletM); 768 | 769 | const char *waStr = (*_jniEnv)->GetStringUTFChars(_jniEnv, wa, NULL); 770 | 771 | NSString *str = [NSString stringWithUTF8String:waStr]; 772 | (*_jniEnv)->ReleaseStringUTFChars(_jniEnv, wa, waStr); 773 | 774 | return str; 775 | } 776 | 777 | return nil; 778 | } 779 | 780 | - (NSString *)base64Wallet 781 | { 782 | jclass mgrClass = [self jClassForClass:@"com/hive/bitcoinkit/BitcoinManager"]; 783 | jmethodID walletM = (*_jniEnv)->GetMethodID(_jniEnv, mgrClass, "getWalletAddress", "()Ljava/lang/String;"); 784 | 785 | if (walletM) 786 | { 787 | jstring wa = (*_jniEnv)->CallObjectMethod(_jniEnv, _managerObject, walletM); 788 | 789 | const char *waStr = (*_jniEnv)->GetStringUTFChars(_jniEnv, wa, NULL); 790 | 791 | NSString *str = [NSString stringWithUTF8String:waStr]; 792 | (*_jniEnv)->ReleaseStringUTFChars(_jniEnv, wa, waStr); 793 | 794 | return str; 795 | } 796 | 797 | return nil; 798 | } 799 | 800 | - (NSString *)walletFileBase64String 801 | { 802 | jclass mgrClass = [self jClassForClass:@"com/hive/bitcoinkit/BitcoinManager"]; 803 | jmethodID walletM = (*_jniEnv)->GetMethodID(_jniEnv, mgrClass, "getWalletFileBase64String", "()Ljava/lang/String;"); 804 | 805 | if (walletM) 806 | { 807 | jstring wa = (*_jniEnv)->CallObjectMethod(_jniEnv, _managerObject, walletM); 808 | 809 | if(!wa) 810 | { 811 | return nil; 812 | } 813 | const char *waStr = (*_jniEnv)->GetStringUTFChars(_jniEnv, wa, NULL); 814 | 815 | NSString *str = [NSString stringWithUTF8String:waStr]; 816 | (*_jniEnv)->ReleaseStringUTFChars(_jniEnv, wa, waStr); 817 | 818 | return str; 819 | } 820 | 821 | return nil; 822 | } 823 | 824 | - (BOOL)saveWallet 825 | { 826 | jclass mgrClass = [self jClassForClass:@"com/hive/bitcoinkit/BitcoinManager"]; 827 | // We're ready! Let's start 828 | jmethodID saveMethode = (*_jniEnv)->GetMethodID(_jniEnv, mgrClass, "saveWallet", "()V"); 829 | 830 | if (saveMethode == NULL) 831 | return NO; 832 | 833 | (*_jniEnv)->CallVoidMethod(_jniEnv, _managerObject, saveMethode); 834 | 835 | return YES; 836 | } 837 | 838 | - (BOOL)isWalletEncrypted 839 | { 840 | jclass mgrClass = [self jClassForClass:@"com/hive/bitcoinkit/BitcoinManager"]; 841 | // We're ready! Let's start 842 | jmethodID isEncMethode = (*_jniEnv)->GetMethodID(_jniEnv, mgrClass, "isWalletEncrypted", "()Z"); 843 | 844 | if (isEncMethode == NULL) 845 | return NO; 846 | 847 | jboolean valid = (*_jniEnv)->CallBooleanMethod(_jniEnv, _managerObject, isEncMethode); 848 | return valid; 849 | } 850 | 851 | - (void)changeWalletPassword:(NSData *)fromPassword 852 | toPassword:(NSData *)toPassword 853 | error:(NSError **)error 854 | { 855 | jarray fromCharArray = fromPassword ? JCharArrayFromNSData(_jniEnv, fromPassword) : NULL; 856 | jarray toCharArray = JCharArrayFromNSData(_jniEnv, toPassword); 857 | 858 | [self callVoidMethodWithName:"changeWalletPassword" 859 | error:error 860 | signature:"([C[C)V", fromCharArray, toCharArray]; 861 | 862 | if (fromCharArray) 863 | { 864 | [self zeroCharArray:fromCharArray size:(jsize)(fromPassword.length / sizeof(jchar))]; 865 | } 866 | 867 | [self zeroCharArray:toCharArray size:(jsize)(toPassword.length / sizeof(jchar))]; 868 | } 869 | 870 | - (void)zeroCharArray:(jarray)charArray size:(jsize)size { 871 | jchar zero[size]; 872 | memset(zero, 0, size * sizeof(jchar)); 873 | (*_jniEnv)->SetCharArrayRegion(_jniEnv, charArray, 0, size, zero); 874 | } 875 | 876 | - (void)removeEncryption:(NSData *)password error:(NSError **)error 877 | { 878 | 879 | jarray toCharArray = JCharArrayFromNSData(_jniEnv, password); 880 | [self callVoidMethodWithName:"decryptWallet" 881 | error:error 882 | signature:"([C)V", toCharArray]; 883 | 884 | } 885 | 886 | - (BOOL)changeWalletEncryptionKeyFrom:(NSString *)oldpasswd to:(NSString *)newpasswd 887 | { 888 | return NO; 889 | } 890 | 891 | - (BOOL)unlockWalletWith:(NSString *)passwd 892 | { 893 | return NO; 894 | } 895 | 896 | - (void)lockWallet 897 | { 898 | 899 | } 900 | 901 | - (BOOL)exportWalletWithPassphase:(NSString *)passphrase To:(NSURL *)exportURL 902 | { 903 | jclass mgrClass = [self jClassForClass:@"com/hive/bitcoinkit/BitcoinManager"]; 904 | // We're ready! Let's start 905 | jmethodID tM = (*_jniEnv)->GetMethodID(_jniEnv, mgrClass, "getWalletDump", "(Ljava/lang/String;)Ljava/lang/String;"); 906 | 907 | if (tM == NULL) 908 | return NO; 909 | 910 | jstring passphraseJString = nil; 911 | 912 | if(passphrase) 913 | { 914 | passphraseJString = (*_jniEnv)->NewStringUTF(_jniEnv, passphrase.UTF8String); 915 | } 916 | 917 | jstring walletDrumpString = (*_jniEnv)->CallObjectMethod(_jniEnv, _managerObject, tM, passphraseJString); 918 | 919 | if (walletDrumpString) 920 | { 921 | const char *walletDrumpChars = (*_jniEnv)->GetStringUTFChars(_jniEnv, walletDrumpString, NULL); 922 | 923 | NSString *bStr = [NSString stringWithUTF8String:walletDrumpChars]; 924 | (*_jniEnv)->ReleaseStringUTFChars(_jniEnv, walletDrumpString, walletDrumpChars); 925 | 926 | NSError *error = nil; 927 | [bStr writeToFile:exportURL.path atomically:YES encoding:NSUTF8StringEncoding error:&error]; 928 | if(error) { 929 | return NO; 930 | } 931 | 932 | NSLog(@"%@", bStr); 933 | return YES; 934 | } 935 | 936 | return NO; 937 | } 938 | 939 | - (BOOL)importWalletFrom:(NSURL *)importURL 940 | { 941 | return NO; 942 | } 943 | 944 | 945 | - (uint64_t)balance 946 | { 947 | return [self balance:0]; 948 | } 949 | 950 | - (uint64_t)balanceUnconfirmed 951 | { 952 | return [self balance:1]; 953 | } 954 | 955 | - (uint64_t)balance:(int)type 956 | { 957 | jclass mgrClass = [self jClassForClass:@"com/hive/bitcoinkit/BitcoinManager"]; 958 | // We're ready! Let's start 959 | jmethodID balanceM = (*_jniEnv)->GetMethodID(_jniEnv, mgrClass, "getBalanceString", "(I)Ljava/lang/String;"); 960 | 961 | if (balanceM == NULL) 962 | return 0; 963 | 964 | jstring balanceString = (*_jniEnv)->CallObjectMethod(_jniEnv, _managerObject, balanceM, (jint)type); 965 | 966 | if (balanceString) 967 | { 968 | const char *balanceChars = (*_jniEnv)->GetStringUTFChars(_jniEnv, balanceString, NULL); 969 | 970 | NSString *bStr = [NSString stringWithUTF8String:balanceChars]; 971 | (*_jniEnv)->ReleaseStringUTFChars(_jniEnv, balanceString, balanceChars); 972 | 973 | return [bStr longLongValue]; 974 | } 975 | 976 | return 0; 977 | } 978 | 979 | - (void)checkBalance:(NSTimer *)timer 980 | { 981 | uint64_t currentBalance = [self balance]; 982 | if (_lastBalance != currentBalance) 983 | { 984 | [self onBalanceChanged]; 985 | _lastBalance = currentBalance; 986 | } 987 | } 988 | 989 | #pragma mark - transaction/blockchain stack 990 | 991 | - (NSDictionary *)modifiedTransactionForTransaction:(NSDictionary *)transaction 992 | { 993 | NSMutableDictionary *d = [NSMutableDictionary dictionaryWithDictionary:transaction]; 994 | NSDate *date = [_dateFormatter dateFromString:transaction[@"time"]]; 995 | if (date) 996 | d[@"time"] = date; 997 | else 998 | d[@"time"] = [NSDate dateWithTimeIntervalSinceNow:0]; 999 | 1000 | return d; 1001 | } 1002 | 1003 | - (NSDictionary *)transactionForHash:(NSString *)hash 1004 | { 1005 | jclass mgrClass = [self jClassForClass:@"com/hive/bitcoinkit/BitcoinManager"]; 1006 | // We're ready! Let's start 1007 | jmethodID tM = (*_jniEnv)->GetMethodID(_jniEnv, mgrClass, "getTransaction", "(Ljava/lang/String;)Ljava/lang/String;"); 1008 | 1009 | if (tM == NULL) 1010 | return nil; 1011 | 1012 | 1013 | jstring transString = (*_jniEnv)->CallObjectMethod(_jniEnv, _managerObject, tM, (*_jniEnv)->NewStringUTF(_jniEnv, hash.UTF8String)); 1014 | 1015 | if (transString) 1016 | { 1017 | const char *transChars = (*_jniEnv)->GetStringUTFChars(_jniEnv, transString, NULL); 1018 | 1019 | NSString *bStr = [NSString stringWithUTF8String:transChars]; 1020 | (*_jniEnv)->ReleaseStringUTFChars(_jniEnv, transString, transChars); 1021 | 1022 | return [self modifiedTransactionForTransaction:[NSJSONSerialization JSONObjectWithData:[bStr dataUsingEncoding:NSUTF8StringEncoding] options:0 error:NULL]]; 1023 | 1024 | } 1025 | 1026 | return nil; 1027 | } 1028 | 1029 | - (NSDictionary *)transactionAtIndex:(NSUInteger)index 1030 | { 1031 | jclass mgrClass = [self jClassForClass:@"com/hive/bitcoinkit/BitcoinManager"]; 1032 | // We're ready! Let's start 1033 | jmethodID tM = (*_jniEnv)->GetMethodID(_jniEnv, mgrClass, "getTransaction", "(I)Ljava/lang/String;"); 1034 | 1035 | if (tM == NULL) 1036 | return nil; 1037 | 1038 | 1039 | jstring transString = (*_jniEnv)->CallObjectMethod(_jniEnv, _managerObject, tM, index); 1040 | 1041 | if (transString) 1042 | { 1043 | const char *transChars = (*_jniEnv)->GetStringUTFChars(_jniEnv, transString, NULL); 1044 | 1045 | NSString *bStr = [NSString stringWithUTF8String:transChars]; 1046 | (*_jniEnv)->ReleaseStringUTFChars(_jniEnv, transString, transChars); 1047 | 1048 | return [self modifiedTransactionForTransaction:[NSJSONSerialization JSONObjectWithData:[bStr dataUsingEncoding:NSUTF8StringEncoding] options:0 error:NULL]]; 1049 | 1050 | } 1051 | 1052 | return nil; 1053 | } 1054 | 1055 | - (NSArray *)allTransactions:(int)max 1056 | { 1057 | jclass mgrClass = [self jClassForClass:@"com/hive/bitcoinkit/BitcoinManager"]; 1058 | // We're ready! Let's start 1059 | jmethodID tM = (*_jniEnv)->GetMethodID(_jniEnv, mgrClass, "getAllTransactions", "(I)Ljava/lang/String;"); 1060 | 1061 | if (tM == NULL) 1062 | return nil; 1063 | 1064 | jstring transString = (*_jniEnv)->CallObjectMethod(_jniEnv, _managerObject, tM, (jint)max); 1065 | 1066 | if (transString) 1067 | { 1068 | const char *transChars = (*_jniEnv)->GetStringUTFChars(_jniEnv, transString, NULL); 1069 | 1070 | NSString *bStr = [NSString stringWithUTF8String:transChars]; 1071 | (*_jniEnv)->ReleaseStringUTFChars(_jniEnv, transString, transChars); 1072 | 1073 | NSArray *ts = [NSJSONSerialization JSONObjectWithData:[bStr dataUsingEncoding:NSUTF8StringEncoding] options:0 error:NULL]; 1074 | NSMutableArray *mts = [NSMutableArray array]; 1075 | 1076 | for (NSDictionary *t in ts) 1077 | { 1078 | [mts addObject:[self modifiedTransactionForTransaction:t]]; 1079 | } 1080 | 1081 | return mts; 1082 | 1083 | 1084 | } 1085 | 1086 | return nil; 1087 | } 1088 | 1089 | - (NSArray *)transactionsWithRange:(NSRange)range 1090 | { 1091 | jclass mgrClass = [self jClassForClass:@"com/hive/bitcoinkit/BitcoinManager"]; 1092 | // We're ready! Let's start 1093 | jmethodID tM = (*_jniEnv)->GetMethodID(_jniEnv, mgrClass, "getTransaction", "(II)Ljava/lang/String;"); 1094 | 1095 | if (tM == NULL) 1096 | return nil; 1097 | 1098 | 1099 | jstring transString = (*_jniEnv)->CallObjectMethod(_jniEnv, _managerObject, tM, range.location, range.length); 1100 | 1101 | if (transString) 1102 | { 1103 | const char *transChars = (*_jniEnv)->GetStringUTFChars(_jniEnv, transString, NULL); 1104 | 1105 | NSString *bStr = [NSString stringWithUTF8String:transChars]; 1106 | (*_jniEnv)->ReleaseStringUTFChars(_jniEnv, transString, transChars); 1107 | 1108 | NSArray *ts = [NSJSONSerialization JSONObjectWithData:[bStr dataUsingEncoding:NSUTF8StringEncoding] options:0 error:NULL]; 1109 | NSMutableArray *mts = [NSMutableArray array]; 1110 | 1111 | for (NSDictionary *t in ts) 1112 | { 1113 | [mts addObject:[self modifiedTransactionForTransaction:t]]; 1114 | } 1115 | 1116 | return mts; 1117 | 1118 | 1119 | 1120 | } 1121 | 1122 | return nil; 1123 | } 1124 | 1125 | - (BOOL)isAddressValid:(NSString *)address 1126 | { 1127 | jclass mgrClass = [self jClassForClass:@"com/hive/bitcoinkit/BitcoinManager"]; 1128 | // We're ready! Let's start 1129 | jmethodID aV = (*_jniEnv)->GetMethodID(_jniEnv, mgrClass, "isAddressValid", "(Ljava/lang/String;)Z"); 1130 | 1131 | if (aV == NULL) 1132 | return NO; 1133 | 1134 | jboolean valid = (*_jniEnv)->CallBooleanMethod(_jniEnv, _managerObject, aV, (*_jniEnv)->NewStringUTF(_jniEnv, address.UTF8String)); 1135 | return valid; 1136 | } 1137 | 1138 | - (NSString *)commitPreparedTransaction:(NSError **)error 1139 | { 1140 | jstring txHashString = [self callObjectMethodWithName:"commitSendRequest" error:error signature:"()Ljava/lang/String;"]; 1141 | if(txHashString) 1142 | { 1143 | return NSStringFromJString(_jniEnv, txHashString); 1144 | } 1145 | else { 1146 | return nil; 1147 | } 1148 | } 1149 | 1150 | - (void)clearSendRequest:(NSError **)error 1151 | { 1152 | [self callVoidMethodWithName:"clearSendRequest" error:error signature:"()V"]; 1153 | } 1154 | 1155 | - (void)prepareSendCoins:(nanobtc_t)coins toReceipent:(NSString *)receipent comment:(NSString *)comment password:(NSData *)passwordData returnFee:(nanobtc_t *)feeRetVal error:(NSError **)error 1156 | { 1157 | jstring receipentJString = JStringFromNSString(_jniEnv, receipent); 1158 | jstring feeJString = nil; 1159 | jstring jAmount = JStringFromNSString(_jniEnv, [NSString stringWithFormat:@"%lld", coins]); 1160 | 1161 | if(passwordData) 1162 | { 1163 | jarray charArray = JCharArrayFromNSData(_jniEnv, passwordData); 1164 | feeJString = [self callObjectMethodWithName:"createSendRequest" error:error signature:"(Ljava/lang/String;Ljava/lang/String;[C)Ljava/lang/String;", jAmount, receipentJString, charArray]; 1165 | } 1166 | else 1167 | { 1168 | feeJString = [self callObjectMethodWithName:"createSendRequest" error:error signature:"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", jAmount, receipentJString]; 1169 | } 1170 | 1171 | 1172 | if (feeJString) 1173 | { 1174 | NSString *feeNSString = NSStringFromJString(_jniEnv, feeJString); 1175 | 1176 | *feeRetVal = [feeNSString longLongValue]; 1177 | } 1178 | else 1179 | { 1180 | if(!error) 1181 | { 1182 | *error = [NSError errorWithDomain:@"BitcoinKit" code:1001 userInfo:nil]; 1183 | } 1184 | } 1185 | } 1186 | 1187 | - (NSUInteger)transactionCount 1188 | { 1189 | jclass mgrClass = [self jClassForClass:@"com/hive/bitcoinkit/BitcoinManager"]; 1190 | // We're ready! Let's start 1191 | jmethodID tCM = (*_jniEnv)->GetMethodID(_jniEnv, mgrClass, "getTransactionCount", "()I"); 1192 | 1193 | if (tCM == NULL) 1194 | return 0; 1195 | 1196 | jint c = (*_jniEnv)->CallIntMethod(_jniEnv, _managerObject, tCM); 1197 | 1198 | return (NSUInteger)c; 1199 | } 1200 | 1201 | - (NSDate *)lastBlockCreationTime 1202 | { 1203 | jclass mgrClass = [self jClassForClass:@"com/hive/bitcoinkit/BitcoinManager"]; 1204 | // We're ready! Let's start 1205 | jmethodID tCM = (*_jniEnv)->GetMethodID(_jniEnv, mgrClass, "getLastBlockCreationTime", "()J"); 1206 | 1207 | if (tCM == NULL) 1208 | return 0; 1209 | 1210 | jlong c = (*_jniEnv)->CallLongMethod(_jniEnv, _managerObject, tCM); 1211 | if(c == 0) 1212 | { 1213 | return nil; 1214 | } 1215 | NSDate *date = [NSDate dateWithTimeIntervalSince1970:c]; 1216 | return date; 1217 | } 1218 | 1219 | - (NSString *)walletFilename 1220 | { 1221 | //TODO, error handling 1222 | NSError *error = nil;; 1223 | jstring walletLocation = [self callObjectMethodWithName:"getWalletPath" error:&error signature:"()Ljava/lang/String;"]; 1224 | if (walletLocation) 1225 | { 1226 | NSString *walletLocationNSString = NSStringFromJString(_jniEnv, walletLocation); 1227 | return walletLocationNSString; 1228 | } 1229 | return nil; 1230 | } 1231 | 1232 | #pragma mark - Callbacks 1233 | 1234 | - (void)onBalanceChanged 1235 | { 1236 | dispatch_async(dispatch_get_main_queue(), ^{ 1237 | [self willChangeValueForKey:@"balance"]; 1238 | [self didChangeValueForKey:@"balance"]; 1239 | }); 1240 | } 1241 | 1242 | - (void)onPeerCountChanged:(int)peerCount 1243 | { 1244 | dispatch_async(dispatch_get_main_queue(), ^{ 1245 | [self willChangeValueForKey:@"peerCount"]; 1246 | _peerCount = (NSUInteger)peerCount; 1247 | [self didChangeValueForKey:@"peerCount"]; 1248 | }); 1249 | } 1250 | 1251 | - (void)onSynchronizationChanged:(double)progress blockCount:(long)blockCount totalBlocks:(long)totalBlocks 1252 | { 1253 | dispatch_async(dispatch_get_main_queue(), ^{ 1254 | [self willChangeValueForKey:@"syncProgress"]; 1255 | if(progress >= 0) 1256 | { 1257 | _syncProgress = (double)progress; 1258 | } 1259 | 1260 | if(blockCount > 0) 1261 | { 1262 | _currentBlockCount = blockCount; 1263 | } 1264 | if(totalBlocks > 0) 1265 | { 1266 | _totalBlocks = totalBlocks; 1267 | } 1268 | [self didChangeValueForKey:@"syncProgress"]; 1269 | }); 1270 | } 1271 | 1272 | - (void)onTransactionChanged:(NSString *)txid 1273 | { 1274 | if(_syncProgress < 1.0) 1275 | { 1276 | // don't update transactions during the sync process because it will end up in rebuilding the gui muliple times per second 1277 | return; 1278 | } 1279 | dispatch_async(dispatch_get_main_queue(), ^{ 1280 | [self willChangeValueForKey:@"balance"]; 1281 | [[NSNotificationCenter defaultCenter] postNotificationName:kHIBitcoinManagerTransactionChangedNotification object:txid]; 1282 | [self didChangeValueForKey:@"balance"]; 1283 | }); 1284 | } 1285 | 1286 | - (void)onCoinsReceived:(NSString *)txid 1287 | { 1288 | dispatch_async(dispatch_get_main_queue(), ^{ 1289 | [self willChangeValueForKey:@"balance"]; 1290 | [[NSNotificationCenter defaultCenter] postNotificationName:kHIBitcoinManagerCoinsReceivedNotification object:txid]; 1291 | [self didChangeValueForKey:@"balance"]; 1292 | }); 1293 | } 1294 | 1295 | - (void)onWalletChanged 1296 | { 1297 | dispatch_async(dispatch_get_main_queue(), ^{ 1298 | // currently do nothing on this because it will use a lot of cpu during the sync 1299 | 1300 | //[self willChangeValueForKey:@"walletstate"]; 1301 | //[self didChangeValueForKey:@"walletstate"]; 1302 | }); 1303 | } 1304 | 1305 | - (void)onTransactionSucceeded:(NSString *)txid 1306 | { 1307 | dispatch_async(dispatch_get_main_queue(), ^{ 1308 | _sending = NO; 1309 | if (self.sendCompletionBlock) 1310 | { 1311 | self.sendCompletionBlock(txid); 1312 | } 1313 | [self.sendCompletionBlock release]; 1314 | self.sendCompletionBlock = nil; 1315 | }); 1316 | } 1317 | 1318 | - (void)onTransactionFailed 1319 | { 1320 | dispatch_async(dispatch_get_main_queue(), ^{ 1321 | _sending = NO; 1322 | if (self.sendCompletionBlock) 1323 | { 1324 | self.sendCompletionBlock(nil); 1325 | } 1326 | 1327 | [self.sendCompletionBlock release]; 1328 | self.sendCompletionBlock = nil; 1329 | }); 1330 | } 1331 | 1332 | #pragma mark helpers 1333 | 1334 | 1335 | - (NSString *)decimalSeparator { 1336 | return [self createNumberFormatterWithFormat:@"BTC"].decimalSeparator; 1337 | } 1338 | 1339 | - (NSString *)preferredFormat { 1340 | NSString *currency = [[NSUserDefaults standardUserDefaults] stringForKey:kBitcoinKitFormatPreferenceKey]; 1341 | return [self.availableFormats containsObject:currency] ? currency : @"mBTC"; 1342 | } 1343 | 1344 | - (void)setPreferredFormat:(NSString *)preferredFormat { 1345 | NSString *oldValue = self.preferredFormat; 1346 | if (![oldValue isEqualToString:preferredFormat]) { 1347 | [[NSUserDefaults standardUserDefaults] setObject:preferredFormat 1348 | forKey:kBitcoinKitFormatPreferenceKey]; 1349 | [[NSNotificationCenter defaultCenter] postNotificationName:kBitcoinKitFormatChangeNotification 1350 | object:self 1351 | userInfo:nil]; 1352 | } 1353 | } 1354 | 1355 | - (NSString *)stringWithDesignatorForBitcoin:(nanobtc_t)nanoBtcValue { 1356 | return [NSString stringWithFormat:@"%@ %@", [self stringForBitcoin:nanoBtcValue], self.preferredFormat]; 1357 | } 1358 | 1359 | - (NSString *)stringForBitcoin:(nanobtc_t)nanoBtcValue { 1360 | return [self stringForBitcoin:nanoBtcValue withFormat:self.preferredFormat]; 1361 | } 1362 | 1363 | - (NSString *)stringForBitcoin:(nanobtc_t)nanoBtcValue withFormat:(NSString *)format { 1364 | bool isNeg = NO; 1365 | if(nanoBtcValue < 0) 1366 | { 1367 | isNeg = YES; 1368 | nanoBtcValue = -nanoBtcValue; 1369 | } 1370 | NSNumberFormatter *formatter = [self createNumberFormatterWithFormat:format]; 1371 | NSDecimalNumber *number = [NSDecimalNumber decimalNumberWithMantissa:nanoBtcValue 1372 | exponent:-8 1373 | isNegative:isNeg]; 1374 | number = [number decimalNumberByMultiplyingByPowerOf10:[self shiftForFormat:format]]; 1375 | 1376 | return [formatter stringFromNumber:number]; 1377 | } 1378 | 1379 | - (NSNumberFormatter *)createNumberFormatterWithFormat:(NSString *)format { 1380 | // Do not use the formatter's multiplier! It causes rounding errors! 1381 | NSNumberFormatter *formatter = [NSNumberFormatter new]; 1382 | formatter.locale = _locale; 1383 | formatter.generatesDecimalNumbers = YES; 1384 | formatter.minimum = @0; 1385 | formatter.numberStyle = NSNumberFormatterDecimalStyle; 1386 | formatter.minimumIntegerDigits = 1; 1387 | if ([format isEqualToString:@"BTC"]) { 1388 | formatter.minimumFractionDigits = 2; 1389 | formatter.maximumFractionDigits = 8; 1390 | } else if ([format isEqualToString:@"mBTC"]) { 1391 | formatter.minimumFractionDigits = 0; 1392 | formatter.maximumFractionDigits = 5; 1393 | } else if ([format isEqualToString:@"µBTC"]) { 1394 | formatter.minimumFractionDigits = 0; 1395 | formatter.maximumFractionDigits = 2; 1396 | } else if ([format isEqualToString:@"satoshi"]) { 1397 | formatter.minimumFractionDigits = 0; 1398 | formatter.maximumFractionDigits = 0; 1399 | } else { 1400 | @throw [self createUnknownFormatException:format]; 1401 | } 1402 | return formatter; 1403 | } 1404 | 1405 | - (NSException *)createUnknownFormatException:(NSString *)format { 1406 | return [NSException exceptionWithName:@"UnknownBitcoinFormatException" 1407 | reason:[NSString stringWithFormat:@"Unknown Bitcoin format %@", format] 1408 | userInfo:nil]; 1409 | } 1410 | 1411 | - (int)shiftForFormat:(NSString *)format { 1412 | if ([format isEqualToString:@"BTC"]) { 1413 | return 0; 1414 | } else if ([format isEqualToString:@"mBTC"]) { 1415 | return 3; 1416 | } else if ([format isEqualToString:@"µBTC"]) { 1417 | return 6; 1418 | } else if ([format isEqualToString:@"satoshi"]) { 1419 | return 8; 1420 | } else { 1421 | @throw [self createUnknownFormatException:format]; 1422 | } 1423 | } 1424 | 1425 | 1426 | - (NSString *)formatNanobtc:(nanobtc_t)nanoBtcValue 1427 | { 1428 | return [self stringForBitcoin:nanoBtcValue]; 1429 | } 1430 | 1431 | - (NSString *)formatNanobtc:(nanobtc_t)nanoBtcValue withDesignator:(BOOL)designator 1432 | { 1433 | return [self stringWithDesignatorForBitcoin:nanoBtcValue]; 1434 | } 1435 | 1436 | - (nanobtc_t)nanoBtcFromString:(NSString *)userAmount format:(NSString *)format 1437 | { 1438 | int shift = 8-[self shiftForFormat:format]; 1439 | NSDecimalNumber *decimal = [NSDecimalNumber decimalNumberWithString:userAmount]; 1440 | return [[decimal decimalNumberByMultiplyingByPowerOf10:shift] longLongValue]; 1441 | } 1442 | 1443 | @end 1444 | --------------------------------------------------------------------------------