From 8ce04488f2be81057721ef5abeee227f28156f32 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 28 May 2013 06:19:25 +0200 Subject: [PATCH 001/299] added test vectors --- api/src/test/resources/BIP32.json | 76 +++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 api/src/test/resources/BIP32.json diff --git a/api/src/test/resources/BIP32.json b/api/src/test/resources/BIP32.json new file mode 100644 index 00000000..f89e74af --- /dev/null +++ b/api/src/test/resources/BIP32.json @@ -0,0 +1,76 @@ +[ + { + "master":"000102030405060708090a0b0c0d0e0f", + "private":"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", + "public":"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", + "derived": [ + { + "name":"Test1 m/0'", + "locator" : [{"sequence": 0, "private":true}], + "private":"xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7", + "public":"xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw" + }, + { + "name":"Test1 m/0'/1", + "locator" : [{"sequence": 0, "private":true},{"sequence": 1, "private":false}], + "private":"xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs", + "public":"xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ" + }, + { + "name":"Test1 m/0'/1/2'", + "locator" : [{"sequence": 0, "private":true},{"sequence": 1, "private":false},{"sequence": 2, "private":true}], + "private":"xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM", + "public":"xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5" + }, + { + "name":"Test1 m/0'/1/2'/2", + "locator" : [{"sequence": 0, "private":true},{"sequence": 1, "private":false},{"sequence": 2, "private":true},{"sequence": 2, "private":false}], + "private":"xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334", + "public":"xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV" + }, + { + "name":"Test1 m/0'/1/2'/2/1000000000", + "locator" : [{"sequence": 0, "private":true},{"sequence": 1, "private":false},{"sequence": 2, "private":true},{"sequence": 2, "private":false},{"sequence": 1000000000, "private":false}], + "private":"xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76", + "public":"xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy" + } + ] + }, + { + "master":"fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", + "private":"xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", + "public":"xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", + "derived": [ + { + "name":"Test2 m/0", + "locator" : [{"sequence": 0, "private":false}], + "private":"xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt", + "public":"xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH" + }, + { + "name":"Test2 m/0/2147483647'", + "locator" : [{"sequence": 0, "private":false},{"sequence": 2147483647, "private":true}], + "private":"xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9", + "public":"xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a" + }, + { + "name":"Test2 m/0/2147483647'/1", + "locator" : [{"sequence": 0, "private":false},{"sequence": 2147483647, "private":true},{"sequence": 1, "private":false}], + "private":"xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef", + "public":"xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon" + }, + { + "name":"Test2 m/0/2147483647'/1/2147483646'", + "locator" : [{"sequence": 0, "private":false},{"sequence": 2147483647, "private":true},{"sequence": 1, "private":false},{"sequence": 2147483646, "private":true}], + "public":"xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL", + "private":"xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc" + }, + { + "name":"Test2 m/0/2147483647'/1/2147483646'/2", + "locator" : [{"sequence": 0, "private":false},{"sequence": 2147483647, "private":true},{"sequence": 1, "private":false},{"sequence": 2147483646, "private":true},{"sequence": 2, "private":false}], + "public":"xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt", + "private":"xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j" + } + ] + } +] \ No newline at end of file From 9843764214c94b2f93ec19183b93d35fadce53eb Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 28 May 2013 07:07:29 +0200 Subject: [PATCH 002/299] matching test vectors wallet caches keyspec --- .../bitsofproof/supernode/api/ECKeyPair.java | 2 +- .../supernode/api/ExtendedKey.java | 14 +++++----- .../supernode/api/SerializedWallet.java | 28 ++++++++++++------- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ECKeyPair.java b/api/src/main/java/com/bitsofproof/supernode/api/ECKeyPair.java index 82896dc7..9c34ab24 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ECKeyPair.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ECKeyPair.java @@ -127,7 +127,7 @@ public ECKeyPair (byte[] p, boolean compressed) throws ValidationException { throw new ValidationException ("Invalid private key"); } - this.priv = new BigInteger (1, p); + this.priv = new BigInteger (1, p).mod (curve.getN ()); this.compressed = compressed; if ( compressed ) { diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java index 291d085e..5f2e9db6 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java @@ -108,7 +108,7 @@ public Key getKey (int sequence) throws ValidationException public ExtendedKey getChild (int sequence) throws ValidationException { ExtendedKey sub = generateKey (sequence); - return new ExtendedKey (sub.getMaster (), sub.getChainCode (), sub.getDepth () + 1, sub.getFingerPrint (), 0); + return new ExtendedKey (sub.getMaster (), sub.getChainCode (), sub.getDepth () + 1, getFingerPrint (), sequence); } private ExtendedKey generateKey (int sequence) throws ValidationException @@ -129,9 +129,9 @@ private ExtendedKey generateKey (int sequence) throws ValidationException { extended = new byte[pub.length + 4]; System.arraycopy (pub, 0, extended, 0, pub.length); - extended[pub.length] = (byte) ((sequence >> 24) & 0xff); - extended[pub.length + 1] = (byte) ((sequence >> 16) & 0xff); - extended[pub.length + 2] = (byte) ((sequence >> 8) & 0xff); + extended[pub.length] = (byte) ((sequence >>> 24) & 0xff); + extended[pub.length + 1] = (byte) ((sequence >>> 16) & 0xff); + extended[pub.length + 2] = (byte) ((sequence >>> 8) & 0xff); extended[pub.length + 3] = (byte) (sequence & 0xff); } else @@ -139,9 +139,9 @@ private ExtendedKey generateKey (int sequence) throws ValidationException byte[] priv = master.getPrivate (); extended = new byte[priv.length + 5]; System.arraycopy (priv, 0, extended, 1, priv.length); - extended[priv.length + 1] = (byte) ((sequence >> 24) & 0xff); - extended[priv.length + 2] = (byte) ((sequence >> 16) & 0xff); - extended[priv.length + 3] = (byte) ((sequence >> 8) & 0xff); + extended[priv.length + 1] = (byte) ((sequence >>> 24) & 0xff); + extended[priv.length + 2] = (byte) ((sequence >>> 16) & 0xff); + extended[priv.length + 3] = (byte) ((sequence >>> 8) & 0xff); extended[priv.length + 4] = (byte) (sequence & 0xff); } byte[] lr = mac.doFinal (extended); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java index e87c414a..71473178 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java @@ -56,6 +56,8 @@ public class SerializedWallet implements Wallet private String fileName; private long timeStamp; private boolean production; + private SecretKeySpec keyspec = null; + private final byte[] iv = new byte[16]; private BCSAPI api; @@ -288,11 +290,14 @@ public void writeWallet (OutputStream output, String passphrase) throws Validati try { output.write (MAGIC.getBytes ("US-ASCII")); - byte[] iv = new byte[16]; - rnd.nextBytes (iv); - output.write (iv); - byte[] derived = SCrypt.generate (passphrase.getBytes ("UTF-8"), iv, 16384, 8, 8, 32); - SecretKeySpec keyspec = new SecretKeySpec (derived, "AES"); + if ( keyspec == null || !passphrase.equals (this.passphrase) ) + { + this.passphrase = passphrase; + rnd.nextBytes (iv); + output.write (iv); + byte[] derived = SCrypt.generate (passphrase.getBytes ("UTF-8"), iv, 16384, 8, 8, 32); + keyspec = new SecretKeySpec (derived, "AES"); + } Cipher cipher = Cipher.getInstance ("AES/CBC/PKCS5Padding", "BC"); cipher.init (Cipher.ENCRYPT_MODE, keyspec, new IvParameterSpec (iv)); CipherOutputStream cip = new CipherOutputStream (output, cipher); @@ -372,12 +377,11 @@ public static SerializedWallet readWallet (InputStream input, String passphrase, { throw new ValidationException ("Not a wallet"); } - byte[] iv = new byte[16]; - input.read (iv); - derived = SCrypt.generate (passphrase.getBytes ("UTF-8"), iv, 16384, 8, 8, 32); - SecretKeySpec keyspec = new SecretKeySpec (derived, "AES"); + input.read (wallet.iv); + derived = SCrypt.generate (passphrase.getBytes ("UTF-8"), wallet.iv, 16384, 8, 8, 32); + wallet.keyspec = new SecretKeySpec (derived, "AES"); Cipher cipher = Cipher.getInstance ("AES/CBC/PKCS5Padding", "BC"); - cipher.init (Cipher.DECRYPT_MODE, keyspec, new IvParameterSpec (iv)); + cipher.init (Cipher.DECRYPT_MODE, wallet.keyspec, new IvParameterSpec (wallet.iv)); CipherInputStream cis = new CipherInputStream (input, cipher); GZIPInputStream unzip = new GZIPInputStream (cis); @@ -393,6 +397,10 @@ public static SerializedWallet readWallet (InputStream input, String passphrase, { throw new ValidationException ("Expected production keys"); } + if ( !production && !key.key.startsWith ("t") ) + { + throw new ValidationException ("Expected test keys"); + } key.nextSequence = k.getNextSequence (); if ( k.hasName () ) { From 0b2ff5bb236aa22a61f0a10aad195ea239915858 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Sat, 1 Jun 2013 09:43:18 +0200 Subject: [PATCH 003/299] Use 1.1-SNAPSHOT --- api/pom.xml | 2 +- pom.xml | 2 +- server/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index c679389d..a7203d26 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.0 + 1.1-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index 3b60815c..4e7d7f0c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.0 + 1.1-SNAPSHOT supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/pom.xml b/server/pom.xml index c6fd9ee2..6e9c8dd7 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.0 + 1.1-SNAPSHOT ../ From b516f53f87d7ec294607fd75f0da30b633fcd538 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Sat, 1 Jun 2013 18:00:11 +0200 Subject: [PATCH 004/299] write iv into wallet --- .../java/com/bitsofproof/supernode/api/SerializedWallet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java index 71473178..31b3aeb7 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java @@ -294,10 +294,10 @@ public void writeWallet (OutputStream output, String passphrase) throws Validati { this.passphrase = passphrase; rnd.nextBytes (iv); - output.write (iv); byte[] derived = SCrypt.generate (passphrase.getBytes ("UTF-8"), iv, 16384, 8, 8, 32); keyspec = new SecretKeySpec (derived, "AES"); } + output.write (iv); Cipher cipher = Cipher.getInstance ("AES/CBC/PKCS5Padding", "BC"); cipher.init (Cipher.ENCRYPT_MODE, keyspec, new IvParameterSpec (iv)); CipherOutputStream cip = new CipherOutputStream (output, cipher); From ed181ccd83a7030cb7c46042a3fdd577fbc9523e Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Sat, 1 Jun 2013 18:39:08 +0200 Subject: [PATCH 005/299] use file timestamp correctly --- .../supernode/api/SerializedWallet.java | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java index 31b3aeb7..add82339 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java @@ -47,11 +47,15 @@ import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.crypto.generators.SCrypt; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.bitsofproof.supernode.api.BloomFilter.UpdateMode; public class SerializedWallet implements Wallet { + private static final Logger log = LoggerFactory.getLogger (SerializedWallet.class); + private String passphrase; private String fileName; private long timeStamp; @@ -126,7 +130,13 @@ public AccountManager getAccountManager (String name) throws BCSAPIException transactions.clear (); } final DefaultAccountManager fam = am; - api.scanTransactions (am.getAddresses (), UpdateMode.all, getTimeStamp (), new TransactionListener () + long ts = getTimeStamp (); + if ( ts > 0 ) + { + // go back a day to ensure possible re-orgs are included. + ts -= 60 * 60 * 24; + } + api.scanTransactions (am.getAddresses (), UpdateMode.all, ts, new TransactionListener () { @Override public void process (Transaction t) @@ -137,6 +147,22 @@ public void process (Transaction t) } } }); + persist (); + am.addAccountListener (new AccountListener () + { + @Override + public void accountChanged (AccountManager account, Transaction t) + { + try + { + persist (); + } + catch ( BCSAPIException e ) + { + log.error ("Failed to persist wallet", e); + } + } + }); return am; } catch ( ValidationException e ) @@ -209,7 +235,7 @@ public void persist () throws BCSAPIException out.close (); f.delete (); tmp.renameTo (f); - timeStamp = f.lastModified (); + timeStamp = f.lastModified () / 1000; } } catch ( IOException e ) From 0b2a108efaca5f4bf569efc287c949ad59e27a6c Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Mon, 3 Jun 2013 10:13:36 +0200 Subject: [PATCH 006/299] moving common pieces of api and server to common package --- .../supernode/api/AccountManager.java | 4 ++++ .../supernode/api/AddressConverter.java | 4 ++++ .../com/bitsofproof/supernode/api/BCSAPI.java | 3 ++- .../com/bitsofproof/supernode/api/Block.java | 4 ++++ .../supernode/api/ClientBusAdaptor.java | 4 +++- .../com/bitsofproof/supernode/api/Color.java | 5 +++++ .../supernode/api/DefaultAccountManager.java | 6 +++++- .../bitsofproof/supernode/api/ExtendedKey.java | 7 +++++++ .../supernode/api/KeyFormatter.java | 6 ++++++ .../supernode/api/SerializedWallet.java | 3 ++- .../bitsofproof/supernode/api/Transaction.java | 8 +++++++- .../supernode/api/TransactionInput.java | 2 ++ .../supernode/api/TransactionOutput.java | 6 +++++- .../{api => common}/BinaryAggregator.java | 2 +- .../supernode/{api => common}/BloomFilter.java | 4 +++- .../supernode/{api => common}/ByteUtils.java | 3 ++- .../supernode/{api => common}/ByteVector.java | 2 +- .../supernode/{api => common}/ECKeyPair.java | 3 ++- .../supernode/{api => common}/ECPublicKey.java | 2 +- .../supernode/{api => common}/Hash.java | 3 ++- .../InMemoryBusConnectionFactory.java | 2 +- .../supernode/{api => common}/Key.java | 3 ++- .../{api => common}/ScriptFormat.java | 3 ++- .../{api => common}/ValidationException.java | 2 +- .../supernode/{api => common}/WireFormat.java | 3 ++- .../supernode/api/BloomFilterTest.java | 3 ++- .../supernode/api/ExtendedKeyTest.java | 5 +++++ .../supernode/api/InMemoryBusTest.java | 2 ++ .../supernode/api/KeySerializerTest.java | 4 ++++ .../supernode/api/WalletFormatTest.java | 6 +++++- .../supernode/core/AddressHandler.java | 6 +++--- .../supernode/core/AddressSeeder.java | 2 +- .../supernode/core/BitcoinPeer.java | 8 ++++---- .../bitsofproof/supernode/core/BlockStore.java | 8 ++++---- .../supernode/core/BlocktesterChain.java | 6 +++--- .../supernode/core/CachedBlockStore.java | 18 +++++++++--------- .../supernode/core/ChainLoader.java | 4 ++-- .../bitsofproof/supernode/core/ColorStore.java | 2 +- .../supernode/core/GetBlocksHandler.java | 2 +- .../supernode/core/GetDataHandler.java | 4 ++-- .../supernode/core/GetHeadersHandler.java | 6 +++--- .../supernode/core/HeartbeatHandler.java | 2 +- .../supernode/core/IRCDiscovery.java | 5 +++-- .../supernode/core/ImplementBCSAPI.java | 12 ++++++------ .../bitsofproof/supernode/core/PeerStore.java | 2 +- .../supernode/core/SatoshiChain.java | 6 +++--- .../supernode/core/ScriptEvaluation.java | 14 +++++++------- .../supernode/core/Testnet3Chain.java | 6 +++--- .../core/TransactionValidationException.java | 2 +- .../bitsofproof/supernode/core/TxHandler.java | 8 ++++---- .../supernode/core/UnitTestChain.java | 6 +++--- .../bitsofproof/supernode/main/Supernode.java | 2 +- .../supernode/messages/AddrMessage.java | 6 +++--- .../supernode/messages/AlertMessage.java | 8 ++++---- .../supernode/messages/BlockMessage.java | 4 ++-- .../supernode/messages/FilterAddMessage.java | 4 ++-- .../supernode/messages/FilterClearMessage.java | 4 ++-- .../supernode/messages/FilterLoadMessage.java | 6 +++--- .../supernode/messages/GetBlocksMessage.java | 4 ++-- .../supernode/messages/GetDataMessage.java | 4 ++-- .../supernode/messages/GetHeadersMessage.java | 4 ++-- .../supernode/messages/HeadersMessage.java | 4 ++-- .../supernode/messages/InvMessage.java | 4 ++-- .../supernode/messages/MerkleBlockMessage.java | 12 ++++++------ .../supernode/messages/PingMessage.java | 4 ++-- .../supernode/messages/PongMessage.java | 4 ++-- .../supernode/messages/TxMessage.java | 4 ++-- .../supernode/messages/VersionMessage.java | 6 +++--- .../com/bitsofproof/supernode/model/Blk.java | 10 +++++----- .../com/bitsofproof/supernode/model/Head.java | 4 ++-- .../bitsofproof/supernode/model/JpaStore.java | 6 +++--- .../bitsofproof/supernode/model/KnownPeer.java | 2 +- .../supernode/model/LvlDiskStore.java | 2 +- .../bitsofproof/supernode/model/LvlStore.java | 8 ++++---- .../supernode/model/StoredColor.java | 6 +++--- .../com/bitsofproof/supernode/model/Tx.java | 18 +++++++++--------- .../com/bitsofproof/supernode/model/TxIn.java | 10 +++++----- .../com/bitsofproof/supernode/model/TxOut.java | 8 ++++---- .../bitsofproof/supernode/test/APITest.java | 4 ++-- .../supernode/test/AddressTest.java | 4 ++-- .../bitsofproof/supernode/test/Base58Test.java | 4 ++-- .../supernode/test/BitcoinJBlockTest.java | 2 +- .../bitsofproof/supernode/test/ScriptTest.java | 4 ++-- .../supernode/test/TransactionTest.java | 8 ++++---- .../supernode/test/WireFormatTest.java | 6 +++--- .../test/resources/context/EmbeddedBCSAPI.xml | 2 +- 86 files changed, 255 insertions(+), 180 deletions(-) rename api/src/main/java/com/bitsofproof/supernode/{api => common}/BinaryAggregator.java (96%) rename api/src/main/java/com/bitsofproof/supernode/{api => common}/BloomFilter.java (98%) rename api/src/main/java/com/bitsofproof/supernode/{api => common}/ByteUtils.java (98%) rename api/src/main/java/com/bitsofproof/supernode/{api => common}/ByteVector.java (95%) rename api/src/main/java/com/bitsofproof/supernode/{api => common}/ECKeyPair.java (99%) rename api/src/main/java/com/bitsofproof/supernode/{api => common}/ECPublicKey.java (97%) rename api/src/main/java/com/bitsofproof/supernode/{api => common}/Hash.java (98%) rename api/src/main/java/com/bitsofproof/supernode/{api => common}/InMemoryBusConnectionFactory.java (99%) rename api/src/main/java/com/bitsofproof/supernode/{api => common}/Key.java (95%) rename api/src/main/java/com/bitsofproof/supernode/{api => common}/ScriptFormat.java (99%) rename api/src/main/java/com/bitsofproof/supernode/{api => common}/ValidationException.java (95%) rename api/src/main/java/com/bitsofproof/supernode/{api => common}/WireFormat.java (99%) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java index 6f70670b..40d17e9e 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java @@ -17,6 +17,10 @@ import java.util.Collection; +import com.bitsofproof.supernode.common.BloomFilter; +import com.bitsofproof.supernode.common.Key; +import com.bitsofproof.supernode.common.ValidationException; + public interface AccountManager { public BloomFilter getFilter (); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AddressConverter.java b/api/src/main/java/com/bitsofproof/supernode/api/AddressConverter.java index ef095e6a..d5d791e8 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AddressConverter.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AddressConverter.java @@ -15,6 +15,10 @@ */ package com.bitsofproof.supernode.api; +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ValidationException; + public class AddressConverter { public static byte[] fromSatoshiStyle (String s, int addressFlag) throws ValidationException diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java index 9a3a567d..09030860 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java @@ -17,7 +17,8 @@ import java.util.Collection; -import com.bitsofproof.supernode.api.BloomFilter.UpdateMode; +import com.bitsofproof.supernode.common.BloomFilter; +import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; /** * This is the API extensions to the bitsofproof supernode should build on diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Block.java b/api/src/main/java/com/bitsofproof/supernode/api/Block.java index 0d389e97..2aa09651 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/Block.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/Block.java @@ -21,6 +21,10 @@ import java.util.ArrayList; import java.util.List; +import com.bitsofproof.supernode.common.BinaryAggregator; +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.WireFormat; import com.google.protobuf.ByteString; public class Block implements Serializable, Cloneable diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ClientBusAdaptor.java b/api/src/main/java/com/bitsofproof/supernode/api/ClientBusAdaptor.java index 45b5020e..4f96dae3 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ClientBusAdaptor.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ClientBusAdaptor.java @@ -42,7 +42,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.bitsofproof.supernode.api.BloomFilter.UpdateMode; +import com.bitsofproof.supernode.common.BloomFilter; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Color.java b/api/src/main/java/com/bitsofproof/supernode/api/Color.java index 65aeb391..57c4754c 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/Color.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/Color.java @@ -18,6 +18,11 @@ import java.io.UnsupportedEncodingException; import java.util.Arrays; +import com.bitsofproof.supernode.common.ECKeyPair; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.Key; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.WireFormat; import com.google.protobuf.ByteString; public class Color diff --git a/api/src/main/java/com/bitsofproof/supernode/api/DefaultAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/DefaultAccountManager.java index 362b1366..393957c0 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/DefaultAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/DefaultAccountManager.java @@ -28,9 +28,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.bitsofproof.supernode.api.BloomFilter.UpdateMode; import com.bitsofproof.supernode.api.Transaction.TransactionSink; import com.bitsofproof.supernode.api.Transaction.TransactionSource; +import com.bitsofproof.supernode.common.BloomFilter; +import com.bitsofproof.supernode.common.ByteVector; +import com.bitsofproof.supernode.common.Key; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; class DefaultAccountManager implements TransactionListener, TrunkListener, AccountManager { diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java index 5f2e9db6..b75d57df 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java @@ -32,6 +32,13 @@ import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.ECKeyPair; +import com.bitsofproof.supernode.common.ECPublicKey; +import com.bitsofproof.supernode.common.Key; +import com.bitsofproof.supernode.common.ValidationException; + + /** * Key Generator following BIP32 https://en.bitcoin.it/wiki/BIP_0032 */ diff --git a/api/src/main/java/com/bitsofproof/supernode/api/KeyFormatter.java b/api/src/main/java/com/bitsofproof/supernode/api/KeyFormatter.java index e2b17f02..cb458790 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/KeyFormatter.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/KeyFormatter.java @@ -32,6 +32,12 @@ import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.crypto.generators.SCrypt; +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.ECKeyPair; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.Key; +import com.bitsofproof.supernode.common.ValidationException; + /** * Key serializer following BIP38 https://en.bitcoin.it/wiki/BIP_0038 and WIF https://en.bitcoin.it/wiki/Wallet_import_format */ diff --git a/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java index add82339..ecc7bc1d 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java @@ -50,7 +50,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.bitsofproof.supernode.api.BloomFilter.UpdateMode; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; public class SerializedWallet implements Wallet { diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java b/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java index 1d111455..dc13a352 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java @@ -23,7 +23,13 @@ import org.bouncycastle.util.Arrays; -import com.bitsofproof.supernode.api.ScriptFormat.Opcode; +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.Key; +import com.bitsofproof.supernode.common.ScriptFormat; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.WireFormat; +import com.bitsofproof.supernode.common.ScriptFormat.Opcode; import com.google.protobuf.ByteString; public class Transaction implements Serializable, Cloneable diff --git a/api/src/main/java/com/bitsofproof/supernode/api/TransactionInput.java b/api/src/main/java/com/bitsofproof/supernode/api/TransactionInput.java index 2d741d59..6c0839dc 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/TransactionInput.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/TransactionInput.java @@ -17,6 +17,8 @@ import java.io.Serializable; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.WireFormat; import com.google.protobuf.ByteString; public class TransactionInput implements Serializable, Cloneable diff --git a/api/src/main/java/com/bitsofproof/supernode/api/TransactionOutput.java b/api/src/main/java/com/bitsofproof/supernode/api/TransactionOutput.java index 843ae35a..b121eeff 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/TransactionOutput.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/TransactionOutput.java @@ -18,7 +18,11 @@ import java.io.Serializable; import java.util.List; -import com.bitsofproof.supernode.api.ScriptFormat.Token; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ScriptFormat; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.WireFormat; +import com.bitsofproof.supernode.common.ScriptFormat.Token; import com.google.protobuf.ByteString; public class TransactionOutput implements Serializable, Cloneable diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BinaryAggregator.java b/api/src/main/java/com/bitsofproof/supernode/common/BinaryAggregator.java similarity index 96% rename from api/src/main/java/com/bitsofproof/supernode/api/BinaryAggregator.java rename to api/src/main/java/com/bitsofproof/supernode/common/BinaryAggregator.java index e28021c3..25409063 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BinaryAggregator.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/BinaryAggregator.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.bitsofproof.supernode.api; +package com.bitsofproof.supernode.common; import java.util.List; diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BloomFilter.java b/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java similarity index 98% rename from api/src/main/java/com/bitsofproof/supernode/api/BloomFilter.java rename to api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java index f809977d..c4af6fe0 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BloomFilter.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java @@ -13,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.bitsofproof.supernode.api; +package com.bitsofproof.supernode.common; import java.security.SecureRandom; import org.bouncycastle.util.Arrays; +import com.bitsofproof.supernode.api.AddressConverter; + public class BloomFilter { public static enum UpdateMode diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ByteUtils.java b/api/src/main/java/com/bitsofproof/supernode/common/ByteUtils.java similarity index 98% rename from api/src/main/java/com/bitsofproof/supernode/api/ByteUtils.java rename to api/src/main/java/com/bitsofproof/supernode/common/ByteUtils.java index 847002bf..45be29d0 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ByteUtils.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/ByteUtils.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.bitsofproof.supernode.api; +package com.bitsofproof.supernode.common; import java.io.UnsupportedEncodingException; import java.math.BigInteger; @@ -21,6 +21,7 @@ import org.bouncycastle.util.encoders.Hex; + public class ByteUtils { private static final char[] b58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray (); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ByteVector.java b/api/src/main/java/com/bitsofproof/supernode/common/ByteVector.java similarity index 95% rename from api/src/main/java/com/bitsofproof/supernode/api/ByteVector.java rename to api/src/main/java/com/bitsofproof/supernode/common/ByteVector.java index 49aaf6ce..1f1e5084 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ByteVector.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/ByteVector.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.bitsofproof.supernode.api; +package com.bitsofproof.supernode.common; import java.util.Arrays; diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ECKeyPair.java b/api/src/main/java/com/bitsofproof/supernode/common/ECKeyPair.java similarity index 99% rename from api/src/main/java/com/bitsofproof/supernode/api/ECKeyPair.java rename to api/src/main/java/com/bitsofproof/supernode/common/ECKeyPair.java index 9c34ab24..d3d892a0 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ECKeyPair.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/ECKeyPair.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.bitsofproof.supernode.api; +package com.bitsofproof.supernode.common; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -36,6 +36,7 @@ import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; + public class ECKeyPair implements Key { private static final SecureRandom secureRandom = new SecureRandom (); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ECPublicKey.java b/api/src/main/java/com/bitsofproof/supernode/common/ECPublicKey.java similarity index 97% rename from api/src/main/java/com/bitsofproof/supernode/api/ECPublicKey.java rename to api/src/main/java/com/bitsofproof/supernode/common/ECPublicKey.java index 7f1a409d..754d2ccf 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ECPublicKey.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/ECPublicKey.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.bitsofproof.supernode.api; +package com.bitsofproof.supernode.common; import org.bouncycastle.util.Arrays; diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Hash.java b/api/src/main/java/com/bitsofproof/supernode/common/Hash.java similarity index 98% rename from api/src/main/java/com/bitsofproof/supernode/api/Hash.java rename to api/src/main/java/com/bitsofproof/supernode/common/Hash.java index 63cb593e..ebd737a1 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/Hash.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/Hash.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.bitsofproof.supernode.api; +package com.bitsofproof.supernode.common; import java.math.BigInteger; import java.security.MessageDigest; @@ -21,6 +21,7 @@ import org.bouncycastle.crypto.digests.RIPEMD160Digest; + public class Hash { private final byte[] bytes; diff --git a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryBusConnectionFactory.java b/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java similarity index 99% rename from api/src/main/java/com/bitsofproof/supernode/api/InMemoryBusConnectionFactory.java rename to api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java index 9cff8866..92e679b8 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryBusConnectionFactory.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.bitsofproof.supernode.api; +package com.bitsofproof.supernode.common; import java.io.Serializable; import java.security.SecureRandom; diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Key.java b/api/src/main/java/com/bitsofproof/supernode/common/Key.java similarity index 95% rename from api/src/main/java/com/bitsofproof/supernode/api/Key.java rename to api/src/main/java/com/bitsofproof/supernode/common/Key.java index 7073dd8f..0399cd61 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/Key.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/Key.java @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.bitsofproof.supernode.api; +package com.bitsofproof.supernode.common; + public interface Key extends Cloneable { diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ScriptFormat.java b/api/src/main/java/com/bitsofproof/supernode/common/ScriptFormat.java similarity index 99% rename from api/src/main/java/com/bitsofproof/supernode/api/ScriptFormat.java rename to api/src/main/java/com/bitsofproof/supernode/common/ScriptFormat.java index 3d916cc9..d48f629d 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ScriptFormat.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/ScriptFormat.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.bitsofproof.supernode.api; +package com.bitsofproof.supernode.common; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -22,6 +22,7 @@ import java.util.List; import java.util.StringTokenizer; + public class ScriptFormat { // unfortunatelly unused: https://bitcointalk.org/index.php?topic=120836.0 diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ValidationException.java b/api/src/main/java/com/bitsofproof/supernode/common/ValidationException.java similarity index 95% rename from api/src/main/java/com/bitsofproof/supernode/api/ValidationException.java rename to api/src/main/java/com/bitsofproof/supernode/common/ValidationException.java index a6484481..893e7f22 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ValidationException.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/ValidationException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.bitsofproof.supernode.api; +package com.bitsofproof.supernode.common; public class ValidationException extends Exception { diff --git a/api/src/main/java/com/bitsofproof/supernode/api/WireFormat.java b/api/src/main/java/com/bitsofproof/supernode/common/WireFormat.java similarity index 99% rename from api/src/main/java/com/bitsofproof/supernode/api/WireFormat.java rename to api/src/main/java/com/bitsofproof/supernode/common/WireFormat.java index 9003226b..07f1264d 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/WireFormat.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/WireFormat.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.bitsofproof.supernode.api; +package com.bitsofproof.supernode.common; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -21,6 +21,7 @@ import java.net.InetAddress; import java.net.UnknownHostException; + public class WireFormat { diff --git a/api/src/test/java/com/bitsofproof/supernode/api/BloomFilterTest.java b/api/src/test/java/com/bitsofproof/supernode/api/BloomFilterTest.java index 630a6a65..19d660d3 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/BloomFilterTest.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/BloomFilterTest.java @@ -23,7 +23,8 @@ import org.junit.Test; -import com.bitsofproof.supernode.api.BloomFilter.UpdateMode; +import com.bitsofproof.supernode.common.BloomFilter; +import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; public class BloomFilterTest { diff --git a/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java b/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java index b7fd088e..0512f738 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java @@ -29,6 +29,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.bitsofproof.supernode.common.ECKeyPair; +import com.bitsofproof.supernode.common.ECPublicKey; +import com.bitsofproof.supernode.common.Key; +import com.bitsofproof.supernode.common.ValidationException; + public class ExtendedKeyTest { private final SecureRandom random = new SecureRandom (); diff --git a/api/src/test/java/com/bitsofproof/supernode/api/InMemoryBusTest.java b/api/src/test/java/com/bitsofproof/supernode/api/InMemoryBusTest.java index c556df09..4455b53f 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/InMemoryBusTest.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/InMemoryBusTest.java @@ -32,6 +32,8 @@ import org.junit.Test; +import com.bitsofproof.supernode.common.InMemoryBusConnectionFactory; + public class InMemoryBusTest { private final Semaphore ready = new Semaphore (0); diff --git a/api/src/test/java/com/bitsofproof/supernode/api/KeySerializerTest.java b/api/src/test/java/com/bitsofproof/supernode/api/KeySerializerTest.java index 90552ad9..91b58b98 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/KeySerializerTest.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/KeySerializerTest.java @@ -29,6 +29,10 @@ import org.junit.BeforeClass; import org.junit.Test; +import com.bitsofproof.supernode.common.ECKeyPair; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ValidationException; + public class KeySerializerTest { @BeforeClass diff --git a/api/src/test/java/com/bitsofproof/supernode/api/WalletFormatTest.java b/api/src/test/java/com/bitsofproof/supernode/api/WalletFormatTest.java index d509ba2b..7fa9e555 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/WalletFormatTest.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/WalletFormatTest.java @@ -26,7 +26,11 @@ import org.junit.BeforeClass; import org.junit.Test; -import com.bitsofproof.supernode.api.ScriptFormat.Token; +import com.bitsofproof.supernode.common.ByteVector; +import com.bitsofproof.supernode.common.Key; +import com.bitsofproof.supernode.common.ScriptFormat; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.ScriptFormat.Token; public class WalletFormatTest { diff --git a/server/src/main/java/com/bitsofproof/supernode/core/AddressHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/AddressHandler.java index e44258cb..bb15696d 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/AddressHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/AddressHandler.java @@ -20,9 +20,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.bitsofproof.supernode.api.ValidationException; -import com.bitsofproof.supernode.api.WireFormat; -import com.bitsofproof.supernode.api.WireFormat.Address; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.WireFormat; +import com.bitsofproof.supernode.common.WireFormat.Address; import com.bitsofproof.supernode.messages.AddrMessage; import com.bitsofproof.supernode.messages.BitcoinMessageListener; import com.bitsofproof.supernode.messages.GetAddrMessage; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/AddressSeeder.java b/server/src/main/java/com/bitsofproof/supernode/core/AddressSeeder.java index 6bdff0ad..b16e5ee0 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/AddressSeeder.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/AddressSeeder.java @@ -22,7 +22,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.bitsofproof.supernode.api.WireFormat.Address; +import com.bitsofproof.supernode.common.WireFormat.Address; import com.bitsofproof.supernode.messages.AddrMessage; public class AddressSeeder implements Runnable diff --git a/server/src/main/java/com/bitsofproof/supernode/core/BitcoinPeer.java b/server/src/main/java/com/bitsofproof/supernode/core/BitcoinPeer.java index 2d30e8a2..ac39963f 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/BitcoinPeer.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/BitcoinPeer.java @@ -32,10 +32,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.bitsofproof.supernode.api.BloomFilter; -import com.bitsofproof.supernode.api.Hash; -import com.bitsofproof.supernode.api.ValidationException; -import com.bitsofproof.supernode.api.WireFormat; +import com.bitsofproof.supernode.common.BloomFilter; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.WireFormat; import com.bitsofproof.supernode.messages.AddrMessage; import com.bitsofproof.supernode.messages.AlertMessage; import com.bitsofproof.supernode.messages.BitcoinMessageListener; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java index 05d9a251..aa46eb32 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java @@ -18,10 +18,10 @@ import java.util.List; import java.util.Set; -import com.bitsofproof.supernode.api.BloomFilter; -import com.bitsofproof.supernode.api.BloomFilter.UpdateMode; -import com.bitsofproof.supernode.api.ByteVector; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.BloomFilter; +import com.bitsofproof.supernode.common.ByteVector; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.model.Blk; import com.bitsofproof.supernode.model.Tx; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/BlocktesterChain.java b/server/src/main/java/com/bitsofproof/supernode/core/BlocktesterChain.java index 0c41b1f2..047b4509 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/BlocktesterChain.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/BlocktesterChain.java @@ -21,9 +21,9 @@ import org.bouncycastle.util.encoders.Hex; -import com.bitsofproof.supernode.api.ByteUtils; -import com.bitsofproof.supernode.api.Hash; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.model.Blk; import com.bitsofproof.supernode.model.Tx; import com.bitsofproof.supernode.model.TxIn; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index df90225d..43a25879 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -43,17 +43,17 @@ import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; -import com.bitsofproof.supernode.api.BloomFilter; -import com.bitsofproof.supernode.api.BloomFilter.UpdateMode; -import com.bitsofproof.supernode.api.ByteUtils; -import com.bitsofproof.supernode.api.ByteVector; import com.bitsofproof.supernode.api.ColorRules; import com.bitsofproof.supernode.api.ColorRules.ColoredCoin; -import com.bitsofproof.supernode.api.Hash; -import com.bitsofproof.supernode.api.ScriptFormat; -import com.bitsofproof.supernode.api.ScriptFormat.Token; -import com.bitsofproof.supernode.api.ValidationException; -import com.bitsofproof.supernode.api.WireFormat; +import com.bitsofproof.supernode.common.BloomFilter; +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.ByteVector; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ScriptFormat; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.WireFormat; +import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; +import com.bitsofproof.supernode.common.ScriptFormat.Token; import com.bitsofproof.supernode.model.Blk; import com.bitsofproof.supernode.model.Head; import com.bitsofproof.supernode.model.StoredColor; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ChainLoader.java b/server/src/main/java/com/bitsofproof/supernode/core/ChainLoader.java index 0bea31d4..f0d049b4 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ChainLoader.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ChainLoader.java @@ -30,8 +30,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.bitsofproof.supernode.api.Hash; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.messages.BitcoinMessageListener; import com.bitsofproof.supernode.messages.BlockMessage; import com.bitsofproof.supernode.messages.GetBlocksMessage; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ColorStore.java b/server/src/main/java/com/bitsofproof/supernode/core/ColorStore.java index e7b00222..b02fc578 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ColorStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ColorStore.java @@ -15,7 +15,7 @@ */ package com.bitsofproof.supernode.core; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.model.StoredColor; public interface ColorStore diff --git a/server/src/main/java/com/bitsofproof/supernode/core/GetBlocksHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/GetBlocksHandler.java index e4d00c1e..e20ac3d4 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/GetBlocksHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/GetBlocksHandler.java @@ -21,7 +21,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.bitsofproof.supernode.api.Hash; +import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.messages.BitcoinMessageListener; import com.bitsofproof.supernode.messages.GetBlocksMessage; import com.bitsofproof.supernode.messages.InvMessage; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/GetDataHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/GetDataHandler.java index b07187d3..6c729b9d 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/GetDataHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/GetDataHandler.java @@ -22,8 +22,8 @@ import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import com.bitsofproof.supernode.api.Hash; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.messages.BitcoinMessageListener; import com.bitsofproof.supernode.messages.BlockMessage; import com.bitsofproof.supernode.messages.GetDataMessage; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/GetHeadersHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/GetHeadersHandler.java index 0da8e1ca..e2ee4edc 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/GetHeadersHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/GetHeadersHandler.java @@ -25,9 +25,9 @@ import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import com.bitsofproof.supernode.api.Hash; -import com.bitsofproof.supernode.api.ValidationException; -import com.bitsofproof.supernode.api.WireFormat; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.WireFormat; import com.bitsofproof.supernode.messages.BitcoinMessageListener; import com.bitsofproof.supernode.messages.GetHeadersMessage; import com.bitsofproof.supernode.messages.HeadersMessage; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/HeartbeatHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/HeartbeatHandler.java index 972cf87e..b2e68a08 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/HeartbeatHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/HeartbeatHandler.java @@ -23,7 +23,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.messages.BitcoinMessageListener; import com.bitsofproof.supernode.messages.PingMessage; import com.bitsofproof.supernode.messages.PongMessage; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/IRCDiscovery.java b/server/src/main/java/com/bitsofproof/supernode/core/IRCDiscovery.java index f23d8482..25b61a43 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/IRCDiscovery.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/IRCDiscovery.java @@ -31,8 +31,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.bitsofproof.supernode.api.ByteUtils; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.ValidationException; + public class IRCDiscovery implements Discovery { diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index 40865e30..49690f8d 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -46,15 +46,15 @@ import com.bitsofproof.supernode.api.BCSAPIMessage; import com.bitsofproof.supernode.api.Block; -import com.bitsofproof.supernode.api.BloomFilter; -import com.bitsofproof.supernode.api.BloomFilter.UpdateMode; -import com.bitsofproof.supernode.api.ByteVector; import com.bitsofproof.supernode.api.Color; -import com.bitsofproof.supernode.api.Hash; import com.bitsofproof.supernode.api.Transaction; import com.bitsofproof.supernode.api.TrunkUpdateMessage; -import com.bitsofproof.supernode.api.ValidationException; -import com.bitsofproof.supernode.api.WireFormat; +import com.bitsofproof.supernode.common.BloomFilter; +import com.bitsofproof.supernode.common.ByteVector; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.WireFormat; +import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.core.BlockStore.TransactionProcessor; import com.bitsofproof.supernode.messages.BlockMessage; import com.bitsofproof.supernode.model.Blk; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/PeerStore.java b/server/src/main/java/com/bitsofproof/supernode/core/PeerStore.java index ee9ca825..f042f6e2 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/PeerStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/PeerStore.java @@ -18,7 +18,7 @@ import java.net.InetAddress; import java.util.Collection; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.model.KnownPeer; public interface PeerStore diff --git a/server/src/main/java/com/bitsofproof/supernode/core/SatoshiChain.java b/server/src/main/java/com/bitsofproof/supernode/core/SatoshiChain.java index c87b9943..67641378 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/SatoshiChain.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/SatoshiChain.java @@ -21,9 +21,9 @@ import org.bouncycastle.util.encoders.Hex; -import com.bitsofproof.supernode.api.ByteUtils; -import com.bitsofproof.supernode.api.Hash; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.model.Blk; import com.bitsofproof.supernode.model.Tx; import com.bitsofproof.supernode.model.TxIn; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ScriptEvaluation.java b/server/src/main/java/com/bitsofproof/supernode/core/ScriptEvaluation.java index 280642fd..672236a1 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ScriptEvaluation.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ScriptEvaluation.java @@ -27,13 +27,13 @@ import org.bouncycastle.crypto.digests.RIPEMD160Digest; -import com.bitsofproof.supernode.api.ByteUtils; -import com.bitsofproof.supernode.api.ECKeyPair; -import com.bitsofproof.supernode.api.Hash; -import com.bitsofproof.supernode.api.ScriptFormat; -import com.bitsofproof.supernode.api.ScriptFormat.Opcode; -import com.bitsofproof.supernode.api.ValidationException; -import com.bitsofproof.supernode.api.WireFormat; +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.ECKeyPair; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ScriptFormat; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.WireFormat; +import com.bitsofproof.supernode.common.ScriptFormat.Opcode; import com.bitsofproof.supernode.model.Tx; import com.bitsofproof.supernode.model.TxIn; import com.bitsofproof.supernode.model.TxOut; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/Testnet3Chain.java b/server/src/main/java/com/bitsofproof/supernode/core/Testnet3Chain.java index 29958634..63489930 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/Testnet3Chain.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/Testnet3Chain.java @@ -21,9 +21,9 @@ import org.bouncycastle.util.encoders.Hex; -import com.bitsofproof.supernode.api.ByteUtils; -import com.bitsofproof.supernode.api.Hash; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.model.Blk; import com.bitsofproof.supernode.model.Tx; import com.bitsofproof.supernode.model.TxIn; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TransactionValidationException.java b/server/src/main/java/com/bitsofproof/supernode/core/TransactionValidationException.java index cb0dc2f8..eb66c89f 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TransactionValidationException.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TransactionValidationException.java @@ -15,7 +15,7 @@ */ package com.bitsofproof.supernode.core; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.model.Tx; public class TransactionValidationException extends ValidationException diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index 4d260b86..3fe0beea 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -31,10 +31,10 @@ import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; -import com.bitsofproof.supernode.api.BloomFilter.UpdateMode; -import com.bitsofproof.supernode.api.ByteVector; -import com.bitsofproof.supernode.api.Hash; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.ByteVector; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.core.BlockStore.TransactionProcessor; import com.bitsofproof.supernode.messages.BitcoinMessageListener; import com.bitsofproof.supernode.messages.GetDataMessage; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/UnitTestChain.java b/server/src/main/java/com/bitsofproof/supernode/core/UnitTestChain.java index 99d1c96c..9bf61ba5 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/UnitTestChain.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/UnitTestChain.java @@ -21,9 +21,9 @@ import org.bouncycastle.util.encoders.Hex; -import com.bitsofproof.supernode.api.ByteUtils; -import com.bitsofproof.supernode.api.Hash; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.model.Blk; import com.bitsofproof.supernode.model.Tx; import com.bitsofproof.supernode.model.TxIn; diff --git a/server/src/main/java/com/bitsofproof/supernode/main/Supernode.java b/server/src/main/java/com/bitsofproof/supernode/main/Supernode.java index 3937caed..33db6a94 100644 --- a/server/src/main/java/com/bitsofproof/supernode/main/Supernode.java +++ b/server/src/main/java/com/bitsofproof/supernode/main/Supernode.java @@ -25,7 +25,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.core.BitcoinNetwork; public class Supernode extends Main implements Main.App diff --git a/server/src/main/java/com/bitsofproof/supernode/messages/AddrMessage.java b/server/src/main/java/com/bitsofproof/supernode/messages/AddrMessage.java index e7fb7f1b..e17522dc 100644 --- a/server/src/main/java/com/bitsofproof/supernode/messages/AddrMessage.java +++ b/server/src/main/java/com/bitsofproof/supernode/messages/AddrMessage.java @@ -18,9 +18,9 @@ import java.util.ArrayList; import java.util.List; -import com.bitsofproof.supernode.api.WireFormat; -import com.bitsofproof.supernode.api.WireFormat.Reader; -import com.bitsofproof.supernode.api.WireFormat.Writer; +import com.bitsofproof.supernode.common.WireFormat; +import com.bitsofproof.supernode.common.WireFormat.Reader; +import com.bitsofproof.supernode.common.WireFormat.Writer; import com.bitsofproof.supernode.core.BitcoinPeer; public class AddrMessage extends BitcoinPeer.Message diff --git a/server/src/main/java/com/bitsofproof/supernode/messages/AlertMessage.java b/server/src/main/java/com/bitsofproof/supernode/messages/AlertMessage.java index 5478add9..b8889e8a 100644 --- a/server/src/main/java/com/bitsofproof/supernode/messages/AlertMessage.java +++ b/server/src/main/java/com/bitsofproof/supernode/messages/AlertMessage.java @@ -17,10 +17,10 @@ import java.io.UnsupportedEncodingException; -import com.bitsofproof.supernode.api.ECKeyPair; -import com.bitsofproof.supernode.api.Hash; -import com.bitsofproof.supernode.api.WireFormat.Reader; -import com.bitsofproof.supernode.api.WireFormat.Writer; +import com.bitsofproof.supernode.common.ECKeyPair; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.WireFormat.Reader; +import com.bitsofproof.supernode.common.WireFormat.Writer; import com.bitsofproof.supernode.core.BitcoinPeer; public class AlertMessage extends BitcoinPeer.Message diff --git a/server/src/main/java/com/bitsofproof/supernode/messages/BlockMessage.java b/server/src/main/java/com/bitsofproof/supernode/messages/BlockMessage.java index cc5a66c9..270563e7 100644 --- a/server/src/main/java/com/bitsofproof/supernode/messages/BlockMessage.java +++ b/server/src/main/java/com/bitsofproof/supernode/messages/BlockMessage.java @@ -15,8 +15,8 @@ */ package com.bitsofproof.supernode.messages; -import com.bitsofproof.supernode.api.WireFormat.Reader; -import com.bitsofproof.supernode.api.WireFormat.Writer; +import com.bitsofproof.supernode.common.WireFormat.Reader; +import com.bitsofproof.supernode.common.WireFormat.Writer; import com.bitsofproof.supernode.core.BitcoinPeer; import com.bitsofproof.supernode.model.Blk; diff --git a/server/src/main/java/com/bitsofproof/supernode/messages/FilterAddMessage.java b/server/src/main/java/com/bitsofproof/supernode/messages/FilterAddMessage.java index d2345fee..a53766ff 100644 --- a/server/src/main/java/com/bitsofproof/supernode/messages/FilterAddMessage.java +++ b/server/src/main/java/com/bitsofproof/supernode/messages/FilterAddMessage.java @@ -15,8 +15,8 @@ */ package com.bitsofproof.supernode.messages; -import com.bitsofproof.supernode.api.WireFormat.Reader; -import com.bitsofproof.supernode.api.WireFormat.Writer; +import com.bitsofproof.supernode.common.WireFormat.Reader; +import com.bitsofproof.supernode.common.WireFormat.Writer; import com.bitsofproof.supernode.core.BitcoinPeer; public class FilterAddMessage extends BitcoinPeer.Message diff --git a/server/src/main/java/com/bitsofproof/supernode/messages/FilterClearMessage.java b/server/src/main/java/com/bitsofproof/supernode/messages/FilterClearMessage.java index 2aef5a61..dcea2a19 100644 --- a/server/src/main/java/com/bitsofproof/supernode/messages/FilterClearMessage.java +++ b/server/src/main/java/com/bitsofproof/supernode/messages/FilterClearMessage.java @@ -15,8 +15,8 @@ */ package com.bitsofproof.supernode.messages; -import com.bitsofproof.supernode.api.WireFormat.Reader; -import com.bitsofproof.supernode.api.WireFormat.Writer; +import com.bitsofproof.supernode.common.WireFormat.Reader; +import com.bitsofproof.supernode.common.WireFormat.Writer; import com.bitsofproof.supernode.core.BitcoinPeer; public class FilterClearMessage extends BitcoinPeer.Message diff --git a/server/src/main/java/com/bitsofproof/supernode/messages/FilterLoadMessage.java b/server/src/main/java/com/bitsofproof/supernode/messages/FilterLoadMessage.java index e9658c96..977f91c0 100644 --- a/server/src/main/java/com/bitsofproof/supernode/messages/FilterLoadMessage.java +++ b/server/src/main/java/com/bitsofproof/supernode/messages/FilterLoadMessage.java @@ -15,9 +15,9 @@ */ package com.bitsofproof.supernode.messages; -import com.bitsofproof.supernode.api.BloomFilter; -import com.bitsofproof.supernode.api.WireFormat.Reader; -import com.bitsofproof.supernode.api.WireFormat.Writer; +import com.bitsofproof.supernode.common.BloomFilter; +import com.bitsofproof.supernode.common.WireFormat.Reader; +import com.bitsofproof.supernode.common.WireFormat.Writer; import com.bitsofproof.supernode.core.BitcoinPeer; public class FilterLoadMessage extends BitcoinPeer.Message diff --git a/server/src/main/java/com/bitsofproof/supernode/messages/GetBlocksMessage.java b/server/src/main/java/com/bitsofproof/supernode/messages/GetBlocksMessage.java index 115316cd..775a7548 100644 --- a/server/src/main/java/com/bitsofproof/supernode/messages/GetBlocksMessage.java +++ b/server/src/main/java/com/bitsofproof/supernode/messages/GetBlocksMessage.java @@ -18,8 +18,8 @@ import java.util.ArrayList; import java.util.List; -import com.bitsofproof.supernode.api.WireFormat.Reader; -import com.bitsofproof.supernode.api.WireFormat.Writer; +import com.bitsofproof.supernode.common.WireFormat.Reader; +import com.bitsofproof.supernode.common.WireFormat.Writer; import com.bitsofproof.supernode.core.BitcoinPeer; public class GetBlocksMessage extends BitcoinPeer.Message diff --git a/server/src/main/java/com/bitsofproof/supernode/messages/GetDataMessage.java b/server/src/main/java/com/bitsofproof/supernode/messages/GetDataMessage.java index 12199604..6a1345af 100644 --- a/server/src/main/java/com/bitsofproof/supernode/messages/GetDataMessage.java +++ b/server/src/main/java/com/bitsofproof/supernode/messages/GetDataMessage.java @@ -18,8 +18,8 @@ import java.util.ArrayList; import java.util.List; -import com.bitsofproof.supernode.api.WireFormat.Reader; -import com.bitsofproof.supernode.api.WireFormat.Writer; +import com.bitsofproof.supernode.common.WireFormat.Reader; +import com.bitsofproof.supernode.common.WireFormat.Writer; import com.bitsofproof.supernode.core.BitcoinPeer; public class GetDataMessage extends BitcoinPeer.Message diff --git a/server/src/main/java/com/bitsofproof/supernode/messages/GetHeadersMessage.java b/server/src/main/java/com/bitsofproof/supernode/messages/GetHeadersMessage.java index 0e2af278..21fcb74f 100644 --- a/server/src/main/java/com/bitsofproof/supernode/messages/GetHeadersMessage.java +++ b/server/src/main/java/com/bitsofproof/supernode/messages/GetHeadersMessage.java @@ -18,8 +18,8 @@ import java.util.ArrayList; import java.util.List; -import com.bitsofproof.supernode.api.WireFormat.Reader; -import com.bitsofproof.supernode.api.WireFormat.Writer; +import com.bitsofproof.supernode.common.WireFormat.Reader; +import com.bitsofproof.supernode.common.WireFormat.Writer; import com.bitsofproof.supernode.core.BitcoinPeer; public class GetHeadersMessage extends BitcoinPeer.Message diff --git a/server/src/main/java/com/bitsofproof/supernode/messages/HeadersMessage.java b/server/src/main/java/com/bitsofproof/supernode/messages/HeadersMessage.java index 12c889f6..eedcdc44 100644 --- a/server/src/main/java/com/bitsofproof/supernode/messages/HeadersMessage.java +++ b/server/src/main/java/com/bitsofproof/supernode/messages/HeadersMessage.java @@ -18,8 +18,8 @@ import java.util.ArrayList; import java.util.List; -import com.bitsofproof.supernode.api.WireFormat.Reader; -import com.bitsofproof.supernode.api.WireFormat.Writer; +import com.bitsofproof.supernode.common.WireFormat.Reader; +import com.bitsofproof.supernode.common.WireFormat.Writer; import com.bitsofproof.supernode.core.BitcoinPeer; public class HeadersMessage extends BitcoinPeer.Message diff --git a/server/src/main/java/com/bitsofproof/supernode/messages/InvMessage.java b/server/src/main/java/com/bitsofproof/supernode/messages/InvMessage.java index e8d33e12..80d74768 100644 --- a/server/src/main/java/com/bitsofproof/supernode/messages/InvMessage.java +++ b/server/src/main/java/com/bitsofproof/supernode/messages/InvMessage.java @@ -18,8 +18,8 @@ import java.util.ArrayList; import java.util.List; -import com.bitsofproof.supernode.api.WireFormat.Reader; -import com.bitsofproof.supernode.api.WireFormat.Writer; +import com.bitsofproof.supernode.common.WireFormat.Reader; +import com.bitsofproof.supernode.common.WireFormat.Writer; import com.bitsofproof.supernode.core.BitcoinPeer; public class InvMessage extends BitcoinPeer.Message diff --git a/server/src/main/java/com/bitsofproof/supernode/messages/MerkleBlockMessage.java b/server/src/main/java/com/bitsofproof/supernode/messages/MerkleBlockMessage.java index 79ba4266..fc7fb8d9 100644 --- a/server/src/main/java/com/bitsofproof/supernode/messages/MerkleBlockMessage.java +++ b/server/src/main/java/com/bitsofproof/supernode/messages/MerkleBlockMessage.java @@ -21,12 +21,12 @@ import java.util.ArrayList; import java.util.List; -import com.bitsofproof.supernode.api.BinaryAggregator; -import com.bitsofproof.supernode.api.BloomFilter; -import com.bitsofproof.supernode.api.ByteUtils; -import com.bitsofproof.supernode.api.Hash; -import com.bitsofproof.supernode.api.WireFormat.Reader; -import com.bitsofproof.supernode.api.WireFormat.Writer; +import com.bitsofproof.supernode.common.BinaryAggregator; +import com.bitsofproof.supernode.common.BloomFilter; +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.WireFormat.Reader; +import com.bitsofproof.supernode.common.WireFormat.Writer; import com.bitsofproof.supernode.core.BitcoinPeer; import com.bitsofproof.supernode.model.Blk; import com.bitsofproof.supernode.model.Tx; diff --git a/server/src/main/java/com/bitsofproof/supernode/messages/PingMessage.java b/server/src/main/java/com/bitsofproof/supernode/messages/PingMessage.java index 7167687b..c6a1ce56 100644 --- a/server/src/main/java/com/bitsofproof/supernode/messages/PingMessage.java +++ b/server/src/main/java/com/bitsofproof/supernode/messages/PingMessage.java @@ -17,8 +17,8 @@ import java.security.SecureRandom; -import com.bitsofproof.supernode.api.WireFormat.Reader; -import com.bitsofproof.supernode.api.WireFormat.Writer; +import com.bitsofproof.supernode.common.WireFormat.Reader; +import com.bitsofproof.supernode.common.WireFormat.Writer; import com.bitsofproof.supernode.core.BitcoinPeer; public class PingMessage extends BitcoinPeer.Message diff --git a/server/src/main/java/com/bitsofproof/supernode/messages/PongMessage.java b/server/src/main/java/com/bitsofproof/supernode/messages/PongMessage.java index 4041d94b..43065c10 100644 --- a/server/src/main/java/com/bitsofproof/supernode/messages/PongMessage.java +++ b/server/src/main/java/com/bitsofproof/supernode/messages/PongMessage.java @@ -15,8 +15,8 @@ */ package com.bitsofproof.supernode.messages; -import com.bitsofproof.supernode.api.WireFormat.Reader; -import com.bitsofproof.supernode.api.WireFormat.Writer; +import com.bitsofproof.supernode.common.WireFormat.Reader; +import com.bitsofproof.supernode.common.WireFormat.Writer; import com.bitsofproof.supernode.core.BitcoinPeer; public class PongMessage extends BitcoinPeer.Message diff --git a/server/src/main/java/com/bitsofproof/supernode/messages/TxMessage.java b/server/src/main/java/com/bitsofproof/supernode/messages/TxMessage.java index 43aa2a26..beec80bd 100644 --- a/server/src/main/java/com/bitsofproof/supernode/messages/TxMessage.java +++ b/server/src/main/java/com/bitsofproof/supernode/messages/TxMessage.java @@ -15,8 +15,8 @@ */ package com.bitsofproof.supernode.messages; -import com.bitsofproof.supernode.api.WireFormat.Reader; -import com.bitsofproof.supernode.api.WireFormat.Writer; +import com.bitsofproof.supernode.common.WireFormat.Reader; +import com.bitsofproof.supernode.common.WireFormat.Writer; import com.bitsofproof.supernode.core.BitcoinPeer; import com.bitsofproof.supernode.model.Tx; diff --git a/server/src/main/java/com/bitsofproof/supernode/messages/VersionMessage.java b/server/src/main/java/com/bitsofproof/supernode/messages/VersionMessage.java index 6b206212..26db5667 100644 --- a/server/src/main/java/com/bitsofproof/supernode/messages/VersionMessage.java +++ b/server/src/main/java/com/bitsofproof/supernode/messages/VersionMessage.java @@ -18,9 +18,9 @@ import java.net.InetAddress; import java.net.UnknownHostException; -import com.bitsofproof.supernode.api.WireFormat; -import com.bitsofproof.supernode.api.WireFormat.Reader; -import com.bitsofproof.supernode.api.WireFormat.Writer; +import com.bitsofproof.supernode.common.WireFormat; +import com.bitsofproof.supernode.common.WireFormat.Reader; +import com.bitsofproof.supernode.common.WireFormat.Writer; import com.bitsofproof.supernode.core.BitcoinPeer; public class VersionMessage extends BitcoinPeer.Message diff --git a/server/src/main/java/com/bitsofproof/supernode/model/Blk.java b/server/src/main/java/com/bitsofproof/supernode/model/Blk.java index 8e274aa3..4d9fffc4 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/Blk.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/Blk.java @@ -35,11 +35,11 @@ import org.json.JSONException; import org.json.JSONObject; -import com.bitsofproof.supernode.api.BinaryAggregator; -import com.bitsofproof.supernode.api.ByteUtils; -import com.bitsofproof.supernode.api.Hash; -import com.bitsofproof.supernode.api.ValidationException; -import com.bitsofproof.supernode.api.WireFormat; +import com.bitsofproof.supernode.common.BinaryAggregator; +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.WireFormat; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; diff --git a/server/src/main/java/com/bitsofproof/supernode/model/Head.java b/server/src/main/java/com/bitsofproof/supernode/model/Head.java index 84b04d50..8755503b 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/Head.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/Head.java @@ -23,8 +23,8 @@ import javax.persistence.Id; import javax.persistence.Table; -import com.bitsofproof.supernode.api.Hash; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ValidationException; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; diff --git a/server/src/main/java/com/bitsofproof/supernode/model/JpaStore.java b/server/src/main/java/com/bitsofproof/supernode/model/JpaStore.java index 9c5917c8..75bfa6c1 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/JpaStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/JpaStore.java @@ -36,9 +36,9 @@ import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; -import com.bitsofproof.supernode.api.BloomFilter; -import com.bitsofproof.supernode.api.Hash; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.BloomFilter; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.core.CachedBlockStore; import com.bitsofproof.supernode.core.ColorStore; import com.bitsofproof.supernode.core.Discovery; diff --git a/server/src/main/java/com/bitsofproof/supernode/model/KnownPeer.java b/server/src/main/java/com/bitsofproof/supernode/model/KnownPeer.java index de0a8c38..e4bb5533 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/KnownPeer.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/KnownPeer.java @@ -23,7 +23,7 @@ import javax.persistence.Id; import javax.persistence.Table; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.ValidationException; import com.google.protobuf.InvalidProtocolBufferException; @Entity diff --git a/server/src/main/java/com/bitsofproof/supernode/model/LvlDiskStore.java b/server/src/main/java/com/bitsofproof/supernode/model/LvlDiskStore.java index a609124e..b9ac4922 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/LvlDiskStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/LvlDiskStore.java @@ -29,7 +29,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.bitsofproof.supernode.api.ByteUtils; +import com.bitsofproof.supernode.common.ByteUtils; import com.bitsofproof.supernode.model.OrderedMapStoreKey.KeyType; public class LvlDiskStore implements OrderedMapStore diff --git a/server/src/main/java/com/bitsofproof/supernode/model/LvlStore.java b/server/src/main/java/com/bitsofproof/supernode/model/LvlStore.java index e31024b9..299febc6 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/LvlStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/LvlStore.java @@ -30,10 +30,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.bitsofproof.supernode.api.BloomFilter; -import com.bitsofproof.supernode.api.Hash; -import com.bitsofproof.supernode.api.ValidationException; -import com.bitsofproof.supernode.api.WireFormat; +import com.bitsofproof.supernode.common.BloomFilter; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.WireFormat; import com.bitsofproof.supernode.core.CachedBlockStore; import com.bitsofproof.supernode.core.ColorStore; import com.bitsofproof.supernode.core.Discovery; diff --git a/server/src/main/java/com/bitsofproof/supernode/model/StoredColor.java b/server/src/main/java/com/bitsofproof/supernode/model/StoredColor.java index d1d7295a..1ec60654 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/StoredColor.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/StoredColor.java @@ -27,9 +27,9 @@ import javax.persistence.Lob; import javax.persistence.Table; -import com.bitsofproof.supernode.api.ECKeyPair; -import com.bitsofproof.supernode.api.Hash; -import com.bitsofproof.supernode.api.WireFormat; +import com.bitsofproof.supernode.common.ECKeyPair; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.WireFormat; @Entity @Table (name = "color") diff --git a/server/src/main/java/com/bitsofproof/supernode/model/Tx.java b/server/src/main/java/com/bitsofproof/supernode/model/Tx.java index 22f3ff8f..6daa9079 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/Tx.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/Tx.java @@ -34,15 +34,15 @@ import org.json.JSONException; import org.json.JSONObject; -import com.bitsofproof.supernode.api.BloomFilter; -import com.bitsofproof.supernode.api.BloomFilter.UpdateMode; -import com.bitsofproof.supernode.api.ByteUtils; -import com.bitsofproof.supernode.api.ByteVector; -import com.bitsofproof.supernode.api.Hash; -import com.bitsofproof.supernode.api.ScriptFormat; -import com.bitsofproof.supernode.api.ScriptFormat.Token; -import com.bitsofproof.supernode.api.ValidationException; -import com.bitsofproof.supernode.api.WireFormat; +import com.bitsofproof.supernode.common.BloomFilter; +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.ByteVector; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ScriptFormat; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.WireFormat; +import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; +import com.bitsofproof.supernode.common.ScriptFormat.Token; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; diff --git a/server/src/main/java/com/bitsofproof/supernode/model/TxIn.java b/server/src/main/java/com/bitsofproof/supernode/model/TxIn.java index 41ae58b7..d2d82799 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/TxIn.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/TxIn.java @@ -31,11 +31,11 @@ import org.json.JSONException; import org.json.JSONObject; -import com.bitsofproof.supernode.api.ByteUtils; -import com.bitsofproof.supernode.api.Hash; -import com.bitsofproof.supernode.api.ScriptFormat; -import com.bitsofproof.supernode.api.ValidationException; -import com.bitsofproof.supernode.api.WireFormat; +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ScriptFormat; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.WireFormat; @Entity @Table (name = "txin") diff --git a/server/src/main/java/com/bitsofproof/supernode/model/TxOut.java b/server/src/main/java/com/bitsofproof/supernode/model/TxOut.java index c8de6cfa..9fd10119 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/TxOut.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/TxOut.java @@ -31,10 +31,10 @@ import org.json.JSONException; import org.json.JSONObject; -import com.bitsofproof.supernode.api.ByteUtils; -import com.bitsofproof.supernode.api.ScriptFormat; -import com.bitsofproof.supernode.api.ValidationException; -import com.bitsofproof.supernode.api.WireFormat; +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.ScriptFormat; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.WireFormat; @Entity @Table (name = "txout") diff --git a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java index b1134a13..87842cb1 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java @@ -41,12 +41,12 @@ import com.bitsofproof.supernode.api.BCSAPI; import com.bitsofproof.supernode.api.BCSAPIException; import com.bitsofproof.supernode.api.Block; -import com.bitsofproof.supernode.api.Hash; import com.bitsofproof.supernode.api.SerializedWallet; import com.bitsofproof.supernode.api.Transaction; import com.bitsofproof.supernode.api.TransactionListener; import com.bitsofproof.supernode.api.TrunkListener; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.core.BlockStore; import com.bitsofproof.supernode.core.Chain; import com.bitsofproof.supernode.core.Difficulty; diff --git a/server/src/test/java/com/bitsofproof/supernode/test/AddressTest.java b/server/src/test/java/com/bitsofproof/supernode/test/AddressTest.java index 5a5f53ba..7a35248d 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/AddressTest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/AddressTest.java @@ -25,8 +25,8 @@ import org.junit.Test; import com.bitsofproof.supernode.api.AddressConverter; -import com.bitsofproof.supernode.api.ByteUtils; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.ValidationException; public class AddressTest { diff --git a/server/src/test/java/com/bitsofproof/supernode/test/Base58Test.java b/server/src/test/java/com/bitsofproof/supernode/test/Base58Test.java index ac7aa421..28c284e5 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/Base58Test.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/Base58Test.java @@ -24,8 +24,8 @@ import org.json.JSONException; import org.junit.Test; -import com.bitsofproof.supernode.api.ByteUtils; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.ValidationException; /** * These are the unit tests from bitcoind that are supported with test data in json format diff --git a/server/src/test/java/com/bitsofproof/supernode/test/BitcoinJBlockTest.java b/server/src/test/java/com/bitsofproof/supernode/test/BitcoinJBlockTest.java index 712c79b8..ccce922d 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/BitcoinJBlockTest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/BitcoinJBlockTest.java @@ -33,7 +33,7 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.core.BlockStore; import com.bitsofproof.supernode.core.BlocktesterChain; import com.bitsofproof.supernode.model.Blk; diff --git a/server/src/test/java/com/bitsofproof/supernode/test/ScriptTest.java b/server/src/test/java/com/bitsofproof/supernode/test/ScriptTest.java index 4aa5e05a..2b110537 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/ScriptTest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/ScriptTest.java @@ -25,8 +25,8 @@ import org.json.JSONException; import org.junit.Test; -import com.bitsofproof.supernode.api.ScriptFormat; -import com.bitsofproof.supernode.api.ValidationException; +import com.bitsofproof.supernode.common.ScriptFormat; +import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.core.ScriptEvaluation; public class ScriptTest diff --git a/server/src/test/java/com/bitsofproof/supernode/test/TransactionTest.java b/server/src/test/java/com/bitsofproof/supernode/test/TransactionTest.java index e27bc9de..c99f2975 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/TransactionTest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/TransactionTest.java @@ -34,10 +34,10 @@ import org.json.JSONException; import org.junit.Test; -import com.bitsofproof.supernode.api.ByteUtils; -import com.bitsofproof.supernode.api.ScriptFormat; -import com.bitsofproof.supernode.api.ValidationException; -import com.bitsofproof.supernode.api.WireFormat; +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.ScriptFormat; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.WireFormat; import com.bitsofproof.supernode.core.ScriptEvaluation; import com.bitsofproof.supernode.model.Tx; import com.bitsofproof.supernode.model.TxIn; diff --git a/server/src/test/java/com/bitsofproof/supernode/test/WireFormatTest.java b/server/src/test/java/com/bitsofproof/supernode/test/WireFormatTest.java index cc0b4a4c..7a54d3dc 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/WireFormatTest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/WireFormatTest.java @@ -22,9 +22,9 @@ import org.junit.Test; -import com.bitsofproof.supernode.api.Hash; -import com.bitsofproof.supernode.api.ValidationException; -import com.bitsofproof.supernode.api.WireFormat; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.WireFormat; import com.bitsofproof.supernode.model.Blk; import com.bitsofproof.supernode.model.Tx; diff --git a/server/src/test/resources/context/EmbeddedBCSAPI.xml b/server/src/test/resources/context/EmbeddedBCSAPI.xml index ae42bd55..ed4df42a 100644 --- a/server/src/test/resources/context/EmbeddedBCSAPI.xml +++ b/server/src/test/resources/context/EmbeddedBCSAPI.xml @@ -9,7 +9,7 @@ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> + class="com.bitsofproof.supernode.common.InMemoryBusConnectionFactory" /> From c46878b693b4ec27c6bbbba1972c50581df77daa Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 4 Jun 2013 09:18:40 +0200 Subject: [PATCH 007/299] separated common parts only from API --- .../supernode/api/AccountListener.java | 1 + .../com/bitsofproof/supernode/api/BCSAPI.java | 10 -- .../supernode/api/DefaultAccountManager.java | 6 +- ...usAdaptor.java => JMSServerConnector.java} | 14 +- .../supernode/api/ServerConnector.java | 147 ++++++++++++++++++ .../supernode/api/SimpleWalletFactory.java | 22 +++ .../supernode/api/Transaction.java | 2 +- .../supernode/api/TransactionOutput.java | 2 +- .../supernode/api/TrunkListener.java | 1 + .../supernode/api/TrunkUpdateMessage.java | 1 + .../com/bitsofproof/supernode/api/Wallet.java | 1 + .../supernode/api/WalletFactory.java | 6 + .../supernode/api/WalletFormatTest.java | 4 + .../bitsofproof/supernode/test/APITest.java | 5 +- .../test/resources/context/EmbeddedBCSAPI.xml | 3 +- 15 files changed, 194 insertions(+), 31 deletions(-) rename api/src/main/java/com/bitsofproof/supernode/api/{ClientBusAdaptor.java => JMSServerConnector.java} (98%) create mode 100644 api/src/main/java/com/bitsofproof/supernode/api/ServerConnector.java create mode 100644 api/src/main/java/com/bitsofproof/supernode/api/SimpleWalletFactory.java create mode 100644 api/src/main/java/com/bitsofproof/supernode/api/WalletFactory.java diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AccountListener.java b/api/src/main/java/com/bitsofproof/supernode/api/AccountListener.java index 77355685..e70f2a22 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AccountListener.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AccountListener.java @@ -15,6 +15,7 @@ */ package com.bitsofproof.supernode.api; + public interface AccountListener { public void accountChanged (AccountManager account, Transaction t); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java index 09030860..710d543e 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java @@ -162,14 +162,4 @@ public interface BCSAPI * @param listener */ public void removeFilteredListener (BloomFilter filter, TransactionListener listener); - - /** - * retrieve or create a wallet - * - * @param name - * @param passphrase - * @return - * @throws BCSAPIException - */ - public Wallet getWallet (String name, String passphrase) throws BCSAPIException; } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/DefaultAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/DefaultAccountManager.java index 393957c0..51d17f2b 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/DefaultAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/DefaultAccountManager.java @@ -31,10 +31,10 @@ import com.bitsofproof.supernode.api.Transaction.TransactionSink; import com.bitsofproof.supernode.api.Transaction.TransactionSource; import com.bitsofproof.supernode.common.BloomFilter; +import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.common.ByteVector; import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ValidationException; -import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; class DefaultAccountManager implements TransactionListener, TrunkListener, AccountManager { @@ -49,8 +49,6 @@ class DefaultAccountManager implements TransactionListener, TrunkListener, Accou private final LocalUTXO receiving = new LocalUTXO (); private final LocalUTXO sending = new LocalUTXO (); - private long balance; - private final List accountListener = Collections.synchronizedList (new ArrayList ()); private final Map processedTransaction = new HashMap (); @@ -229,7 +227,7 @@ public boolean updateWithTransaction (Transaction t) if ( modified ) { wallet.addTransaction (t); - log.trace ("Updated account " + name + " with " + t.getHash () + " balance " + balance); + log.trace ("Updated account " + name + " with " + t.getHash ()); } return modified; diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ClientBusAdaptor.java b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java similarity index 98% rename from api/src/main/java/com/bitsofproof/supernode/api/ClientBusAdaptor.java rename to api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java index 4f96dae3..57897f98 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ClientBusAdaptor.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java @@ -43,14 +43,14 @@ import org.slf4j.LoggerFactory; import com.bitsofproof.supernode.common.BloomFilter; -import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; +import com.bitsofproof.supernode.common.Hash; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -public class ClientBusAdaptor implements BCSAPI +public class JMSServerConnector implements BCSAPI { - private static final Logger log = LoggerFactory.getLogger (ClientBusAdaptor.class); + private static final Logger log = LoggerFactory.getLogger (JMSServerConnector.class); private ConnectionFactory connectionFactory; private Connection connection; @@ -970,12 +970,4 @@ public Color getColor (String digest) throws BCSAPIException return null; } - @Override - public Wallet getWallet (String fileName, String passphrase) throws BCSAPIException - { - SerializedWallet wallet = SerializedWallet.read (fileName, passphrase, isProduction ()); - wallet.setApi (this); - return wallet; - } - } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/ServerConnector.java new file mode 100644 index 00000000..44f6439e --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/api/ServerConnector.java @@ -0,0 +1,147 @@ +package com.bitsofproof.supernode.api; + +import java.util.Collection; + +import com.bitsofproof.supernode.common.BloomFilter; +import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; + +public interface ServerConnector +{ + /** + * returns nounce while doing a full roundtrip to the server + * + * @param nonce + * @return + * @throws BCSAPIException + */ + public long ping (long nonce) throws BCSAPIException; + + /** + * sets the alert listener for the connections + * + * @param listener + * @throws BCSAPIException + */ + public void addAlertListener (AlertListener listener) throws BCSAPIException; + + public void removeAlertListener (AlertListener listener); + + /** + * Are we talking to production? + * + * @return + * @throws BCSAPIException + */ + public boolean isProduction () throws BCSAPIException; + + /** + * get block header for the hash + * + * @param hash + * @return block header or null if hash is unknown + * @throws BCSAPIException + */ + public Block getBlockHeader (String hash) throws BCSAPIException; + + /** + * get block for the hash + * + * @param hash + * @return block or null if hash is unknown + * @throws BCSAPIException + */ + public Block getBlock (String hash) throws BCSAPIException; + + /** + * get the transaction identified by the hash on the trunk + * + * @param hash + * @return transaction or null if no transaction with that hash on the trunk + * @throws BCSAPIException + */ + public Transaction getTransaction (String hash) throws BCSAPIException; + + /** + * send a signed transaction + * + * @param transaction + * @throws BCSAPIException + */ + public void sendTransaction (Transaction transaction) throws BCSAPIException; + + /** + * send a mined block + * + * @param block + * @throws BCSAPIException + */ + public void sendBlock (Block block) throws BCSAPIException; + + /** + * Register a transactions listener + * + * @param listener + * will be called for every validated transaction + * @throws BCSAPIException + */ + public void registerTransactionListener (TransactionListener listener) throws BCSAPIException; + + /** + * remove a listener for validated transactions + * + * @param listener + */ + public void removeTransactionListener (TransactionListener listener); + + /** + * Register a block listener + * + * @param listener + * will be called for every validated new block + * @throws BCSAPIException + */ + public void registerTrunkListener (TrunkListener listener) throws BCSAPIException; + + /** + * remove a trunk listener previously registered + * + * @param listener + */ + public void removeTrunkListener (TrunkListener listener); + + /** + * Match transactions using and address or outpoint in match. This returns only exact matches but limited in result set. + * + * @param match + * @param listener + * @throws BCSAPIException + */ + public void scanTransactions (Collection match, UpdateMode mode, long after, TransactionListener listener) throws BCSAPIException; + + /** + * scan transactions matching the filter + * + * @param filter + * @param after + * @param listener + * @throws BCSAPIException + */ + public void scanTransactions (BloomFilter filter, TransactionListener listener) throws BCSAPIException; + + /** + * register listener with a Bloom filter + * + * @param filter + * @param listener + * @throws BCSAPIException + */ + public void registerFilteredListener (BloomFilter filter, TransactionListener listener) throws BCSAPIException; + + /** + * remove the listener of a Bloom filter + * + * @param filter + * @param listener + */ + public void removeFilteredListener (BloomFilter filter, TransactionListener listener); +} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/SimpleWalletFactory.java b/api/src/main/java/com/bitsofproof/supernode/api/SimpleWalletFactory.java new file mode 100644 index 00000000..a8050d2b --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/api/SimpleWalletFactory.java @@ -0,0 +1,22 @@ +package com.bitsofproof.supernode.api; + + +public class SimpleWalletFactory implements WalletFactory +{ + + private BCSAPI api; + + public void setApi (BCSAPI api) + { + this.api = api; + } + + @Override + public Wallet getWallet (String fileName, String passphrase) throws BCSAPIException + { + SerializedWallet wallet = SerializedWallet.read (fileName, passphrase, api.isProduction ()); + wallet.setApi (api); + return wallet; + } + +} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java b/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java index dc13a352..98170440 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java @@ -27,9 +27,9 @@ import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ScriptFormat; +import com.bitsofproof.supernode.common.ScriptFormat.Opcode; import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.common.WireFormat; -import com.bitsofproof.supernode.common.ScriptFormat.Opcode; import com.google.protobuf.ByteString; public class Transaction implements Serializable, Cloneable diff --git a/api/src/main/java/com/bitsofproof/supernode/api/TransactionOutput.java b/api/src/main/java/com/bitsofproof/supernode/api/TransactionOutput.java index b121eeff..ece022f7 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/TransactionOutput.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/TransactionOutput.java @@ -20,9 +20,9 @@ import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.ScriptFormat; +import com.bitsofproof.supernode.common.ScriptFormat.Token; import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.common.WireFormat; -import com.bitsofproof.supernode.common.ScriptFormat.Token; import com.google.protobuf.ByteString; public class TransactionOutput implements Serializable, Cloneable diff --git a/api/src/main/java/com/bitsofproof/supernode/api/TrunkListener.java b/api/src/main/java/com/bitsofproof/supernode/api/TrunkListener.java index d3626c66..9fbb1160 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/TrunkListener.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/TrunkListener.java @@ -17,6 +17,7 @@ import java.util.List; + public interface TrunkListener { public void trunkUpdate (List removed, List added); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/TrunkUpdateMessage.java b/api/src/main/java/com/bitsofproof/supernode/api/TrunkUpdateMessage.java index 1bca122f..b61fa37e 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/TrunkUpdateMessage.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/TrunkUpdateMessage.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.List; + public class TrunkUpdateMessage implements Serializable { private static final long serialVersionUID = 6012842751107958147L; diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java b/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java index 3847aaaf..b074d9be 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java @@ -1,5 +1,6 @@ package com.bitsofproof.supernode.api; + public interface Wallet { diff --git a/api/src/main/java/com/bitsofproof/supernode/api/WalletFactory.java b/api/src/main/java/com/bitsofproof/supernode/api/WalletFactory.java new file mode 100644 index 00000000..4bcb7013 --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/api/WalletFactory.java @@ -0,0 +1,6 @@ +package com.bitsofproof.supernode.api; + +public interface WalletFactory +{ + public Wallet getWallet (String fileName, String passphrase) throws BCSAPIException; +} diff --git a/api/src/test/java/com/bitsofproof/supernode/api/WalletFormatTest.java b/api/src/test/java/com/bitsofproof/supernode/api/WalletFormatTest.java index 7fa9e555..95d4bcfc 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/WalletFormatTest.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/WalletFormatTest.java @@ -26,6 +26,10 @@ import org.junit.BeforeClass; import org.junit.Test; +import com.bitsofproof.supernode.api.AddressConverter; +import com.bitsofproof.supernode.api.ExtendedKey; +import com.bitsofproof.supernode.api.SerializedWallet; +import com.bitsofproof.supernode.api.Transaction; import com.bitsofproof.supernode.common.ByteVector; import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ScriptFormat; diff --git a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java index 87842cb1..c8900b1d 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java @@ -45,6 +45,7 @@ import com.bitsofproof.supernode.api.Transaction; import com.bitsofproof.supernode.api.TransactionListener; import com.bitsofproof.supernode.api.TrunkListener; +import com.bitsofproof.supernode.api.Wallet; import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.core.BlockStore; @@ -68,7 +69,7 @@ public class APITest private static final long FEE = COIN / 1000L; private static Map blocks = new HashMap (); - private static SerializedWallet wallet; + private static Wallet wallet; private static AccountManager alice; private static AccountManager bob; @@ -203,7 +204,7 @@ public void init () throws BCSAPIException, ValidationException store.resetStore (chain); store.cache (chain, 0); wallet = new SerializedWallet (); - wallet.setApi (api); + ((SerializedWallet) wallet).setApi (api); alice = wallet.getAccountManager ("Alice"); bob = wallet.getAccountManager ("Bob"); diff --git a/server/src/test/resources/context/EmbeddedBCSAPI.xml b/server/src/test/resources/context/EmbeddedBCSAPI.xml index ed4df42a..482bd5eb 100644 --- a/server/src/test/resources/context/EmbeddedBCSAPI.xml +++ b/server/src/test/resources/context/EmbeddedBCSAPI.xml @@ -46,9 +46,8 @@ - + - From 3f208a26f4fe05d8a38f96ed49ba5f092b029bb6 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 4 Jun 2013 09:36:33 +0200 Subject: [PATCH 008/299] precompute murmur for scan --- .../supernode/common/BloomFilter.java | 34 ++++++++++++++++--- .../supernode/core/CachedBlockStore.java | 12 +++++-- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java b/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java index c4af6fe0..fd0d1602 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java @@ -16,6 +16,9 @@ package com.bitsofproof.supernode.common; import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import org.bouncycastle.util.Arrays; @@ -105,7 +108,7 @@ public void add (byte[] data) { for ( int i = 0; i < hashFunctions; ++i ) { - setBit (murmurhash3bit (i, data)); + setBit (murmurhash3bit (i, data, tweak) % (filter.length * 8)); } } @@ -113,7 +116,7 @@ public boolean contains (byte[] data) { for ( int i = 0; i < hashFunctions; ++i ) { - if ( !testBit (murmurhash3bit (i, data)) ) + if ( !testBit (murmurhash3bit (i, data, tweak) % (filter.length * 8)) ) { return false; } @@ -121,9 +124,32 @@ public boolean contains (byte[] data) return true; } - private int murmurhash3bit (int hashNum, byte[] data) + public static List precomputeHashes (byte[] data, long tweak) { - return (int) ((murmurhash3 (data, 0, data.length, (int) (hashNum * 0xFBA4C795L + tweak)) & 0xFFFFFFFFL) % (filter.length * 8)); + ArrayList list = new ArrayList (MAX_HASH_FUNCS); + for ( int i = 0; i < MAX_HASH_FUNCS; ++i ) + { + list.add (murmurhash3bit (i, data, tweak)); + } + return list; + } + + public boolean contains (List hashes) + { + Iterator ni = hashes.iterator (); + for ( int i = 0; i < hashFunctions; ++i ) + { + if ( !testBit (ni.next () % (filter.length * 8)) ) + { + return false; + } + } + return true; + } + + private static int murmurhash3bit (int hashNum, byte[] data, long tweak) + { + return (int) (murmurhash3 (data, 0, data.length, (int) (hashNum * 0xFBA4C795L + tweak)) & 0xFFFFFFFFL); } public void toWire (WireFormat.Writer writer) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 43a25879..51d879a8 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -46,14 +46,14 @@ import com.bitsofproof.supernode.api.ColorRules; import com.bitsofproof.supernode.api.ColorRules.ColoredCoin; import com.bitsofproof.supernode.common.BloomFilter; +import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.common.ByteUtils; import com.bitsofproof.supernode.common.ByteVector; import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.ScriptFormat; +import com.bitsofproof.supernode.common.ScriptFormat.Token; import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.common.WireFormat; -import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; -import com.bitsofproof.supernode.common.ScriptFormat.Token; import com.bitsofproof.supernode.model.Blk; import com.bitsofproof.supernode.model.Head; import com.bitsofproof.supernode.model.StoredColor; @@ -409,6 +409,12 @@ public void filterTransactions (Set matchSet, UpdateMode update, lon lock.readLock ().unlock (); } + Map> hashes = new HashMap> (); + for ( ByteVector b : matchSet ) + { + hashes.put (b, BloomFilter.precomputeHashes (b.toByteArray (), 0)); + } + for ( CachedBlock cb : blocks ) { if ( matchSet.size () > MAX_MATCH_SET ) @@ -419,7 +425,7 @@ public void filterTransactions (Set matchSet, UpdateMode update, lon BloomFilter filter = new BloomFilter (cb.filterMap, cb.filterFunctions, 0, UpdateMode.none); for ( ByteVector v : matchSet ) { - if ( filter.contains (v.toByteArray ()) ) + if ( filter.contains (hashes.get (v)) ) { found = true; break; From 5e42185282a0ea2d4b76cdb1ad7d4f23288d31d7 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 4 Jun 2013 09:45:28 +0200 Subject: [PATCH 009/299] return murmur as long --- .../supernode/common/BloomFilter.java | 18 +++++++++--------- .../supernode/core/CachedBlockStore.java | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java b/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java index fd0d1602..9ab1272e 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java @@ -108,7 +108,7 @@ public void add (byte[] data) { for ( int i = 0; i < hashFunctions; ++i ) { - setBit (murmurhash3bit (i, data, tweak) % (filter.length * 8)); + setBit ((int) (murmurhash3bit (i, data, tweak) % (filter.length * 8))); } } @@ -116,7 +116,7 @@ public boolean contains (byte[] data) { for ( int i = 0; i < hashFunctions; ++i ) { - if ( !testBit (murmurhash3bit (i, data, tweak) % (filter.length * 8)) ) + if ( !testBit ((int) (murmurhash3bit (i, data, tweak) % (filter.length * 8))) ) { return false; } @@ -124,9 +124,9 @@ public boolean contains (byte[] data) return true; } - public static List precomputeHashes (byte[] data, long tweak) + public static List precomputeHashes (byte[] data, long tweak) { - ArrayList list = new ArrayList (MAX_HASH_FUNCS); + ArrayList list = new ArrayList (MAX_HASH_FUNCS); for ( int i = 0; i < MAX_HASH_FUNCS; ++i ) { list.add (murmurhash3bit (i, data, tweak)); @@ -134,12 +134,12 @@ public static List precomputeHashes (byte[] data, long tweak) return list; } - public boolean contains (List hashes) + public boolean contains (List hashes) { - Iterator ni = hashes.iterator (); + Iterator ni = hashes.iterator (); for ( int i = 0; i < hashFunctions; ++i ) { - if ( !testBit (ni.next () % (filter.length * 8)) ) + if ( !testBit ((int) (ni.next () % (filter.length * 8))) ) { return false; } @@ -147,9 +147,9 @@ public boolean contains (List hashes) return true; } - private static int murmurhash3bit (int hashNum, byte[] data, long tweak) + private static long murmurhash3bit (int hashNum, byte[] data, long tweak) { - return (int) (murmurhash3 (data, 0, data.length, (int) (hashNum * 0xFBA4C795L + tweak)) & 0xFFFFFFFFL); + return murmurhash3 (data, 0, data.length, (int) (hashNum * 0xFBA4C795L + tweak)) & 0xFFFFFFFFL; } public void toWire (WireFormat.Writer writer) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 51d879a8..878a7b9f 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -409,7 +409,7 @@ public void filterTransactions (Set matchSet, UpdateMode update, lon lock.readLock ().unlock (); } - Map> hashes = new HashMap> (); + Map> hashes = new HashMap> (); for ( ByteVector b : matchSet ) { hashes.put (b, BloomFilter.precomputeHashes (b.toByteArray (), 0)); From a5c0e855b40a731c0f7cefadeb6194e77a5cc939 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 4 Jun 2013 10:13:42 +0200 Subject: [PATCH 010/299] unsigned signed again --- .../supernode/common/BloomFilter.java | 18 +++++++++--------- .../supernode/core/CachedBlockStore.java | 10 +++++----- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java b/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java index 9ab1272e..69107dc8 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java @@ -108,7 +108,7 @@ public void add (byte[] data) { for ( int i = 0; i < hashFunctions; ++i ) { - setBit ((int) (murmurhash3bit (i, data, tweak) % (filter.length * 8))); + setBit ((int) ((murmurhash3bit (i, data, tweak) & 0xFFFFFFFFL) % (filter.length * 8))); } } @@ -116,7 +116,7 @@ public boolean contains (byte[] data) { for ( int i = 0; i < hashFunctions; ++i ) { - if ( !testBit ((int) (murmurhash3bit (i, data, tweak) % (filter.length * 8))) ) + if ( !testBit ((int) ((murmurhash3bit (i, data, tweak) & 0xFFFFFFFFL) % (filter.length * 8))) ) { return false; } @@ -124,9 +124,9 @@ public boolean contains (byte[] data) return true; } - public static List precomputeHashes (byte[] data, long tweak) + public static List precomputeHashes (byte[] data, long tweak) { - ArrayList list = new ArrayList (MAX_HASH_FUNCS); + ArrayList list = new ArrayList (MAX_HASH_FUNCS); for ( int i = 0; i < MAX_HASH_FUNCS; ++i ) { list.add (murmurhash3bit (i, data, tweak)); @@ -134,12 +134,12 @@ public static List precomputeHashes (byte[] data, long tweak) return list; } - public boolean contains (List hashes) + public boolean contains (List hashes) { - Iterator ni = hashes.iterator (); + Iterator ni = hashes.iterator (); for ( int i = 0; i < hashFunctions; ++i ) { - if ( !testBit ((int) (ni.next () % (filter.length * 8))) ) + if ( !testBit ((int) ((ni.next () & 0xFFFFFFFFL) % (filter.length * 8))) ) { return false; } @@ -147,9 +147,9 @@ public boolean contains (List hashes) return true; } - private static long murmurhash3bit (int hashNum, byte[] data, long tweak) + private static int murmurhash3bit (int hashNum, byte[] data, long tweak) { - return murmurhash3 (data, 0, data.length, (int) (hashNum * 0xFBA4C795L + tweak)) & 0xFFFFFFFFL; + return murmurhash3 (data, 0, data.length, (int) (hashNum * 0xFBA4C795L + tweak)); } public void toWire (WireFormat.Writer writer) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 878a7b9f..d96b194f 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -386,6 +386,10 @@ public void cache (Chain chain, int size) throws ValidationException @Override public void filterTransactions (Set matchSet, UpdateMode update, long after, TransactionProcessor processor) throws ValidationException { + if ( matchSet.size () > MAX_MATCH_SET ) + { + throw new ValidationException ("Match set too big for filterTransactions. Use scan instead"); + } List blocks = new ArrayList (); try { @@ -409,7 +413,7 @@ public void filterTransactions (Set matchSet, UpdateMode update, lon lock.readLock ().unlock (); } - Map> hashes = new HashMap> (); + Map> hashes = new HashMap> (); for ( ByteVector b : matchSet ) { hashes.put (b, BloomFilter.precomputeHashes (b.toByteArray (), 0)); @@ -417,10 +421,6 @@ public void filterTransactions (Set matchSet, UpdateMode update, lon for ( CachedBlock cb : blocks ) { - if ( matchSet.size () > MAX_MATCH_SET ) - { - throw new ValidationException ("Match set too big for filterTransactions. Use scan instead"); - } boolean found = false; BloomFilter filter = new BloomFilter (cb.filterMap, cb.filterFunctions, 0, UpdateMode.none); for ( ByteVector v : matchSet ) From f5490833508adb3efb111bb03bd8442d0b555d82 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 4 Jun 2013 10:36:35 +0200 Subject: [PATCH 011/299] test excecution was different with more processors. put into same function --- .../bitsofproof/supernode/test/APITest.java | 33 +++++-------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java index c8900b1d..ec73fd6a 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java @@ -199,7 +199,7 @@ public static void provider () } @Test - public void init () throws BCSAPIException, ValidationException + public void test () throws BCSAPIException, ValidationException { store.resetStore (chain); store.cache (chain, 0); @@ -213,18 +213,12 @@ public void init () throws BCSAPIException, ValidationException api.registerTrunkListener (validationMonitor); api.registerTransactionListener (validationMonitor); - } - @Test - public void checkGenesis () throws BCSAPIException - { + // check genesis String genesisHash = chain.getGenesis ().getHash (); assertTrue (api.getBlock (genesisHash).getHash ().equals (genesisHash)); - } - @Test - public void send1Block () throws BCSAPIException, ValidationException - { + // send 1 block Block block = createBlock (chain.getGenesis ().getHash (), Transaction.createCoinbase (alice.getNextKey (), 50 * COIN, 1)); mineBlock (block); blocks.put (1, block); @@ -234,15 +228,12 @@ public void send1Block () throws BCSAPIException, ValidationException validationMonitor.expectBlockValidations (1); assertTrue (validationMonitor.getLast ().equals (block.getHash ())); aliceMonitor.expectUpdates (1); - } - @Test - public void send10Blocks () throws ValidationException, BCSAPIException - { + // send 10 blocks String hash = blocks.get (1).getHash (); for ( int i = 0; i < 10; ++i ) { - Block block = createBlock (hash, Transaction.createCoinbase (alice.getNextKey (), 5000000000L, i + 2)); + block = createBlock (hash, Transaction.createCoinbase (alice.getNextKey (), 5000000000L, i + 2)); block.setCreateTime (block.getCreateTime () + (i + 1) * 1000); // avoid clash of timestamp with median mineBlock (block); blocks.put (i + 2, block); @@ -252,11 +243,8 @@ public void send10Blocks () throws ValidationException, BCSAPIException validationMonitor.expectBlockValidations (10); assertTrue (validationMonitor.getLast ().equals (hash)); aliceMonitor.expectUpdates (10); - } - @Test - public void spendSome () throws BCSAPIException, ValidationException - { + // spend some long aliceStartingBalance = alice.getBalance (); Transaction spend = alice.pay (bob.getNextKey ().getAddress (), 50 * COIN, FEE); api.sendTransaction (spend); @@ -265,13 +253,10 @@ public void spendSome () throws BCSAPIException, ValidationException validationMonitor.expectTransactionValidations (1); assertTrue (bob.getBalance () == 50 * COIN); assertTrue (alice.getBalance () == aliceStartingBalance - bob.getBalance () - FEE); - } - @Test - public void splitSome () throws BCSAPIException, ValidationException - { - long aliceStartingBalance = alice.getBalance (); - Transaction spend = alice.split (new long[] { 1 * COIN, 2 * COIN }, FEE); + // split + aliceStartingBalance = alice.getBalance (); + spend = alice.split (new long[] { 1 * COIN, 2 * COIN }, FEE); api.sendTransaction (spend); aliceMonitor.expectUpdates (1); validationMonitor.expectTransactionValidations (1); From a9121591ca5cb64baece21ce9337a8527e931561 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 4 Jun 2013 11:42:30 +0200 Subject: [PATCH 012/299] add trace to match --- .../com/bitsofproof/supernode/core/CachedBlockStore.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index d96b194f..7db62012 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -390,6 +390,7 @@ public void filterTransactions (Set matchSet, UpdateMode update, lon { throw new ValidationException ("Match set too big for filterTransactions. Use scan instead"); } + log.trace ("Match request for " + matchSet.size ()); List blocks = new ArrayList (); try { @@ -412,12 +413,14 @@ public void filterTransactions (Set matchSet, UpdateMode update, lon { lock.readLock ().unlock (); } + log.trace ("Sorted chain"); Map> hashes = new HashMap> (); for ( ByteVector b : matchSet ) { hashes.put (b, BloomFilter.precomputeHashes (b.toByteArray (), 0)); } + log.trace ("Precomputed murmur hashes chain"); for ( CachedBlock cb : blocks ) { @@ -428,6 +431,7 @@ public void filterTransactions (Set matchSet, UpdateMode update, lon if ( filter.contains (hashes.get (v)) ) { found = true; + log.trace ("Match in block " + cb.height); break; } } @@ -437,14 +441,17 @@ public void filterTransactions (Set matchSet, UpdateMode update, lon } try { + int n = 0; Blk b = retrieveBlock (cb); for ( Tx t : b.getTransactions () ) { if ( t.matches (matchSet, update) ) { processor.process (t); + ++n; } } + log.trace ("Matched transactions in block: " + n); } catch ( ValidationException e ) { @@ -452,6 +459,7 @@ public void filterTransactions (Set matchSet, UpdateMode update, lon } } processor.process (null); + log.trace ("Done with Match request"); } private boolean isBlockOnBranch (CachedBlock block, CachedHead branch, int untilHeight) From fd133e712ea43ec509ea0bdf21d75f7b7f34d214 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 4 Jun 2013 13:18:42 +0200 Subject: [PATCH 013/299] compute hash for matches while scanning --- .../supernode/core/CachedBlockStore.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 7db62012..046508b1 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -416,19 +416,19 @@ public void filterTransactions (Set matchSet, UpdateMode update, lon log.trace ("Sorted chain"); Map> hashes = new HashMap> (); - for ( ByteVector b : matchSet ) - { - hashes.put (b, BloomFilter.precomputeHashes (b.toByteArray (), 0)); - } - log.trace ("Precomputed murmur hashes chain"); - for ( CachedBlock cb : blocks ) { boolean found = false; BloomFilter filter = new BloomFilter (cb.filterMap, cb.filterFunctions, 0, UpdateMode.none); for ( ByteVector v : matchSet ) { - if ( filter.contains (hashes.get (v)) ) + List hashList = hashes.get (v); + if ( hashList == null ) + { + hashList = BloomFilter.precomputeHashes (v.toByteArray (), 0); + hashes.put (v, hashList); + } + if ( filter.contains (hashList) ) { found = true; log.trace ("Match in block " + cb.height); From 56472b40a437533f5ae52a469cc295742453e3e5 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 4 Jun 2013 13:32:51 +0200 Subject: [PATCH 014/299] increase bloom filterprecision --- .../java/com/bitsofproof/supernode/core/CachedBlockStore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 046508b1..115b28aa 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -985,7 +985,7 @@ public TransactionValidationException call () } // this is last loop before persist since modifying the entities. - BloomFilter filter = BloomFilter.createOptimalFilter (Math.max (5 * numberOfOutputs, 500), 1.0e-8, 0, UpdateMode.none); + BloomFilter filter = BloomFilter.createOptimalFilter (Math.max (5 * numberOfOutputs, 500), 1.0e-10, 0, UpdateMode.none); for ( Tx t : b.getTransactions () ) { From 78a70e5bee9a09e0c0f502651d2b617ccbad2cf8 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 4 Jun 2013 16:28:38 +0200 Subject: [PATCH 015/299] avoid a race condition --- .../main/java/com/bitsofproof/supernode/core/ChainLoader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ChainLoader.java b/server/src/main/java/com/bitsofproof/supernode/core/ChainLoader.java index f0d049b4..a7a9a8f3 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ChainLoader.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ChainLoader.java @@ -201,7 +201,7 @@ public void process (InvMessage m, BitcoinPeer peer) for ( byte[] h : m.getBlockHashes () ) { String hash = new Hash (h).toString (); - if ( !havePending.containsKey (hash) && !store.isStoredBlock (hash) ) + if ( !havePending.containsKey (hash) ) { KnownBlock kn = new KnownBlock (); kn.nr = n++; From f1120f32a1a63b3373637f7160f5099db5a5c9fb Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 4 Jun 2013 17:04:35 +0200 Subject: [PATCH 016/299] Revert "avoid a race condition" This reverts commit 78a70e5bee9a09e0c0f502651d2b617ccbad2cf8. --- .../main/java/com/bitsofproof/supernode/core/ChainLoader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ChainLoader.java b/server/src/main/java/com/bitsofproof/supernode/core/ChainLoader.java index a7a9a8f3..f0d049b4 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ChainLoader.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ChainLoader.java @@ -201,7 +201,7 @@ public void process (InvMessage m, BitcoinPeer peer) for ( byte[] h : m.getBlockHashes () ) { String hash = new Hash (h).toString (); - if ( !havePending.containsKey (hash) ) + if ( !havePending.containsKey (hash) && !store.isStoredBlock (hash) ) { KnownBlock kn = new KnownBlock (); kn.nr = n++; From c4367114721970148e021001963d571880214619 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 4 Jun 2013 17:09:53 +0200 Subject: [PATCH 017/299] cache current longest block header in a list, this will have further uses later --- .../supernode/core/CachedBlockStore.java | 115 ++++++++++-------- 1 file changed, 64 insertions(+), 51 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 115b28aa..97765edc 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -106,6 +106,8 @@ public abstract class CachedBlockStore implements BlockStore protected final Map cachedHeads = new HashMap (); protected final Map cachedColors = new HashMap (); + private final LinkedList blockChain = new LinkedList (); + private final ImplementTxOutCache cachedUTXO = new ImplementTxOutCache (); private final List trunkListener = new ArrayList (); @@ -366,6 +368,8 @@ public void cache (Chain chain, int size) throws ValidationException log.trace ("Cache chain..."); cacheChain (); + updateBlockChainCache (); + if ( size > 0 ) { log.trace ("Cache UTXO set ..."); @@ -380,6 +384,25 @@ public void cache (Chain chain, int size) throws ValidationException } } + private void updateBlockChainCache () + { + List appendList = new ArrayList (); + CachedBlock q = currentHead.getLast (); + CachedBlock p = q.previous; + while ( p != null ) + { + if ( blockChain.getLast () != p ) + { + blockChain.removeLast (); + appendList.add (q); + } + q = p; + p = q.previous; + } + Collections.reverse (appendList); + blockChain.addAll (appendList); + } + private static final int MAX_MATCH_SET = 1000; @Transactional (propagation = Propagation.REQUIRED, rollbackFor = { Exception.class }, readOnly = true) @@ -390,74 +413,63 @@ public void filterTransactions (Set matchSet, UpdateMode update, lon { throw new ValidationException ("Match set too big for filterTransactions. Use scan instead"); } - log.trace ("Match request for " + matchSet.size ()); - List blocks = new ArrayList (); try { + Map> hashes = new HashMap> (); + lock.readLock ().lock (); - CachedBlock q = currentHead.getLast (); - CachedBlock p = q.previous; - while ( p != null ) + for ( CachedBlock cb : blockChain ) { - if ( after == 0 || q.time > after ) + if ( cb.time <= after ) { - blocks.add (q); + continue; } - q = p; - p = q.previous; - } - Collections.reverse (blocks); - } - finally - { - lock.readLock ().unlock (); - } - log.trace ("Sorted chain"); - - Map> hashes = new HashMap> (); - for ( CachedBlock cb : blocks ) - { - boolean found = false; - BloomFilter filter = new BloomFilter (cb.filterMap, cb.filterFunctions, 0, UpdateMode.none); - for ( ByteVector v : matchSet ) - { - List hashList = hashes.get (v); - if ( hashList == null ) + boolean found = false; + BloomFilter filter = new BloomFilter (cb.filterMap, cb.filterFunctions, 0, UpdateMode.none); + for ( ByteVector v : matchSet ) { - hashList = BloomFilter.precomputeHashes (v.toByteArray (), 0); - hashes.put (v, hashList); + List hashList = hashes.get (v); + if ( hashList == null ) + { + hashList = BloomFilter.precomputeHashes (v.toByteArray (), 0); + hashes.put (v, hashList); + } + if ( filter.contains (hashList) ) + { + found = true; + log.trace ("Match in block " + cb.height); + break; + } } - if ( filter.contains (hashList) ) + if ( !found ) { - found = true; - log.trace ("Match in block " + cb.height); - break; + continue; } - } - if ( !found ) - { - continue; - } - try - { - int n = 0; - Blk b = retrieveBlock (cb); - for ( Tx t : b.getTransactions () ) + try { - if ( t.matches (matchSet, update) ) + int n = 0; + Blk b = retrieveBlock (cb); + for ( Tx t : b.getTransactions () ) { - processor.process (t); - ++n; + if ( t.matches (matchSet, update) ) + { + processor.process (t); + ++n; + } } + log.trace ("Matched transactions in block: " + n); + } + catch ( ValidationException e ) + { + log.error ("Error while scanning blocks", e); } - log.trace ("Matched transactions in block: " + n); - } - catch ( ValidationException e ) - { - log.error ("Error while scanning blocks", e); } } + finally + { + lock.readLock ().unlock (); + } processor.process (null); log.trace ("Done with Match request"); } @@ -1128,6 +1140,7 @@ else if ( currentHead.getLast () == cachedPrevious ) addedBlocks.add (b); currentHead = usingHead; } + updateBlockChainCache (); usingHead.setLast (m); From 342f738c4cf0267555f945df2491b473cf29d9fc Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 4 Jun 2013 17:14:06 +0200 Subject: [PATCH 018/299] handle empty chain --- .../java/com/bitsofproof/supernode/core/CachedBlockStore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 97765edc..00a1ba59 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -391,7 +391,7 @@ private void updateBlockChainCache () CachedBlock p = q.previous; while ( p != null ) { - if ( blockChain.getLast () != p ) + if ( blockChain.isEmpty () || blockChain.getLast () != p ) { blockChain.removeLast (); appendList.add (q); From 5a9f3f3c61ddaf5c2f04b8c70eb66873e2bee0f5 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 4 Jun 2013 17:16:22 +0200 Subject: [PATCH 019/299] argh... --- .../com/bitsofproof/supernode/core/CachedBlockStore.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 00a1ba59..538d1a28 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -391,7 +391,11 @@ private void updateBlockChainCache () CachedBlock p = q.previous; while ( p != null ) { - if ( blockChain.isEmpty () || blockChain.getLast () != p ) + if ( blockChain.isEmpty () ) + { + appendList.add (q); + } + else if ( blockChain.getLast () != p ) { blockChain.removeLast (); appendList.add (q); From da198cb3e980238f6c9ec87a2e79a592d790ab92 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 4 Jun 2013 19:06:32 +0200 Subject: [PATCH 020/299] readded accidentally deleted test --- .../supernode/api/ExtendedKey.java | 11 ++- .../supernode/api/ExtendedKeyTest.java | 75 +++++++++++++++++++ .../supernode/api/KeySerializerTest.java | 1 - 3 files changed, 85 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java index b75d57df..4d7734fb 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java @@ -38,7 +38,6 @@ import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ValidationException; - /** * Key Generator following BIP32 https://en.bitcoin.it/wiki/BIP_0032 */ @@ -118,6 +117,16 @@ public ExtendedKey getChild (int sequence) throws ValidationException return new ExtendedKey (sub.getMaster (), sub.getChainCode (), sub.getDepth () + 1, getFingerPrint (), sequence); } + public ExtendedKey getReadOnly () + { + return new ExtendedKey (new ECPublicKey (master.getPublic (), true), chainCode, depth, parent, sequence); + } + + public boolean isReadOnly () + { + return master.getPrivate () == null; + } + private ExtendedKey generateKey (int sequence) throws ValidationException { try diff --git a/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java b/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java index 0512f738..af4b8cf6 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java @@ -17,18 +17,31 @@ import static org.junit.Assert.assertTrue; +import java.io.IOException; +import java.io.InputStream; import java.lang.management.ManagementFactory; import java.lang.management.ThreadMXBean; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; import java.security.SecureRandom; import java.security.Security; import java.util.Arrays; +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; import org.junit.BeforeClass; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.bitsofproof.supernode.common.ByteUtils; import com.bitsofproof.supernode.common.ECKeyPair; import com.bitsofproof.supernode.common.ECPublicKey; import com.bitsofproof.supernode.common.Key; @@ -69,6 +82,68 @@ public void testGenerator () throws ValidationException private static final ThreadMXBean mxb = ManagementFactory.getThreadMXBean (); private static final Logger log = LoggerFactory.getLogger (ExtendedKeyTest.class); + private static final String BIP32 = "BIP32.json"; + + private JSONArray readObjectArray (String resource) throws IOException, JSONException + { + InputStream input = this.getClass ().getResource ("/" + resource).openStream (); + StringBuffer content = new StringBuffer (); + byte[] buffer = new byte[1024]; + int len; + while ( (len = input.read (buffer)) > 0 ) + { + byte[] s = new byte[len]; + System.arraycopy (buffer, 0, s, 0, len); + content.append (new String (buffer, "UTF-8")); + } + return new JSONArray (content.toString ()); + } + + @Test + public void bip32Test () throws IOException, JSONException, NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, ValidationException + { + JSONArray tests = readObjectArray (BIP32); + for ( int i = 0; i < tests.length (); ++i ) + { + JSONObject test = tests.getJSONObject (i); + byte[] seed = ByteUtils.fromHex (test.getString ("master")); + Mac mac = Mac.getInstance ("HmacSHA512", "BC"); + SecretKey seedkey = new SecretKeySpec ("Bitcoin seed".getBytes (), "HmacSHA512"); + mac.init (seedkey); + byte[] lr = mac.doFinal (seed); + byte[] l = Arrays.copyOfRange (lr, 0, 32); + byte[] r = Arrays.copyOfRange (lr, 32, 64); + ECKeyPair keyPair = new ECKeyPair (l, true); + ExtendedKey ekprivate = new ExtendedKey (keyPair, r, 0, 0, 0); + ExtendedKey ekpublic = ekprivate.getReadOnly (); + assertTrue (ekprivate.serialize (true).equals (test.get ("private"))); + assertTrue (ekpublic.serialize (true).equals (test.get ("public"))); + JSONArray derived = test.getJSONArray ("derived"); + for ( int j = 0; j < derived.length (); ++j ) + { + JSONObject derivedTest = derived.getJSONObject (j); + JSONArray locator = derivedTest.getJSONArray ("locator"); + ExtendedKey ek = ekprivate; + ExtendedKey ep = ekpublic; + for ( int k = 0; k < locator.length (); ++k ) + { + JSONObject c = locator.getJSONObject (k); + if ( !c.getBoolean ("private") ) + { + ek = ek.getChild (c.getInt ("sequence")); + } + else + { + ek = ek.getChild (c.getInt ("sequence") | 0x80000000); + } + ep = ek.getReadOnly (); + } + assertTrue (ek.serialize (true).equals (derivedTest.getString ("private"))); + assertTrue (ep.serialize (true).equals (derivedTest.getString ("public"))); + } + } + } + @Test public void testECDSASpeed () throws ValidationException { diff --git a/api/src/test/java/com/bitsofproof/supernode/api/KeySerializerTest.java b/api/src/test/java/com/bitsofproof/supernode/api/KeySerializerTest.java index 91b58b98..b9f46dfe 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/KeySerializerTest.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/KeySerializerTest.java @@ -44,7 +44,6 @@ public static void init () private static final String WIF = "WIF.json"; private static final String BIP38NoEC = "BIP38NoEC.json"; private static final String BIP38EC = "BIP38EC.json"; - private static final String ExtendedKeyTest = "ExtendedKey.json"; private JSONArray readObjectArray (String resource) throws IOException, JSONException { From a5f982677b340fd26aa83a2433fd7ceda87bfb90 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 4 Jun 2013 19:12:25 +0200 Subject: [PATCH 021/299] added create from seed and passphrase --- .../supernode/api/ExtendedKey.java | 41 +++++++++++++++++++ .../supernode/api/ExtendedKeyTest.java | 13 +----- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java index 4d7734fb..13259ec8 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java @@ -17,6 +17,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -52,6 +53,46 @@ public class ExtendedKey private final int parent; private final int sequence; + public static ExtendedKey createFromPassphrase (String passphrase) throws ValidationException + { + try + { + return createFromSeed (passphrase.getBytes ("UTF-8")); + } + catch ( UnsupportedEncodingException e ) + { + throw new ValidationException (e); + } + } + + public static ExtendedKey createFromSeed (byte[] seed) throws ValidationException + { + Mac mac; + try + { + mac = Mac.getInstance ("HmacSHA512", "BC"); + SecretKey seedkey = new SecretKeySpec ("Bitcoin seed".getBytes (), "HmacSHA512"); + mac.init (seedkey); + byte[] lr = mac.doFinal (seed); + byte[] l = Arrays.copyOfRange (lr, 0, 32); + byte[] r = Arrays.copyOfRange (lr, 32, 64); + ECKeyPair keyPair = new ECKeyPair (l, true); + return new ExtendedKey (keyPair, r, 0, 0, 0); + } + catch ( NoSuchAlgorithmException e ) + { + throw new ValidationException (e); + } + catch ( NoSuchProviderException e ) + { + throw new ValidationException (e); + } + catch ( InvalidKeyException e ) + { + throw new ValidationException (e); + } + } + public static ExtendedKey createNew () { Key key = ECKeyPair.createNew (true); diff --git a/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java b/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java index af4b8cf6..5f78e5ae 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java @@ -28,10 +28,6 @@ import java.security.Security; import java.util.Arrays; -import javax.crypto.Mac; -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; - import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.json.JSONArray; import org.json.JSONException; @@ -107,14 +103,7 @@ public void bip32Test () throws IOException, JSONException, NoSuchAlgorithmExcep { JSONObject test = tests.getJSONObject (i); byte[] seed = ByteUtils.fromHex (test.getString ("master")); - Mac mac = Mac.getInstance ("HmacSHA512", "BC"); - SecretKey seedkey = new SecretKeySpec ("Bitcoin seed".getBytes (), "HmacSHA512"); - mac.init (seedkey); - byte[] lr = mac.doFinal (seed); - byte[] l = Arrays.copyOfRange (lr, 0, 32); - byte[] r = Arrays.copyOfRange (lr, 32, 64); - ECKeyPair keyPair = new ECKeyPair (l, true); - ExtendedKey ekprivate = new ExtendedKey (keyPair, r, 0, 0, 0); + ExtendedKey ekprivate = ExtendedKey.createFromSeed (seed); ExtendedKey ekpublic = ekprivate.getReadOnly (); assertTrue (ekprivate.serialize (true).equals (test.get ("private"))); assertTrue (ekpublic.serialize (true).equals (test.get ("public"))); From d5076dc9d16685e9814a5b9790636529caccb079 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 4 Jun 2013 21:29:33 +0200 Subject: [PATCH 022/299] Default will be in memory... --- ...anager.java => InMemoryAccountManager.java} | 18 +++++++++--------- .../api/{LocalUTXO.java => InMemoryUTXO.java} | 2 +- .../supernode/api/SerializedWallet.java | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) rename api/src/main/java/com/bitsofproof/supernode/api/{DefaultAccountManager.java => InMemoryAccountManager.java} (93%) rename api/src/main/java/com/bitsofproof/supernode/api/{LocalUTXO.java => InMemoryUTXO.java} (99%) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/DefaultAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java similarity index 93% rename from api/src/main/java/com/bitsofproof/supernode/api/DefaultAccountManager.java rename to api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java index 51d17f2b..633e66c8 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/DefaultAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java @@ -36,18 +36,18 @@ import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ValidationException; -class DefaultAccountManager implements TransactionListener, TrunkListener, AccountManager +class InMemoryAccountManager implements TransactionListener, TrunkListener, AccountManager { - private static final Logger log = LoggerFactory.getLogger (DefaultAccountManager.class); + private static final Logger log = LoggerFactory.getLogger (InMemoryAccountManager.class); private BCSAPI api; private final Wallet wallet; - private final LocalUTXO confirmed = new LocalUTXO (); - private final LocalUTXO change = new LocalUTXO (); - private final LocalUTXO receiving = new LocalUTXO (); - private final LocalUTXO sending = new LocalUTXO (); + private final InMemoryUTXO confirmed = new InMemoryUTXO (); + private final InMemoryUTXO change = new InMemoryUTXO (); + private final InMemoryUTXO receiving = new InMemoryUTXO (); + private final InMemoryUTXO sending = new InMemoryUTXO (); private final List accountListener = Collections.synchronizedList (new ArrayList ()); private final Map processedTransaction = new HashMap (); @@ -55,7 +55,7 @@ class DefaultAccountManager implements TransactionListener, TrunkListener, Accou private final String name; private final ExtendedKey extended; private final Map keyForAddress = new HashMap (); - private final BloomFilter filter = BloomFilter.createOptimalFilter (100, 1.0 / 1000000.0, UpdateMode.all); + private final BloomFilter filter = BloomFilter.createOptimalFilter (500, 1e-10, UpdateMode.all); private final Set addressSet = new HashSet (); public void setApi (BCSAPI api) @@ -63,12 +63,12 @@ public void setApi (BCSAPI api) this.api = api; } - public DefaultAccountManager (Wallet wallet, String name, ExtendedKey extended, int nextSequence) throws ValidationException + public InMemoryAccountManager (Wallet wallet, String name, ExtendedKey extended, int nextKey) throws ValidationException { this.wallet = wallet; this.name = name; this.extended = extended; - for ( int i = 0; i < nextSequence; ++i ) + for ( int i = 0; i < nextKey; ++i ) { Key key = extended.getKey (i); keyForAddress.put (new ByteVector (key.getAddress ()), key); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/LocalUTXO.java b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java similarity index 99% rename from api/src/main/java/com/bitsofproof/supernode/api/LocalUTXO.java rename to api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java index 3c4e5edf..95d77a75 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/LocalUTXO.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java @@ -22,7 +22,7 @@ import com.bitsofproof.supernode.api.Transaction.TransactionSource; -public class LocalUTXO +public class InMemoryUTXO { private final Map> utxo = new HashMap> (); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java index ecc7bc1d..9a79e045 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java @@ -90,7 +90,7 @@ public AccountManager getAccountManager (String name) throws BCSAPIException int nextSequence = 0; ExtendedKey extended = null; - DefaultAccountManager am = null; + InMemoryAccountManager am = null; for ( WalletKey key : getKeys () ) { if ( key.name.equals (name) ) @@ -101,7 +101,7 @@ public AccountManager getAccountManager (String name) throws BCSAPIException } extended = ExtendedKey.parse (key.key); nextSequence = key.nextSequence; - key.am = am = new DefaultAccountManager (this, name, extended, nextSequence); + key.am = am = new InMemoryAccountManager (this, name, extended, nextSequence); break; } } @@ -115,7 +115,7 @@ public AccountManager getAccountManager (String name) throws BCSAPIException wk.key = extended.serialize (production); nextSequence = wk.nextSequence = 0; addKey (wk); - wk.am = am = new DefaultAccountManager (this, name, extended, nextSequence); + wk.am = am = new InMemoryAccountManager (this, name, extended, nextSequence); } am.setApi (api); am.registerFilter (); @@ -130,7 +130,7 @@ public AccountManager getAccountManager (String name) throws BCSAPIException { transactions.clear (); } - final DefaultAccountManager fam = am; + final InMemoryAccountManager fam = am; long ts = getTimeStamp (); if ( ts > 0 ) { From b71fc9c161d8352636ab5f2d6dfb6c26fbd25fda Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 4 Jun 2013 22:00:54 +0200 Subject: [PATCH 023/299] use external key and seed --- .../java/com/bitsofproof/supernode/api/ExtendedKey.java | 8 ++++---- .../com/bitsofproof/supernode/api/ExtendedKeyTest.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java index 13259ec8..f1747bc5 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java @@ -53,11 +53,11 @@ public class ExtendedKey private final int parent; private final int sequence; - public static ExtendedKey createFromPassphrase (String passphrase) throws ValidationException + public static ExtendedKey createFromPassphrase (String passphrase, byte[] seed) throws ValidationException { try { - return createFromSeed (passphrase.getBytes ("UTF-8")); + return createFromSeed (passphrase.getBytes ("UTF-8"), seed); } catch ( UnsupportedEncodingException e ) { @@ -65,13 +65,13 @@ public static ExtendedKey createFromPassphrase (String passphrase) throws Valida } } - public static ExtendedKey createFromSeed (byte[] seed) throws ValidationException + public static ExtendedKey createFromSeed (byte[] key, byte[] seed) throws ValidationException { Mac mac; try { mac = Mac.getInstance ("HmacSHA512", "BC"); - SecretKey seedkey = new SecretKeySpec ("Bitcoin seed".getBytes (), "HmacSHA512"); + SecretKey seedkey = new SecretKeySpec (key, "HmacSHA512"); mac.init (seedkey); byte[] lr = mac.doFinal (seed); byte[] l = Arrays.copyOfRange (lr, 0, 32); diff --git a/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java b/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java index 5f78e5ae..6cab8853 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java @@ -103,7 +103,7 @@ public void bip32Test () throws IOException, JSONException, NoSuchAlgorithmExcep { JSONObject test = tests.getJSONObject (i); byte[] seed = ByteUtils.fromHex (test.getString ("master")); - ExtendedKey ekprivate = ExtendedKey.createFromSeed (seed); + ExtendedKey ekprivate = ExtendedKey.createFromSeed ("Bitcoin seed".getBytes (), seed); ExtendedKey ekpublic = ekprivate.getReadOnly (); assertTrue (ekprivate.serialize (true).equals (test.get ("private"))); assertTrue (ekpublic.serialize (true).equals (test.get ("public"))); From ba12c0abd67a1dc38e92dd8acacf93c81b487076 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 4 Jun 2013 22:06:13 +0200 Subject: [PATCH 024/299] better naming --- .../main/java/com/bitsofproof/supernode/api/ExtendedKey.java | 4 ++-- .../java/com/bitsofproof/supernode/api/ExtendedKeyTest.java | 4 ++-- api/src/test/resources/BIP32.json | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java index f1747bc5..742032f7 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java @@ -57,7 +57,7 @@ public static ExtendedKey createFromPassphrase (String passphrase, byte[] seed) { try { - return createFromSeed (passphrase.getBytes ("UTF-8"), seed); + return createFromKey (passphrase.getBytes ("UTF-8"), seed); } catch ( UnsupportedEncodingException e ) { @@ -65,7 +65,7 @@ public static ExtendedKey createFromPassphrase (String passphrase, byte[] seed) } } - public static ExtendedKey createFromSeed (byte[] key, byte[] seed) throws ValidationException + public static ExtendedKey createFromKey (byte[] key, byte[] seed) throws ValidationException { Mac mac; try diff --git a/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java b/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java index 6cab8853..79700996 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java @@ -102,8 +102,8 @@ public void bip32Test () throws IOException, JSONException, NoSuchAlgorithmExcep for ( int i = 0; i < tests.length (); ++i ) { JSONObject test = tests.getJSONObject (i); - byte[] seed = ByteUtils.fromHex (test.getString ("master")); - ExtendedKey ekprivate = ExtendedKey.createFromSeed ("Bitcoin seed".getBytes (), seed); + byte[] seed = ByteUtils.fromHex (test.getString ("seed")); + ExtendedKey ekprivate = ExtendedKey.createFromPassphrase ("Bitcoin seed", seed); ExtendedKey ekpublic = ekprivate.getReadOnly (); assertTrue (ekprivate.serialize (true).equals (test.get ("private"))); assertTrue (ekpublic.serialize (true).equals (test.get ("public"))); diff --git a/api/src/test/resources/BIP32.json b/api/src/test/resources/BIP32.json index f89e74af..d5ac5abd 100644 --- a/api/src/test/resources/BIP32.json +++ b/api/src/test/resources/BIP32.json @@ -1,6 +1,6 @@ [ { - "master":"000102030405060708090a0b0c0d0e0f", + "seed":"000102030405060708090a0b0c0d0e0f", "private":"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", "public":"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", "derived": [ @@ -37,7 +37,7 @@ ] }, { - "master":"fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", + "seed":"fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", "private":"xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", "public":"xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", "derived": [ From b1077367987a918d46459581d7dae46431555f37 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 4 Jun 2013 22:08:56 +0200 Subject: [PATCH 025/299] suggest use --- .../supernode/api/ExtendedKey.java | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java index 742032f7..31fadbaf 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java @@ -57,21 +57,8 @@ public static ExtendedKey createFromPassphrase (String passphrase, byte[] seed) { try { - return createFromKey (passphrase.getBytes ("UTF-8"), seed); - } - catch ( UnsupportedEncodingException e ) - { - throw new ValidationException (e); - } - } - - public static ExtendedKey createFromKey (byte[] key, byte[] seed) throws ValidationException - { - Mac mac; - try - { - mac = Mac.getInstance ("HmacSHA512", "BC"); - SecretKey seedkey = new SecretKeySpec (key, "HmacSHA512"); + Mac mac = Mac.getInstance ("HmacSHA512", "BC"); + SecretKey seedkey = new SecretKeySpec (passphrase.getBytes ("UTF-8"), "HmacSHA512"); mac.init (seedkey); byte[] lr = mac.doFinal (seed); byte[] l = Arrays.copyOfRange (lr, 0, 32); @@ -91,6 +78,10 @@ public static ExtendedKey createFromKey (byte[] key, byte[] seed) throws Validat { throw new ValidationException (e); } + catch ( UnsupportedEncodingException e ) + { + throw new ValidationException (e); + } } public static ExtendedKey createNew () From 17c49caa3f5712aaebedb5c629b91ac65a53b147 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 4 Jun 2013 22:16:42 +0200 Subject: [PATCH 026/299] unlikely case of too big private key catched --- .../java/com/bitsofproof/supernode/api/ExtendedKey.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java index 31fadbaf..95dfef7f 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java @@ -63,7 +63,12 @@ public static ExtendedKey createFromPassphrase (String passphrase, byte[] seed) byte[] lr = mac.doFinal (seed); byte[] l = Arrays.copyOfRange (lr, 0, 32); byte[] r = Arrays.copyOfRange (lr, 32, 64); - ECKeyPair keyPair = new ECKeyPair (l, true); + BigInteger m = new BigInteger (1, l); + if ( m.compareTo (curve.getN ()) >= 0 ) + { + throw new ValidationException ("This is rather unlikely, but it did just happen"); + } + ECKeyPair keyPair = new ECKeyPair (m, true); return new ExtendedKey (keyPair, r, 0, 0, 0); } catch ( NoSuchAlgorithmException e ) From 36b98ede1efc67576ca559fd8ea23ee8d3f219a9 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Wed, 5 Jun 2013 08:06:30 +0200 Subject: [PATCH 027/299] added key from passphrase --- .../supernode/api/ExtendedKey.java | 57 ++++++++++++++++--- .../supernode/api/ExtendedKeyTest.java | 19 +++++-- api/src/test/resources/PassphraseKey.json | 52 +++++++++++++++++ 3 files changed, 116 insertions(+), 12 deletions(-) create mode 100644 api/src/test/resources/PassphraseKey.json diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java index 95dfef7f..84012516 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java @@ -24,12 +24,17 @@ import java.security.NoSuchProviderException; import java.security.SecureRandom; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.generators.SCrypt; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; @@ -53,12 +58,54 @@ public class ExtendedKey private final int parent; private final int sequence; - public static ExtendedKey createFromPassphrase (String passphrase, byte[] seed) throws ValidationException + private static final byte[] BITCOIN_SEED = "Bitcoin seed".getBytes (); + + public static ExtendedKey createFromPassphrase (String passphrase, byte[] encryptedSeed) throws ValidationException + { + try + { + byte[] key = SCrypt.generate (passphrase.getBytes ("UTF-8"), BITCOIN_SEED, 16384, 8, 8, 32); + Cipher cipher = Cipher.getInstance ("AES/ECB/NoPadding", "BC"); + SecretKeySpec keyspec = new SecretKeySpec (key, "AES"); + cipher.init (Cipher.DECRYPT_MODE, keyspec); + return create (cipher.doFinal (encryptedSeed)); + } + catch ( UnsupportedEncodingException e ) + { + throw new ValidationException (e); + } + catch ( IllegalBlockSizeException e ) + { + throw new ValidationException (e); + } + catch ( BadPaddingException e ) + { + throw new ValidationException (e); + } + catch ( InvalidKeyException e ) + { + throw new ValidationException (e); + } + catch ( NoSuchAlgorithmException e ) + { + throw new ValidationException (e); + } + catch ( NoSuchProviderException e ) + { + throw new ValidationException (e); + } + catch ( NoSuchPaddingException e ) + { + throw new ValidationException (e); + } + } + + public static ExtendedKey create (byte[] seed) throws ValidationException { try { Mac mac = Mac.getInstance ("HmacSHA512", "BC"); - SecretKey seedkey = new SecretKeySpec (passphrase.getBytes ("UTF-8"), "HmacSHA512"); + SecretKey seedkey = new SecretKeySpec (BITCOIN_SEED, "HmacSHA512"); mac.init (seedkey); byte[] lr = mac.doFinal (seed); byte[] l = Arrays.copyOfRange (lr, 0, 32); @@ -68,7 +115,7 @@ public static ExtendedKey createFromPassphrase (String passphrase, byte[] seed) { throw new ValidationException ("This is rather unlikely, but it did just happen"); } - ECKeyPair keyPair = new ECKeyPair (m, true); + ECKeyPair keyPair = new ECKeyPair (l, true); return new ExtendedKey (keyPair, r, 0, 0, 0); } catch ( NoSuchAlgorithmException e ) @@ -83,10 +130,6 @@ public static ExtendedKey createFromPassphrase (String passphrase, byte[] seed) { throw new ValidationException (e); } - catch ( UnsupportedEncodingException e ) - { - throw new ValidationException (e); - } } public static ExtendedKey createNew () diff --git a/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java b/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java index 79700996..f4b2c7ea 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java @@ -78,8 +78,6 @@ public void testGenerator () throws ValidationException private static final ThreadMXBean mxb = ManagementFactory.getThreadMXBean (); private static final Logger log = LoggerFactory.getLogger (ExtendedKeyTest.class); - private static final String BIP32 = "BIP32.json"; - private JSONArray readObjectArray (String resource) throws IOException, JSONException { InputStream input = this.getClass ().getResource ("/" + resource).openStream (); @@ -98,12 +96,11 @@ private JSONArray readObjectArray (String resource) throws IOException, JSONExce @Test public void bip32Test () throws IOException, JSONException, NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, ValidationException { - JSONArray tests = readObjectArray (BIP32); + JSONArray tests = readObjectArray ("BIP32.json"); for ( int i = 0; i < tests.length (); ++i ) { JSONObject test = tests.getJSONObject (i); - byte[] seed = ByteUtils.fromHex (test.getString ("seed")); - ExtendedKey ekprivate = ExtendedKey.createFromPassphrase ("Bitcoin seed", seed); + ExtendedKey ekprivate = ExtendedKey.create (ByteUtils.fromHex (test.getString ("seed"))); ExtendedKey ekpublic = ekprivate.getReadOnly (); assertTrue (ekprivate.serialize (true).equals (test.get ("private"))); assertTrue (ekpublic.serialize (true).equals (test.get ("public"))); @@ -133,6 +130,18 @@ public void bip32Test () throws IOException, JSONException, NoSuchAlgorithmExcep } } + @Test + public void bip32PassphraseTest () throws ValidationException, JSONException, IOException + { + JSONArray tests = readObjectArray ("PassphraseKey.json"); + for ( int i = 0; i < tests.length (); ++i ) + { + JSONObject test = tests.getJSONObject (i); + ExtendedKey key = ExtendedKey.createFromPassphrase (test.getString ("passphrase"), ByteUtils.fromHex (test.getString ("seed"))); + assertTrue (key.serialize (true).equals (test.get ("key"))); + } + } + @Test public void testECDSASpeed () throws ValidationException { diff --git a/api/src/test/resources/PassphraseKey.json b/api/src/test/resources/PassphraseKey.json new file mode 100644 index 00000000..a010d62d --- /dev/null +++ b/api/src/test/resources/PassphraseKey.json @@ -0,0 +1,52 @@ +[ + { + "key": "xprv9s21ZrQH143K4CVgC3KYNDJDtmWyoFhUGrBDo7zFWAndVKNCTQfMv1ySjRMSJtt8ucH841DzSrj5BRA37ZR5tHuZP3C1SchStNkGM4zxHQw", + "passphrase": "45mFZcmYWVJKfoHsD", + "seed": "6b94a469f3c146e3a3936d87f1229276999afa6c79bcb4bda6a2fd8def1ee67e" + }, + { + "key": "xprv9s21ZrQH143K47Cpy2x4XStsdbLc6BeLCX16ogh48z5ErSfRZngqVzCF8cpqPyZMC5iVhH7EsshojkeztCjBhdVDwYc6CwRHvELbs5vRD7c", + "passphrase": "2Gu8kmY2rDhW54B2Q", + "seed": "4bf09adce269d5fffd9a3f07e7d9565f7bbc4ad4dd18f73874218343363b1c13" + }, + { + "key": "xprv9s21ZrQH143K2tUA9LGdeweo2kgbQjiKbrxbZCTrEDQwXnC9K6sV9Xw4P1iPB9BHjaksC1inHHwxtbn9dUWEXZy1xmXsAkn5usU3Xtmuc7G", + "passphrase": "WvHcPkjX3Arc2WaK", + "seed": "2a6f8a531d2db27c6721c127e97e69e8c0248a01af533e648ef132940139b68e" + }, + { + "key": "xprv9s21ZrQH143K3oqQ2fnyQV1y2zxcpbZnVEd6z56WGdN7geSqxLNCCc6hQF4b8RoeBNQqrPUa8521fLE7RiRgRKyRu1UefQaCz82WGTgS5vn", + "passphrase": "29kajEskBs5m92x8C", + "seed": "a4ef84ae76e1af87fc7544a79613bb484197f340e3ba150dede339597f9b2711" + }, + { + "key": "xprv9s21ZrQH143K2XPdnZ6oAbchJptFGcFdXWFANuLyVvJ6rTffVeUkJgM8P1ihucCJNqCSvqjURhynFyrVSHm7HNd4tsTHZEYrvX7A4XY8jfB", + "passphrase": "2DVqFEiq6bjitEsGG", + "seed": "09ec671b4eb0e841b79b01e9489a240676c75533cd3d16761832593ada950b6f" + }, + { + "key": "xprv9s21ZrQH143K2ujFZxbsYk1eD1ZZAzF3wr3tzo7tjx2DWHTknzc8MDR2pV63uZZjPYCg5vTV11y1KnpPgBVcmAeDVyuXPo8Fe151K3ATWgh", + "passphrase": "Jo4d6xisTTy2Qq9i", + "seed": "bb2a00047b87ade62982c38475c323b74f15beae311d911bad9c43a9d754acdb" + }, + { + "key": "xprv9s21ZrQH143K2PTfBtmSjRMUrwpTwt4f2qMcM1NXScKwoiTSRfzKbJygM6dyTSfiV9MLLu1W7PwYJwSPw1ayujm7ArJJ7cZ1bLDBFy9Nbf1", + "passphrase": "412dbDryAKsXSH3mp", + "seed": "445a220f55fa67ebe7f8dafceaae81d8dea00e727fc78f0c3979072bfa8dee0b" + }, + { + "key": "xprv9s21ZrQH143K3zGPVAK4CmYb65P7EjhE6NtfaXvBCvjfMCHvmKP2vLwGwr29ds9qHYtvp5cKidqv6eLFKFjq5nsX4TBmXMWPXbqDraw3PTe", + "passphrase": "YMz3UG7CVNHDFW2j", + "seed": "1f757dd1a4353e1a2189eccf00ee11bbc3869e0cd0a8ec24484f79be93661459" + }, + { + "key": "xprv9s21ZrQH143K2orh3kfWgMvzfjkDhDabcrEhbzqgc5d9EwVZEgjhkX78fva61dcgUPrvdxKayYZFa7WMTUTUDSK5WpGMEhMBaTC9WuMs8mY", + "passphrase": "5MufM8DjsrssV7XER", + "seed": "39065f43879a8f760ddb97c766c62e4e49b9d6b78b12cc5140f2a1fb078aa493" + }, + { + "key": "xprv9s21ZrQH143K2FrtAkGLQbnbhjpYCSYm9tLjbrMcAC7ze7vUX9MP1BPqxWzG4rVHTDujYiyVZKrcHRZDs83mhfJpPLetM1Y1qBxZyHxPbRS", + "passphrase": "5hhcS4ytxbWPQaiFi", + "seed": "7a2186bae34519911217fcf657973008f868dd189e5e258b1455dd9f00530c76" + } +] From 7b5707750aa620fb4de730d08d7666c2539a1698 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Wed, 5 Jun 2013 10:15:20 +0200 Subject: [PATCH 028/299] implemented scan for extended public key --- .../com/bitsofproof/supernode/api/BCSAPI.java | 10 + .../supernode/api/BCSAPIMessage.java | 550 +++++++++++++++++- .../supernode/api/JMSServerConnector.java | 74 +++ api/src/main/protobuf/BCSAPIMessage.proto | 7 + .../supernode/core/BlockStore.java | 5 +- .../supernode/core/CachedBlockStore.java | 88 +++ .../supernode/core/ImplementBCSAPI.java | 87 ++- 7 files changed, 808 insertions(+), 13 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java index 710d543e..345ee1e7 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java @@ -146,6 +146,16 @@ public interface BCSAPI */ public void scanTransactions (BloomFilter filter, TransactionListener listener) throws BCSAPIException; + /** + * scan transactions for an account + * + * @param master + * - public master key + * @param listener + * @throws BCSAPIException + */ + public void scanTransactions (ExtendedKey master, TransactionListener listener) throws BCSAPIException; + /** * register listener with a Bloom filter * diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java index a928f61e..70253572 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java @@ -7866,6 +7866,519 @@ public Builder clearAfter() { // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.api.ExactMatchRequest) } + public interface AccountRequestOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // required uint32 bcsapiversion = 1; + boolean hasBcsapiversion(); + int getBcsapiversion(); + + // required string publicKey = 2; + boolean hasPublicKey(); + String getPublicKey(); + + // required uint32 lookAhead = 3; + boolean hasLookAhead(); + int getLookAhead(); + } + public static final class AccountRequest extends + com.google.protobuf.GeneratedMessage + implements AccountRequestOrBuilder { + // Use AccountRequest.newBuilder() to construct. + private AccountRequest(Builder builder) { + super(builder); + } + private AccountRequest(boolean noInit) {} + + private static final AccountRequest defaultInstance; + public static AccountRequest getDefaultInstance() { + return defaultInstance; + } + + public AccountRequest getDefaultInstanceForType() { + return defaultInstance; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_AccountRequest_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_AccountRequest_fieldAccessorTable; + } + + private int bitField0_; + // required uint32 bcsapiversion = 1; + public static final int BCSAPIVERSION_FIELD_NUMBER = 1; + private int bcsapiversion_; + public boolean hasBcsapiversion() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + public int getBcsapiversion() { + return bcsapiversion_; + } + + // required string publicKey = 2; + public static final int PUBLICKEY_FIELD_NUMBER = 2; + private java.lang.Object publicKey_; + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + public String getPublicKey() { + java.lang.Object ref = publicKey_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + if (com.google.protobuf.Internal.isValidUtf8(bs)) { + publicKey_ = s; + } + return s; + } + } + private com.google.protobuf.ByteString getPublicKeyBytes() { + java.lang.Object ref = publicKey_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8((String) ref); + publicKey_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // required uint32 lookAhead = 3; + public static final int LOOKAHEAD_FIELD_NUMBER = 3; + private int lookAhead_; + public boolean hasLookAhead() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + public int getLookAhead() { + return lookAhead_; + } + + private void initFields() { + bcsapiversion_ = 0; + publicKey_ = ""; + lookAhead_ = 0; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + if (!hasBcsapiversion()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasPublicKey()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasLookAhead()) { + memoizedIsInitialized = 0; + return false; + } + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, bcsapiversion_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getPublicKeyBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeUInt32(3, lookAhead_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, bcsapiversion_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getPublicKeyBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(3, lookAhead_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest parseFrom(java.io.InputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + public static com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input, extensionRegistry)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequestOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_AccountRequest_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_AccountRequest_fieldAccessorTable; + } + + // Construct using com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder(BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + bcsapiversion_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + publicKey_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + lookAhead_ = 0; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest.getDescriptor(); + } + + public com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest getDefaultInstanceForType() { + return com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest.getDefaultInstance(); + } + + public com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest build() { + com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + private com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest buildParsed() + throws com.google.protobuf.InvalidProtocolBufferException { + com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException( + result).asInvalidProtocolBufferException(); + } + return result; + } + + public com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest buildPartial() { + com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest result = new com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.bcsapiversion_ = bcsapiversion_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.publicKey_ = publicKey_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.lookAhead_ = lookAhead_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest) { + return mergeFrom((com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest other) { + if (other == com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest.getDefaultInstance()) return this; + if (other.hasBcsapiversion()) { + setBcsapiversion(other.getBcsapiversion()); + } + if (other.hasPublicKey()) { + setPublicKey(other.getPublicKey()); + } + if (other.hasLookAhead()) { + setLookAhead(other.getLookAhead()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + if (!hasBcsapiversion()) { + + return false; + } + if (!hasPublicKey()) { + + return false; + } + if (!hasLookAhead()) { + + return false; + } + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder( + this.getUnknownFields()); + while (true) { + int tag = input.readTag(); + switch (tag) { + case 0: + this.setUnknownFields(unknownFields.build()); + onChanged(); + return this; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + this.setUnknownFields(unknownFields.build()); + onChanged(); + return this; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + bcsapiversion_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + publicKey_ = input.readBytes(); + break; + } + case 24: { + bitField0_ |= 0x00000004; + lookAhead_ = input.readUInt32(); + break; + } + } + } + } + + private int bitField0_; + + // required uint32 bcsapiversion = 1; + private int bcsapiversion_ ; + public boolean hasBcsapiversion() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + public int getBcsapiversion() { + return bcsapiversion_; + } + public Builder setBcsapiversion(int value) { + bitField0_ |= 0x00000001; + bcsapiversion_ = value; + onChanged(); + return this; + } + public Builder clearBcsapiversion() { + bitField0_ = (bitField0_ & ~0x00000001); + bcsapiversion_ = 0; + onChanged(); + return this; + } + + // required string publicKey = 2; + private java.lang.Object publicKey_ = ""; + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + public String getPublicKey() { + java.lang.Object ref = publicKey_; + if (!(ref instanceof String)) { + String s = ((com.google.protobuf.ByteString) ref).toStringUtf8(); + publicKey_ = s; + return s; + } else { + return (String) ref; + } + } + public Builder setPublicKey(String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + publicKey_ = value; + onChanged(); + return this; + } + public Builder clearPublicKey() { + bitField0_ = (bitField0_ & ~0x00000002); + publicKey_ = getDefaultInstance().getPublicKey(); + onChanged(); + return this; + } + void setPublicKey(com.google.protobuf.ByteString value) { + bitField0_ |= 0x00000002; + publicKey_ = value; + onChanged(); + } + + // required uint32 lookAhead = 3; + private int lookAhead_ ; + public boolean hasLookAhead() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + public int getLookAhead() { + return lookAhead_; + } + public Builder setLookAhead(int value) { + bitField0_ |= 0x00000004; + lookAhead_ = value; + onChanged(); + return this; + } + public Builder clearLookAhead() { + bitField0_ = (bitField0_ & ~0x00000004); + lookAhead_ = 0; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.AccountRequest) + } + + static { + defaultInstance = new AccountRequest(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.api.AccountRequest) + } + public interface WalletOrBuilder extends com.google.protobuf.MessageOrBuilder { @@ -10211,6 +10724,11 @@ public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder addTransa private static com.google.protobuf.GeneratedMessage.FieldAccessorTable internal_static_com_bitsofproof_supernode_api_ExactMatchRequest_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_com_bitsofproof_supernode_api_AccountRequest_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_com_bitsofproof_supernode_api_AccountRequest_fieldAccessorTable; private static com.google.protobuf.Descriptors.Descriptor internal_static_com_bitsofproof_supernode_api_Wallet_descriptor; private static @@ -10268,16 +10786,18 @@ public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder addTransa "\030\002 \002(\014\022\025\n\rhashFunctions\030\003 \002(\r\022\r\n\005tweak\030\004" + " \002(\r\022\014\n\004mode\030\005 \002(\r\"V\n\021ExactMatchRequest\022" + "\025\n\rbcsapiversion\030\001 \002(\r\022\014\n\004mode\030\002 \002(\r\022\r\n\005" + - "match\030\003 \003(\014\022\r\n\005after\030\004 \001(\004\"\341\002\n\006Wallet\022\025\n" + - "\rbcsapiversion\030\001 \002(\r\0227\n\004keys\030\002 \003(\0132).com" + - ".bitsofproof.supernode.api.Wallet.Key\022L\n" + - "\017publicAddresses\030\003 \003(\01323.com.bitsofproof" + - ".supernode.api.Wallet.PublicAddress\022@\n\014t" + - "ransactions\030\004 \003(\0132*.com.bitsofproof.supe" + - "rnode.api.Transaction\032G\n\003Key\022\013\n\003key\030\001 \002(", - "\t\022\024\n\014nextSequence\030\002 \002(\r\022\014\n\004name\030\003 \001(\t\022\017\n" + - "\007created\030\004 \001(\004\032.\n\rPublicAddress\022\017\n\007addre" + - "ss\030\001 \002(\t\022\014\n\004name\030\002 \002(\t" + "match\030\003 \003(\014\022\r\n\005after\030\004 \001(\004\"M\n\016AccountReq" + + "uest\022\025\n\rbcsapiversion\030\001 \002(\r\022\021\n\tpublicKey" + + "\030\002 \002(\t\022\021\n\tlookAhead\030\003 \002(\r\"\341\002\n\006Wallet\022\025\n\r" + + "bcsapiversion\030\001 \002(\r\0227\n\004keys\030\002 \003(\0132).com." + + "bitsofproof.supernode.api.Wallet.Key\022L\n\017" + + "publicAddresses\030\003 \003(\01323.com.bitsofproof." + + "supernode.api.Wallet.PublicAddress\022@\n\014tr", + "ansactions\030\004 \003(\0132*.com.bitsofproof.super" + + "node.api.Transaction\032G\n\003Key\022\013\n\003key\030\001 \002(\t" + + "\022\024\n\014nextSequence\030\002 \002(\r\022\014\n\004name\030\003 \001(\t\022\017\n\007" + + "created\030\004 \001(\004\032.\n\rPublicAddress\022\017\n\007addres" + + "s\030\001 \002(\t\022\014\n\004name\030\002 \002(\t" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -10380,8 +10900,16 @@ public com.google.protobuf.ExtensionRegistry assignDescriptors( new java.lang.String[] { "Bcsapiversion", "Mode", "Match", "After", }, com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest.class, com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest.Builder.class); - internal_static_com_bitsofproof_supernode_api_Wallet_descriptor = + internal_static_com_bitsofproof_supernode_api_AccountRequest_descriptor = getDescriptor().getMessageTypes().get(12); + internal_static_com_bitsofproof_supernode_api_AccountRequest_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_com_bitsofproof_supernode_api_AccountRequest_descriptor, + new java.lang.String[] { "Bcsapiversion", "PublicKey", "LookAhead", }, + com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest.class, + com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest.Builder.class); + internal_static_com_bitsofproof_supernode_api_Wallet_descriptor = + getDescriptor().getMessageTypes().get(13); internal_static_com_bitsofproof_supernode_api_Wallet_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_com_bitsofproof_supernode_api_Wallet_descriptor, diff --git a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java index 57897f98..e0828167 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java @@ -68,6 +68,7 @@ public class JMSServerConnector implements BCSAPI private MessageProducer colorRequestProducer; private MessageProducer filterRequestProducer; private MessageProducer scanRequestProducer; + private MessageProducer scanAccountProducer; private MessageProducer exactMatchProducer; private Boolean production = null; @@ -224,6 +225,7 @@ public void init () filterRequestProducer = session.createProducer (session.createQueue ("filterRequest")); scanRequestProducer = session.createProducer (session.createQueue ("scanRequest")); exactMatchProducer = session.createProducer (session.createQueue ("matchRequest")); + scanAccountProducer = session.createProducer (session.createQueue ("accountRequest")); } catch ( Exception e ) { @@ -469,6 +471,78 @@ public void onMessage (Message message) } } + @Override + public void scanTransactions (ExtendedKey master, final TransactionListener listener) throws BCSAPIException + { + if ( !master.isReadOnly () ) + { + master = master.getReadOnly (); + } + try + { + BytesMessage m = session.createBytesMessage (); + + BCSAPIMessage.AccountRequest.Builder builder = BCSAPIMessage.AccountRequest.newBuilder (); + builder.setBcsapiversion (1); + builder.setPublicKey (master.serialize (production)); + m.writeBytes (builder.build ().toByteArray ()); + + final TemporaryQueue answerQueue = session.createTemporaryQueue (); + final MessageConsumer consumer = session.createConsumer (answerQueue); + m.setJMSReplyTo (answerQueue); + final Semaphore ready = new Semaphore (0); + consumer.setMessageListener (new MessageListener () + { + @Override + public void onMessage (Message message) + { + BytesMessage m = (BytesMessage) message; + byte[] body; + try + { + if ( m.getBodyLength () > 0 ) + { + body = new byte[(int) m.getBodyLength ()]; + m.readBytes (body); + Transaction t = Transaction.fromProtobuf (BCSAPIMessage.Transaction.parseFrom (body)); + t.computeHash (); + listener.process (t); + } + else + { + consumer.close (); + answerQueue.delete (); + ready.release (); + } + } + catch ( JMSException e ) + { + log.error ("Malformed message received for account scan transactions", e); + } + catch ( InvalidProtocolBufferException e ) + { + log.error ("Malformed message received for account scan transactions", e); + } + } + }); + + scanAccountProducer.send (m); + if ( ready.tryAcquire (timeout, TimeUnit.MILLISECONDS) == false ) + { + throw new BCSAPIException ("timeout"); + } + } + catch ( JMSException e ) + { + throw new BCSAPIException (e); + } + catch ( InterruptedException e ) + { + throw new BCSAPIException (e); + } + + } + @Override public void registerFilteredListener (final BloomFilter filter, final TransactionListener listener) throws BCSAPIException { diff --git a/api/src/main/protobuf/BCSAPIMessage.proto b/api/src/main/protobuf/BCSAPIMessage.proto index 5ffdc372..1b93fc32 100644 --- a/api/src/main/protobuf/BCSAPIMessage.proto +++ b/api/src/main/protobuf/BCSAPIMessage.proto @@ -109,6 +109,13 @@ message ExactMatchRequest optional uint64 after = 4; } +message AccountRequest +{ + required uint32 bcsapiversion = 1; + required string publicKey = 2; + required uint32 lookAhead = 3; +} + message Wallet { required uint32 bcsapiversion = 1; message Key diff --git a/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java index aa46eb32..76404c68 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java @@ -18,10 +18,11 @@ import java.util.List; import java.util.Set; +import com.bitsofproof.supernode.api.ExtendedKey; import com.bitsofproof.supernode.common.BloomFilter; +import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.common.ByteVector; import com.bitsofproof.supernode.common.ValidationException; -import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.model.Blk; import com.bitsofproof.supernode.model.Tx; @@ -76,4 +77,6 @@ public interface TransactionProcessor public void scan (BloomFilter filter, TransactionProcessor processor) throws ValidationException; public void filterTransactions (Set match, UpdateMode update, long after, TransactionProcessor processor) throws ValidationException; + + public void filterTransactions (Set match, ExtendedKey ek, int lookAhead, TransactionProcessor processor) throws ValidationException; } \ No newline at end of file diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 538d1a28..ed722d4e 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -45,6 +45,7 @@ import com.bitsofproof.supernode.api.ColorRules; import com.bitsofproof.supernode.api.ColorRules.ColoredCoin; +import com.bitsofproof.supernode.api.ExtendedKey; import com.bitsofproof.supernode.common.BloomFilter; import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.common.ByteUtils; @@ -409,6 +410,93 @@ else if ( blockChain.getLast () != p ) private static final int MAX_MATCH_SET = 1000; + @Transactional (propagation = Propagation.REQUIRED, rollbackFor = { Exception.class }, readOnly = true) + @Override + public void filterTransactions (Set matchSet, ExtendedKey ek, int lookAhead, TransactionProcessor processor) throws ValidationException + { + Map addressSet = new HashMap (); + lookAhead = Math.min (Math.max (10, lookAhead), MAX_MATCH_SET); + for ( int i = 0; i < lookAhead; ++i ) + { + ByteVector address = new ByteVector (ek.getKey (i).getAddress ()); + matchSet.add (address); + addressSet.put (address, i); + } + try + { + Map> hashes = new HashMap> (); + lock.readLock ().lock (); + + for ( CachedBlock cb : blockChain ) + { + boolean found = false; + BloomFilter filter = new BloomFilter (cb.filterMap, cb.filterFunctions, 0, UpdateMode.none); + for ( ByteVector v : matchSet ) + { + List hashList = hashes.get (v); + if ( hashList == null ) + { + hashList = BloomFilter.precomputeHashes (v.toByteArray (), 0); + hashes.put (v, hashList); + } + if ( filter.contains (hashList) ) + { + found = true; + log.trace ("Match in block " + cb.height); + break; + } + } + if ( !found ) + { + continue; + } + try + { + int n = 0; + Blk b = retrieveBlock (cb); + for ( Tx t : b.getTransactions () ) + { + if ( t.matches (matchSet, UpdateMode.keys) ) + { + processor.process (t); + + int lastUsedAddress = 0; + for ( TxOut o : t.getOutputs () ) + { + for ( Token token : ScriptFormat.parse (o.getScript ()) ) + { + Integer usedAddress; + if ( token.data != null && (usedAddress = addressSet.get (new ByteVector (token.data))) != null ) + { + lastUsedAddress = Math.max (usedAddress, lastUsedAddress); + } + } + } + for ( int i = addressSet.size (); i < lastUsedAddress + lookAhead; ++i ) + { + ByteVector address = new ByteVector (ek.getKey (i).getAddress ()); + matchSet.add (address); + addressSet.put (address, i); + } + ++n; + } + } + log.trace ("Matched transactions in block: " + n + " using " + addressSet.size () + " addresses"); + } + catch ( ValidationException e ) + { + log.error ("Error while scanning blocks", e); + } + } + } + finally + { + lock.readLock ().unlock (); + } + processor.process (null); + log.trace ("Done with Match request"); + } + @Transactional (propagation = Propagation.REQUIRED, rollbackFor = { Exception.class }, readOnly = true) @Override public void filterTransactions (Set matchSet, UpdateMode update, long after, TransactionProcessor processor) throws ValidationException diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index 49690f8d..7d3307dc 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -47,14 +47,15 @@ import com.bitsofproof.supernode.api.BCSAPIMessage; import com.bitsofproof.supernode.api.Block; import com.bitsofproof.supernode.api.Color; +import com.bitsofproof.supernode.api.ExtendedKey; import com.bitsofproof.supernode.api.Transaction; import com.bitsofproof.supernode.api.TrunkUpdateMessage; import com.bitsofproof.supernode.common.BloomFilter; +import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.common.ByteVector; import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.common.WireFormat; -import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.core.BlockStore.TransactionProcessor; import com.bitsofproof.supernode.messages.BlockMessage; import com.bitsofproof.supernode.model.Blk; @@ -138,6 +139,7 @@ public void init () addBloomScanListener (); addMatchScanListener (); addPingListener (); + addAccountScanListener (); } catch ( JMSException e ) { @@ -348,6 +350,89 @@ public void process (Tx tx) }); } + private void addAccountScanListener () throws JMSException + { + addQueueListener ("accountRequest", new MessageListener () + { + @Override + public void onMessage (Message msg) + { + BytesMessage o = (BytesMessage) msg; + byte[] body; + try + { + body = new byte[(int) o.getBodyLength ()]; + o.readBytes (body); + BCSAPIMessage.AccountRequest request = BCSAPIMessage.AccountRequest.parseFrom (body); + final ExtendedKey ek = ExtendedKey.parse (request.getPublicKey ()); + final int lookAhead = request.getLookAhead (); + final Set match = new HashSet (); + final UpdateMode mode = UpdateMode.all; + final MessageProducer producer = session.createProducer (msg.getJMSReplyTo ()); + requestProcessor.execute (new Runnable () + { + @Override + public void run () + { + try + { + TransactionProcessor processor = new TransactionProcessor () + { + @Override + public void process (Tx tx) + { + if ( tx != null ) + { + Transaction transaction = toBCSAPITransaction (tx); + BytesMessage m; + try + { + m = session.createBytesMessage (); + m.writeBytes (transaction.toProtobuf ().toByteArray ()); + producer.send (m); + } + catch ( JMSException e ) + { + } + } + } + }; + + store.filterTransactions (match, ek, lookAhead, processor); + txhandler.scanUnconfirmedPool (match, mode, processor); + try + { + BytesMessage m = session.createBytesMessage (); + producer.send (m); // indicate EOF + producer.close (); + } + catch ( JMSException e ) + { + } + } + catch ( ValidationException e ) + { + log.error ("Error while scanning", e); + } + } + }); + } + catch ( JMSException e ) + { + log.error ("invalid filter request", e); + } + catch ( InvalidProtocolBufferException e ) + { + log.error ("invalid filter request", e); + } + catch ( ValidationException e ) + { + log.error ("Invalid scan account request", e); + } + } + }); + } + private void addNewColorListener () throws JMSException { addTopicListener ("newColor", new MessageListener () From b86bb9933f5b801741a09e99a3124f2afa58bdbf Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Wed, 5 Jun 2013 10:20:10 +0200 Subject: [PATCH 029/299] add lookAhead to api --- api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java | 2 +- .../java/com/bitsofproof/supernode/api/JMSServerConnector.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java index 345ee1e7..5f0fbd68 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java @@ -154,7 +154,7 @@ public interface BCSAPI * @param listener * @throws BCSAPIException */ - public void scanTransactions (ExtendedKey master, TransactionListener listener) throws BCSAPIException; + public void scanTransactions (ExtendedKey master, int lookAhead, TransactionListener listener) throws BCSAPIException; /** * register listener with a Bloom filter diff --git a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java index e0828167..7259790a 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java @@ -472,7 +472,7 @@ public void onMessage (Message message) } @Override - public void scanTransactions (ExtendedKey master, final TransactionListener listener) throws BCSAPIException + public void scanTransactions (ExtendedKey master, int lookAhead, final TransactionListener listener) throws BCSAPIException { if ( !master.isReadOnly () ) { @@ -485,6 +485,7 @@ public void scanTransactions (ExtendedKey master, final TransactionListener list BCSAPIMessage.AccountRequest.Builder builder = BCSAPIMessage.AccountRequest.newBuilder (); builder.setBcsapiversion (1); builder.setPublicKey (master.serialize (production)); + builder.setLookAhead (lookAhead); m.writeBytes (builder.build ().toByteArray ()); final TemporaryQueue answerQueue = session.createTemporaryQueue (); From b01bdfd4535273aa3d58cc792be4e19cdfe18bcc Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Wed, 5 Jun 2013 10:29:48 +0200 Subject: [PATCH 030/299] how could this be missing? --- server/src/main/java/com/bitsofproof/supernode/main/Main.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/src/main/java/com/bitsofproof/supernode/main/Main.java b/server/src/main/java/com/bitsofproof/supernode/main/Main.java index de11bd9a..3ef94afe 100644 --- a/server/src/main/java/com/bitsofproof/supernode/main/Main.java +++ b/server/src/main/java/com/bitsofproof/supernode/main/Main.java @@ -15,9 +15,11 @@ */ package com.bitsofproof.supernode.main; +import java.security.Security; import java.util.ArrayList; import java.util.List; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.support.GenericXmlApplicationContext; @@ -34,6 +36,7 @@ protected interface App public static void main (String[] args) throws Exception { log.info ("bitsofproof supernode (c) 2013 bits of proof zrt."); + Security.addProvider (new BouncyCastleProvider ()); log.trace ("Spring context setup"); if ( args.length == 0 ) From eda9fde9b2d47e1bdc2968ffc1d22da1141abf99 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Wed, 5 Jun 2013 11:10:38 +0200 Subject: [PATCH 031/299] add scan after for account scan --- .../com/bitsofproof/supernode/api/BCSAPI.java | 2 +- .../supernode/api/BCSAPIMessage.java | 81 ++++++++++++++++--- .../supernode/api/JMSServerConnector.java | 3 +- api/src/main/protobuf/BCSAPIMessage.proto | 1 + .../supernode/core/BlockStore.java | 3 +- .../supernode/core/CachedBlockStore.java | 7 +- .../supernode/core/ImplementBCSAPI.java | 3 +- 7 files changed, 83 insertions(+), 17 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java index 5f0fbd68..598f3e76 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java @@ -154,7 +154,7 @@ public interface BCSAPI * @param listener * @throws BCSAPIException */ - public void scanTransactions (ExtendedKey master, int lookAhead, TransactionListener listener) throws BCSAPIException; + public void scanTransactions (ExtendedKey master, int lookAhead, long after, TransactionListener listener) throws BCSAPIException; /** * register listener with a Bloom filter diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java index 70253572..66420a24 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java @@ -7880,6 +7880,10 @@ public interface AccountRequestOrBuilder // required uint32 lookAhead = 3; boolean hasLookAhead(); int getLookAhead(); + + // optional uint64 after = 4; + boolean hasAfter(); + long getAfter(); } public static final class AccountRequest extends com.google.protobuf.GeneratedMessage @@ -7962,10 +7966,21 @@ public int getLookAhead() { return lookAhead_; } + // optional uint64 after = 4; + public static final int AFTER_FIELD_NUMBER = 4; + private long after_; + public boolean hasAfter() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + public long getAfter() { + return after_; + } + private void initFields() { bcsapiversion_ = 0; publicKey_ = ""; lookAhead_ = 0; + after_ = 0L; } private byte memoizedIsInitialized = -1; public final boolean isInitialized() { @@ -8000,6 +8015,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (((bitField0_ & 0x00000004) == 0x00000004)) { output.writeUInt32(3, lookAhead_); } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeUInt64(4, after_); + } getUnknownFields().writeTo(output); } @@ -8021,6 +8039,10 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeUInt32Size(3, lookAhead_); } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt64Size(4, after_); + } size += getUnknownFields().getSerializedSize(); memoizedSerializedSize = size; return size; @@ -8151,6 +8173,8 @@ public Builder clear() { bitField0_ = (bitField0_ & ~0x00000002); lookAhead_ = 0; bitField0_ = (bitField0_ & ~0x00000004); + after_ = 0L; + bitField0_ = (bitField0_ & ~0x00000008); return this; } @@ -8201,6 +8225,10 @@ public com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest buildPartial() to_bitField0_ |= 0x00000004; } result.lookAhead_ = lookAhead_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.after_ = after_; result.bitField0_ = to_bitField0_; onBuilt(); return result; @@ -8226,6 +8254,9 @@ public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequ if (other.hasLookAhead()) { setLookAhead(other.getLookAhead()); } + if (other.hasAfter()) { + setAfter(other.getAfter()); + } this.mergeUnknownFields(other.getUnknownFields()); return this; } @@ -8284,6 +8315,11 @@ public Builder mergeFrom( lookAhead_ = input.readUInt32(); break; } + case 32: { + bitField0_ |= 0x00000008; + after_ = input.readUInt64(); + break; + } } } } @@ -8368,6 +8404,27 @@ public Builder clearLookAhead() { return this; } + // optional uint64 after = 4; + private long after_ ; + public boolean hasAfter() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + public long getAfter() { + return after_; + } + public Builder setAfter(long value) { + bitField0_ |= 0x00000008; + after_ = value; + onChanged(); + return this; + } + public Builder clearAfter() { + bitField0_ = (bitField0_ & ~0x00000008); + after_ = 0L; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.AccountRequest) } @@ -10786,18 +10843,18 @@ public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder addTransa "\030\002 \002(\014\022\025\n\rhashFunctions\030\003 \002(\r\022\r\n\005tweak\030\004" + " \002(\r\022\014\n\004mode\030\005 \002(\r\"V\n\021ExactMatchRequest\022" + "\025\n\rbcsapiversion\030\001 \002(\r\022\014\n\004mode\030\002 \002(\r\022\r\n\005" + - "match\030\003 \003(\014\022\r\n\005after\030\004 \001(\004\"M\n\016AccountReq" + + "match\030\003 \003(\014\022\r\n\005after\030\004 \001(\004\"\\\n\016AccountReq" + "uest\022\025\n\rbcsapiversion\030\001 \002(\r\022\021\n\tpublicKey" + - "\030\002 \002(\t\022\021\n\tlookAhead\030\003 \002(\r\"\341\002\n\006Wallet\022\025\n\r" + - "bcsapiversion\030\001 \002(\r\0227\n\004keys\030\002 \003(\0132).com." + - "bitsofproof.supernode.api.Wallet.Key\022L\n\017" + - "publicAddresses\030\003 \003(\01323.com.bitsofproof." + - "supernode.api.Wallet.PublicAddress\022@\n\014tr", - "ansactions\030\004 \003(\0132*.com.bitsofproof.super" + - "node.api.Transaction\032G\n\003Key\022\013\n\003key\030\001 \002(\t" + - "\022\024\n\014nextSequence\030\002 \002(\r\022\014\n\004name\030\003 \001(\t\022\017\n\007" + - "created\030\004 \001(\004\032.\n\rPublicAddress\022\017\n\007addres" + - "s\030\001 \002(\t\022\014\n\004name\030\002 \002(\t" + "\030\002 \002(\t\022\021\n\tlookAhead\030\003 \002(\r\022\r\n\005after\030\004 \001(\004" + + "\"\341\002\n\006Wallet\022\025\n\rbcsapiversion\030\001 \002(\r\0227\n\004ke" + + "ys\030\002 \003(\0132).com.bitsofproof.supernode.api" + + ".Wallet.Key\022L\n\017publicAddresses\030\003 \003(\01323.c" + + "om.bitsofproof.supernode.api.Wallet.Publ", + "icAddress\022@\n\014transactions\030\004 \003(\0132*.com.bi" + + "tsofproof.supernode.api.Transaction\032G\n\003K" + + "ey\022\013\n\003key\030\001 \002(\t\022\024\n\014nextSequence\030\002 \002(\r\022\014\n" + + "\004name\030\003 \001(\t\022\017\n\007created\030\004 \001(\004\032.\n\rPublicAd" + + "dress\022\017\n\007address\030\001 \002(\t\022\014\n\004name\030\002 \002(\t" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -10905,7 +10962,7 @@ public com.google.protobuf.ExtensionRegistry assignDescriptors( internal_static_com_bitsofproof_supernode_api_AccountRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_com_bitsofproof_supernode_api_AccountRequest_descriptor, - new java.lang.String[] { "Bcsapiversion", "PublicKey", "LookAhead", }, + new java.lang.String[] { "Bcsapiversion", "PublicKey", "LookAhead", "After", }, com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest.class, com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest.Builder.class); internal_static_com_bitsofproof_supernode_api_Wallet_descriptor = diff --git a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java index 7259790a..cb7b9ce4 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java @@ -472,7 +472,7 @@ public void onMessage (Message message) } @Override - public void scanTransactions (ExtendedKey master, int lookAhead, final TransactionListener listener) throws BCSAPIException + public void scanTransactions (ExtendedKey master, int lookAhead, long after, final TransactionListener listener) throws BCSAPIException { if ( !master.isReadOnly () ) { @@ -486,6 +486,7 @@ public void scanTransactions (ExtendedKey master, int lookAhead, final Transacti builder.setBcsapiversion (1); builder.setPublicKey (master.serialize (production)); builder.setLookAhead (lookAhead); + builder.setAfter (after); m.writeBytes (builder.build ().toByteArray ()); final TemporaryQueue answerQueue = session.createTemporaryQueue (); diff --git a/api/src/main/protobuf/BCSAPIMessage.proto b/api/src/main/protobuf/BCSAPIMessage.proto index 1b93fc32..ac041c5f 100644 --- a/api/src/main/protobuf/BCSAPIMessage.proto +++ b/api/src/main/protobuf/BCSAPIMessage.proto @@ -114,6 +114,7 @@ message AccountRequest required uint32 bcsapiversion = 1; required string publicKey = 2; required uint32 lookAhead = 3; + optional uint64 after = 4; } message Wallet { diff --git a/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java index 76404c68..d5eb2865 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java @@ -78,5 +78,6 @@ public interface TransactionProcessor public void filterTransactions (Set match, UpdateMode update, long after, TransactionProcessor processor) throws ValidationException; - public void filterTransactions (Set match, ExtendedKey ek, int lookAhead, TransactionProcessor processor) throws ValidationException; + public void filterTransactions (Set match, ExtendedKey ek, int lookAhead, long after, TransactionProcessor processor) + throws ValidationException; } \ No newline at end of file diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index ed722d4e..de593830 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -412,7 +412,8 @@ else if ( blockChain.getLast () != p ) @Transactional (propagation = Propagation.REQUIRED, rollbackFor = { Exception.class }, readOnly = true) @Override - public void filterTransactions (Set matchSet, ExtendedKey ek, int lookAhead, TransactionProcessor processor) throws ValidationException + public void filterTransactions (Set matchSet, ExtendedKey ek, int lookAhead, long after, TransactionProcessor processor) + throws ValidationException { Map addressSet = new HashMap (); lookAhead = Math.min (Math.max (10, lookAhead), MAX_MATCH_SET); @@ -429,6 +430,10 @@ public void filterTransactions (Set matchSet, ExtendedKey ek, int lo for ( CachedBlock cb : blockChain ) { + if ( cb.time <= after ) + { + continue; + } boolean found = false; BloomFilter filter = new BloomFilter (cb.filterMap, cb.filterFunctions, 0, UpdateMode.none); for ( ByteVector v : matchSet ) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index 7d3307dc..f5322317 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -366,6 +366,7 @@ public void onMessage (Message msg) BCSAPIMessage.AccountRequest request = BCSAPIMessage.AccountRequest.parseFrom (body); final ExtendedKey ek = ExtendedKey.parse (request.getPublicKey ()); final int lookAhead = request.getLookAhead (); + final long after = request.getAfter (); final Set match = new HashSet (); final UpdateMode mode = UpdateMode.all; final MessageProducer producer = session.createProducer (msg.getJMSReplyTo ()); @@ -398,7 +399,7 @@ public void process (Tx tx) } }; - store.filterTransactions (match, ek, lookAhead, processor); + store.filterTransactions (match, ek, lookAhead, after, processor); txhandler.scanUnconfirmedPool (match, mode, processor); try { From d2fbc8c36ade7ea05470d14853599c5721d604b4 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Wed, 5 Jun 2013 14:18:33 +0200 Subject: [PATCH 032/299] now discovering account use, not stored in wallet --- .../supernode/api/AccountManager.java | 10 +-- .../supernode/api/InMemoryAccountManager.java | 82 ++++++++++++++----- .../supernode/api/SerializedWallet.java | 13 ++- 3 files changed, 70 insertions(+), 35 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java index 40d17e9e..b558e47d 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java @@ -17,18 +17,17 @@ import java.util.Collection; -import com.bitsofproof.supernode.common.BloomFilter; import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ValidationException; public interface AccountManager { - public BloomFilter getFilter (); - public String getName (); public Collection getAddresses (); + public ExtendedKey getMasterKey (); + public Key getKeyForAddress (byte[] address); public Key getKey (int ix) throws ValidationException; @@ -37,10 +36,6 @@ public interface AccountManager public int getNextSequence (); - public Collection getTransactions (); - - public Transaction getTransaction (String hash); - public Transaction pay (byte[] receiver, long amount, long fee) throws ValidationException, BCSAPIException; public Transaction split (long[] amounts, long fee) throws ValidationException, BCSAPIException; @@ -58,5 +53,4 @@ public interface AccountManager public void addAccountListener (AccountListener listener); public void removeAccountListener (AccountListener listener); - } \ No newline at end of file diff --git a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java index 633e66c8..47fe0902 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,6 +35,8 @@ import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.common.ByteVector; import com.bitsofproof.supernode.common.Key; +import com.bitsofproof.supernode.common.ScriptFormat; +import com.bitsofproof.supernode.common.ScriptFormat.Token; import com.bitsofproof.supernode.common.ValidationException; class InMemoryAccountManager implements TransactionListener, TrunkListener, AccountManager @@ -63,12 +66,63 @@ public void setApi (BCSAPI api) this.api = api; } - public InMemoryAccountManager (Wallet wallet, String name, ExtendedKey extended, int nextKey) throws ValidationException + public InMemoryAccountManager (Wallet wallet, String name, ExtendedKey extended) { this.wallet = wallet; this.name = name; this.extended = extended; - for ( int i = 0; i < nextKey; ++i ) + } + + public void init (final int lookAhead, long after) throws BCSAPIException, ValidationException + { + final Map addressUse = new HashMap (); + for ( int i = 0; i < lookAhead; ++i ) + { + Key key = extended.getKey (i); + addressUse.put (new ByteVector (key.getAddress ()), i); + } + final AtomicInteger lastUsedKey = new AtomicInteger (-1); + api.scanTransactions (extended, lookAhead, after, new TransactionListener () + { + @Override + public void process (Transaction t) + { + for ( TransactionOutput o : t.getOutputs () ) + { + try + { + for ( Token token : ScriptFormat.parse (o.getScript ()) ) + { + if ( token.data != null ) + { + Integer thisKey = addressUse.get (new ByteVector (token.data)); + if ( thisKey != null ) + { + lastUsedKey.set (Math.max (thisKey, lastUsedKey.get ())); + } + else + { + while ( thisKey == null && (addressUse.size () - lastUsedKey.get ()) < lookAhead ) + { + Key key = extended.getKey (addressUse.size ()); + addressUse.put (new ByteVector (key.getAddress ()), addressUse.size ()); + } + thisKey = addressUse.get (new ByteVector (token.data)); + if ( thisKey != null ) + { + lastUsedKey.set (Math.max (thisKey, lastUsedKey.get ())); + } + } + } + } + } + catch ( ValidationException e ) + { + } + } + } + }); + for ( int i = 0; i <= lastUsedKey.get (); ++i ) { Key key = extended.getKey (i); keyForAddress.put (new ByteVector (key.getAddress ()), key); @@ -95,12 +149,6 @@ public String getName () return name; } - @Override - public BloomFilter getFilter () - { - return filter; - } - @Override public Collection getAddresses () { @@ -145,18 +193,6 @@ public Key getKey (int ix) throws ValidationException return extended.getKey (ix); } - @Override - public Collection getTransactions () - { - return Collections.unmodifiableCollection (processedTransaction.values ()); - } - - @Override - public Transaction getTransaction (String hash) - { - return processedTransaction.get (hash); - } - public boolean updateWithTransaction (Transaction t) { synchronized ( confirmed ) @@ -420,4 +456,10 @@ private void notifyListener (Transaction t) } } } + + @Override + public ExtendedKey getMasterKey () + { + return extended; + } } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java index 9a79e045..c92c2360 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java @@ -50,8 +50,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; +import com.bitsofproof.supernode.common.ValidationException; public class SerializedWallet implements Wallet { @@ -87,7 +87,6 @@ public AccountManager getAccountManager (String name) throws BCSAPIException { try { - int nextSequence = 0; ExtendedKey extended = null; InMemoryAccountManager am = null; @@ -100,8 +99,9 @@ public AccountManager getAccountManager (String name) throws BCSAPIException return key.am; } extended = ExtendedKey.parse (key.key); - nextSequence = key.nextSequence; - key.am = am = new InMemoryAccountManager (this, name, extended, nextSequence); + key.am = am = new InMemoryAccountManager (this, name, extended); + am.setApi (api); + am.init (10, 0); break; } } @@ -113,11 +113,10 @@ public AccountManager getAccountManager (String name) throws BCSAPIException wk.created = System.currentTimeMillis () / 1000; wk.name = name; wk.key = extended.serialize (production); - nextSequence = wk.nextSequence = 0; addKey (wk); - wk.am = am = new InMemoryAccountManager (this, name, extended, nextSequence); + wk.am = am = new InMemoryAccountManager (this, name, extended); + am.setApi (api); } - am.setApi (api); am.registerFilter (); if ( getTimeStamp () > 0 ) { From a2b54b8f62ccfd24a4953d9edb5adecb69981dba Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Thu, 6 Jun 2013 13:11:58 +0200 Subject: [PATCH 033/299] reduce number of locks used. --- .../bitsofproof/supernode/core/TxHandler.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index 3fe0beea..0708b3b2 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -16,7 +16,6 @@ package com.bitsofproof.supernode.core; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -31,10 +30,10 @@ import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; +import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.common.ByteVector; import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.ValidationException; -import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.core.BlockStore.TransactionProcessor; import com.bitsofproof.supernode.messages.BitcoinMessageListener; import com.bitsofproof.supernode.messages.GetDataMessage; @@ -52,9 +51,9 @@ public class TxHandler implements TrunkListener private final BitcoinNetwork network; - private final Set heard = Collections.synchronizedSet (new HashSet ()); - private final Map unconfirmed = Collections.synchronizedMap (new HashMap ()); - private final Set own = Collections.synchronizedSet (new HashSet ()); + private final Set heard = new HashSet (); + private final Map unconfirmed = new HashMap (); + private final Set own = new HashSet (); private TxOutCache availableOutput = null; private PlatformTransactionManager transactionManager; @@ -239,13 +238,19 @@ public ValidationException doInTransaction (TransactionStatus status) public Tx getTransaction (String hash) { - return unconfirmed.get (hash); + synchronized ( unconfirmed ) + { + return unconfirmed.get (hash); + } } private void cacheTransaction (Tx tx) { log.trace ("Caching unconfirmed transaction " + tx.getHash ()); - unconfirmed.put (tx.getHash (), tx); + synchronized ( unconfirmed ) + { + unconfirmed.put (tx.getHash (), tx); + } for ( TxOut out : tx.getOutputs () ) { From c9ed31790ab5aa60fadc04ef08500861eea42719 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Thu, 6 Jun 2013 13:31:13 +0200 Subject: [PATCH 034/299] rescan unconfirmed after trunk update --- .../bitsofproof/supernode/core/TxHandler.java | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index 0708b3b2..6ddec5ac 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -309,21 +310,8 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo try { // this is already running in cache and transaction context - List dropped = new ArrayList (); - synchronized ( unconfirmed ) { - for ( Blk blk : removedBlocks ) - { - for ( Tx tx : blk.getTransactions () ) - { - if ( !unconfirmed.containsKey (tx.getHash ()) ) - { - cacheTransaction (tx.flatCopy ()); - dropped.add (tx.getHash ()); - } - } - } for ( Blk blk : addedBlocks ) { for ( Tx tx : blk.getTransactions () ) @@ -342,13 +330,17 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo } } } - for ( String o : dropped ) + Iterator txi = unconfirmed.values ().iterator (); + while ( txi.hasNext () ) { - Tx tx = unconfirmed.get (o); - if ( tx != null ) + Tx tx = txi.next (); + try + { + network.getStore ().resolveTransactionInputs (tx, availableOutput); + } + catch ( ValidationException e ) { - sendTransaction (tx, null); - notifyListener (tx); + txi.remove (); } } } From 67ac4f49d6b8d96370fd1c8d08f086c9a114fbd4 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Thu, 6 Jun 2013 13:36:14 +0200 Subject: [PATCH 035/299] remove double spend from outputs too. --- .../java/com/bitsofproof/supernode/core/TxHandler.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index 6ddec5ac..060931c5 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -340,6 +340,14 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo } catch ( ValidationException e ) { + for ( TxOut o : tx.getOutputs () ) + { + availableOutput.remove (o.getTxHash (), o.getIx ()); + } + synchronized ( own ) + { + own.remove (tx.getHash ()); + } txi.remove (); } } From d8d767183856b80c49b9443e065d5e47605d0916 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Thu, 6 Jun 2013 13:44:00 +0200 Subject: [PATCH 036/299] do not cache if already on block chain --- .../bitsofproof/supernode/core/TxHandler.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index 060931c5..ffaaabea 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -203,15 +203,18 @@ public ValidationException doInTransaction (TransactionStatus status) try { - network.getStore ().validateTransaction (t, availableOutput); - sendTransaction (t, peer); - cacheTransaction (t); - notifyListener (t); - if ( peer == null ) + if ( network.getStore ().getTransaction (t.getHash ()) == null ) { - synchronized ( own ) + network.getStore ().validateTransaction (t, availableOutput); + sendTransaction (t, peer); + cacheTransaction (t); + notifyListener (t); + if ( peer == null ) { - own.add (t.getHash ()); + synchronized ( own ) + { + own.add (t.getHash ()); + } } } return null; From f4bd39b08612de3176d864644fc321586b35e4f6 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Thu, 6 Jun 2013 14:22:52 +0200 Subject: [PATCH 037/299] reset and rebuild unconfirmed at trunk update using tx dependency order. --- .../supernode/core/ImplementTxOutCache.java | 6 ++ .../core/ImplementTxOutCacheDelta.java | 7 ++ .../bitsofproof/supernode/core/TxHandler.java | 68 ++++++++++++++----- .../supernode/core/TxOutCache.java | 1 - 4 files changed, 63 insertions(+), 19 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementTxOutCache.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementTxOutCache.java index 4676bac5..7987d952 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementTxOutCache.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementTxOutCache.java @@ -27,6 +27,12 @@ public class ImplementTxOutCache implements TxOutCache private final Map> map = new HashMap> (); private final Set used = new HashSet (); + public void clear () + { + map.clear (); + used.clear (); + } + @Override public TxOut get (String hash, Long ix) { diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementTxOutCacheDelta.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementTxOutCacheDelta.java index 50f867d3..91ffd89c 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementTxOutCacheDelta.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementTxOutCacheDelta.java @@ -34,6 +34,13 @@ public ImplementTxOutCacheDelta (TxOutCache cache) delegate = cache; } + public void reset () + { + used.clear (); + removed.clear (); + added.clear (); + } + @Override public TxOut get (String hash, Long ix) { diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index ffaaabea..98313acb 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -16,12 +16,14 @@ package com.bitsofproof.supernode.core; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeSet; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; @@ -55,9 +57,32 @@ public class TxHandler implements TrunkListener private final Set heard = new HashSet (); private final Map unconfirmed = new HashMap (); private final Set own = new HashSet (); - private TxOutCache availableOutput = null; + private ImplementTxOutCacheDelta availableOutput = null; private PlatformTransactionManager transactionManager; + private final Set dependencyOrderedSet = new TreeSet (new Comparator () + { + @Override + public int compare (Tx a, Tx b) + { + for ( TxIn in : b.getInputs () ) + { + if ( in.getSourceHash ().equals (a.getHash ()) ) + { + return -1; + } + } + for ( TxIn in : a.getInputs () ) + { + if ( in.getSourceHash ().equals (b.getHash ()) ) + { + return 1; + } + } + return 0; + } + }); + private final List transactionListener = new ArrayList (); public void addTransactionListener (TxListener listener) @@ -254,16 +279,17 @@ private void cacheTransaction (Tx tx) synchronized ( unconfirmed ) { unconfirmed.put (tx.getHash (), tx); - } - for ( TxOut out : tx.getOutputs () ) - { - availableOutput.add (out); - } + for ( TxOut out : tx.getOutputs () ) + { + availableOutput.add (out); + } + for ( TxIn in : tx.getInputs () ) + { + availableOutput.remove (in.getSourceHash (), in.getIx ()); + } - for ( TxIn in : tx.getInputs () ) - { - availableOutput.remove (in.getSourceHash (), in.getIx ()); + dependencyOrderedSet.add (tx); } } @@ -326,31 +352,37 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo { own.remove (tx.getHash ()); } - for ( TxOut o : tx.getOutputs () ) - { - availableOutput.remove (o.getTxHash (), o.getIx ()); - } + dependencyOrderedSet.remove (tx.getHash ()); } } } - Iterator txi = unconfirmed.values ().iterator (); + + availableOutput.reset (); + + Iterator txi = dependencyOrderedSet.iterator (); while ( txi.hasNext () ) { Tx tx = txi.next (); try { network.getStore ().resolveTransactionInputs (tx, availableOutput); + + for ( TxOut out : tx.getOutputs () ) + { + availableOutput.add (out); + } + for ( TxIn in : tx.getInputs () ) + { + availableOutput.remove (in.getSourceHash (), in.getIx ()); + } } catch ( ValidationException e ) { - for ( TxOut o : tx.getOutputs () ) - { - availableOutput.remove (o.getTxHash (), o.getIx ()); - } synchronized ( own ) { own.remove (tx.getHash ()); } + unconfirmed.remove (tx.getHash ()); txi.remove (); } } diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxOutCache.java b/server/src/main/java/com/bitsofproof/supernode/core/TxOutCache.java index 3417f2d0..11c2f1aa 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxOutCache.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxOutCache.java @@ -19,7 +19,6 @@ public interface TxOutCache { - public TxOut get (String hash, Long ix); public TxOut use (String hash, Long ix); From b3de2b9da83bb1b609289986d071ede28e938f30 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Thu, 6 Jun 2013 14:50:10 +0200 Subject: [PATCH 038/299] provide context for error --- .../src/main/java/com/bitsofproof/supernode/core/TxHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index 98313acb..ac33d700 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -390,7 +390,7 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo } catch ( Exception e ) { - log.error ("Error broadcasting trunk update"); + log.error ("Error broadcasting trunk update", e); } } } From e56dc55bde2987381b4c33ec15e3181f859eb1a6 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Thu, 6 Jun 2013 15:08:36 +0200 Subject: [PATCH 039/299] fix dependency set access --- .../src/main/java/com/bitsofproof/supernode/core/TxHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index ac33d700..f5ad8225 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -352,7 +352,7 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo { own.remove (tx.getHash ()); } - dependencyOrderedSet.remove (tx.getHash ()); + dependencyOrderedSet.remove (tx); } } } From 23a30c2c23df8ee0f1a74852e4ec405de0f2e2d9 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Thu, 6 Jun 2013 17:07:40 +0200 Subject: [PATCH 040/299] broadcast transactions on the transaction topic if they are first seen in a block. re-broadcast transactions with a double spend flag set if they are rendered to double spend by a block or block reorg. --- .../supernode/api/BCSAPIMessage.java | 126 +++++++++++++----- .../supernode/api/Transaction.java | 19 +++ api/src/main/protobuf/BCSAPIMessage.proto | 1 + .../supernode/core/ImplementBCSAPI.java | 20 +-- .../bitsofproof/supernode/core/TxHandler.java | 31 ++++- .../supernode/core/TxListener.java | 2 +- 6 files changed, 153 insertions(+), 46 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java index 66420a24..f134118d 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java @@ -1991,6 +1991,10 @@ com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutputOrBuilder getOutput // optional bytes block = 7; boolean hasBlock(); com.google.protobuf.ByteString getBlock(); + + // optional bool doubleSpend = 8; + boolean hasDoubleSpend(); + boolean getDoubleSpend(); } public static final class Transaction extends com.google.protobuf.GeneratedMessage @@ -2103,6 +2107,16 @@ public com.google.protobuf.ByteString getBlock() { return block_; } + // optional bool doubleSpend = 8; + public static final int DOUBLESPEND_FIELD_NUMBER = 8; + private boolean doubleSpend_; + public boolean hasDoubleSpend() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + public boolean getDoubleSpend() { + return doubleSpend_; + } + private void initFields() { bcsapiversion_ = 0; version_ = 0; @@ -2110,6 +2124,7 @@ private void initFields() { inputs_ = java.util.Collections.emptyList(); outputs_ = java.util.Collections.emptyList(); block_ = com.google.protobuf.ByteString.EMPTY; + doubleSpend_ = false; } private byte memoizedIsInitialized = -1; public final boolean isInitialized() { @@ -2165,6 +2180,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (((bitField0_ & 0x00000008) == 0x00000008)) { output.writeBytes(7, block_); } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeBool(8, doubleSpend_); + } getUnknownFields().writeTo(output); } @@ -2198,6 +2216,10 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeBytesSize(7, block_); } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(8, doubleSpend_); + } size += getUnknownFields().getSerializedSize(); memoizedSerializedSize = size; return size; @@ -2344,6 +2366,8 @@ public Builder clear() { } block_ = com.google.protobuf.ByteString.EMPTY; bitField0_ = (bitField0_ & ~0x00000020); + doubleSpend_ = false; + bitField0_ = (bitField0_ & ~0x00000040); return this; } @@ -2416,6 +2440,10 @@ public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction buildPartial() { to_bitField0_ |= 0x00000008; } result.block_ = block_; + if (((from_bitField0_ & 0x00000040) == 0x00000040)) { + to_bitField0_ |= 0x00000010; + } + result.doubleSpend_ = doubleSpend_; result.bitField0_ = to_bitField0_; onBuilt(); return result; @@ -2496,6 +2524,9 @@ public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.Transaction if (other.hasBlock()) { setBlock(other.getBlock()); } + if (other.hasDoubleSpend()) { + setDoubleSpend(other.getDoubleSpend()); + } this.mergeUnknownFields(other.getUnknownFields()); return this; } @@ -2583,6 +2614,11 @@ public Builder mergeFrom( block_ = input.readBytes(); break; } + case 64: { + bitField0_ |= 0x00000040; + doubleSpend_ = input.readBool(); + break; + } } } } @@ -3048,6 +3084,27 @@ public Builder clearBlock() { return this; } + // optional bool doubleSpend = 8; + private boolean doubleSpend_ ; + public boolean hasDoubleSpend() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + public boolean getDoubleSpend() { + return doubleSpend_; + } + public Builder setDoubleSpend(boolean value) { + bitField0_ |= 0x00000040; + doubleSpend_ = value; + onChanged(); + return this; + } + public Builder clearDoubleSpend() { + bitField0_ = (bitField0_ & ~0x00000040); + doubleSpend_ = false; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.Transaction) } @@ -10818,43 +10875,44 @@ public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder addTransa "\006source\030\001 \002(\014\022\020\n\010sourceix\030\002 \002(\r\022\020\n\010seque" + "nce\030\003 \002(\r\022\016\n\006script\030\004 \002(\014\"A\n\021Transaction" + "Output\022\r\n\005value\030\001 \002(\004\022\016\n\006script\030\002 \002(\014\022\r\n" + - "\005color\030\003 \001(\014\"\332\001\n\013Transaction\022\025\n\rbcsapive" + + "\005color\030\003 \001(\014\"\357\001\n\013Transaction\022\025\n\rbcsapive" + "rsion\030\001 \002(\r\022\017\n\007version\030\003 \002(\r\022\020\n\010locktime", "\030\004 \002(\r\022?\n\006inputs\030\005 \003(\0132/.com.bitsofproof" + ".supernode.api.TransactionInput\022A\n\007outpu" + "ts\030\006 \003(\01320.com.bitsofproof.supernode.api" + - ".TransactionOutput\022\r\n\005block\030\007 \001(\014\"\342\001\n\005Bl" + - "ock\022\025\n\rbcsapiversion\030\001 \002(\r\022\017\n\007version\030\002 " + - "\002(\r\022\025\n\rpreviousBlock\030\003 \002(\014\022\022\n\nmerkleRoot" + - "\030\004 \002(\014\022\021\n\ttimestamp\030\005 \002(\r\022\022\n\ndifficulty\030" + - "\006 \002(\r\022\r\n\005nonce\030\007 \002(\r\022@\n\014transactions\030\010 \003" + - "(\0132*.com.bitsofproof.supernode.api.Trans" + - "action\022\016\n\006height\030\t \001(\r\"\220\001\n\013TrunkUpdate\022\025", - "\n\rbcsapiversion\030\001 \002(\r\0223\n\005added\030\002 \003(\0132$.c" + - "om.bitsofproof.supernode.api.Block\0225\n\007re" + - "moved\030\003 \003(\0132$.com.bitsofproof.supernode." + - "api.Block\"+\n\004Hash\022\025\n\rbcsapiversion\030\001 \002(\r" + - "\022\014\n\004hash\030\002 \003(\014\":\n\020ExceptionMessage\022\025\n\rbc" + - "sapiversion\030\001 \002(\r\022\017\n\007message\030\002 \003(\t\"r\n\005Co" + - "lor\022\023\n\013transaction\030\001 \002(\014\022\r\n\005terms\030\002 \002(\t\022" + - "\014\n\004unit\030\003 \002(\004\022\024\n\014expiryHeight\030\004 \002(\r\022\016\n\006p" + - "ubkey\030\005 \002(\014\022\021\n\tsignature\030\006 \002(\014\"j\n\rFilter" + - "Request\022\025\n\rbcsapiversion\030\001 \002(\r\022\016\n\006filter", - "\030\002 \002(\014\022\025\n\rhashFunctions\030\003 \002(\r\022\r\n\005tweak\030\004" + - " \002(\r\022\014\n\004mode\030\005 \002(\r\"V\n\021ExactMatchRequest\022" + - "\025\n\rbcsapiversion\030\001 \002(\r\022\014\n\004mode\030\002 \002(\r\022\r\n\005" + - "match\030\003 \003(\014\022\r\n\005after\030\004 \001(\004\"\\\n\016AccountReq" + - "uest\022\025\n\rbcsapiversion\030\001 \002(\r\022\021\n\tpublicKey" + - "\030\002 \002(\t\022\021\n\tlookAhead\030\003 \002(\r\022\r\n\005after\030\004 \001(\004" + - "\"\341\002\n\006Wallet\022\025\n\rbcsapiversion\030\001 \002(\r\0227\n\004ke" + - "ys\030\002 \003(\0132).com.bitsofproof.supernode.api" + - ".Wallet.Key\022L\n\017publicAddresses\030\003 \003(\01323.c" + - "om.bitsofproof.supernode.api.Wallet.Publ", - "icAddress\022@\n\014transactions\030\004 \003(\0132*.com.bi" + - "tsofproof.supernode.api.Transaction\032G\n\003K" + - "ey\022\013\n\003key\030\001 \002(\t\022\024\n\014nextSequence\030\002 \002(\r\022\014\n" + - "\004name\030\003 \001(\t\022\017\n\007created\030\004 \001(\004\032.\n\rPublicAd" + - "dress\022\017\n\007address\030\001 \002(\t\022\014\n\004name\030\002 \002(\t" + ".TransactionOutput\022\r\n\005block\030\007 \001(\014\022\023\n\013dou" + + "bleSpend\030\010 \001(\010\"\342\001\n\005Block\022\025\n\rbcsapiversio" + + "n\030\001 \002(\r\022\017\n\007version\030\002 \002(\r\022\025\n\rpreviousBloc" + + "k\030\003 \002(\014\022\022\n\nmerkleRoot\030\004 \002(\014\022\021\n\ttimestamp" + + "\030\005 \002(\r\022\022\n\ndifficulty\030\006 \002(\r\022\r\n\005nonce\030\007 \002(" + + "\r\022@\n\014transactions\030\010 \003(\0132*.com.bitsofproo" + + "f.supernode.api.Transaction\022\016\n\006height\030\t ", + "\001(\r\"\220\001\n\013TrunkUpdate\022\025\n\rbcsapiversion\030\001 \002" + + "(\r\0223\n\005added\030\002 \003(\0132$.com.bitsofproof.supe" + + "rnode.api.Block\0225\n\007removed\030\003 \003(\0132$.com.b" + + "itsofproof.supernode.api.Block\"+\n\004Hash\022\025" + + "\n\rbcsapiversion\030\001 \002(\r\022\014\n\004hash\030\002 \003(\014\":\n\020E" + + "xceptionMessage\022\025\n\rbcsapiversion\030\001 \002(\r\022\017" + + "\n\007message\030\002 \003(\t\"r\n\005Color\022\023\n\013transaction\030" + + "\001 \002(\014\022\r\n\005terms\030\002 \002(\t\022\014\n\004unit\030\003 \002(\004\022\024\n\014ex" + + "piryHeight\030\004 \002(\r\022\016\n\006pubkey\030\005 \002(\014\022\021\n\tsign" + + "ature\030\006 \002(\014\"j\n\rFilterRequest\022\025\n\rbcsapive", + "rsion\030\001 \002(\r\022\016\n\006filter\030\002 \002(\014\022\025\n\rhashFunct" + + "ions\030\003 \002(\r\022\r\n\005tweak\030\004 \002(\r\022\014\n\004mode\030\005 \002(\r\"" + + "V\n\021ExactMatchRequest\022\025\n\rbcsapiversion\030\001 " + + "\002(\r\022\014\n\004mode\030\002 \002(\r\022\r\n\005match\030\003 \003(\014\022\r\n\005afte" + + "r\030\004 \001(\004\"\\\n\016AccountRequest\022\025\n\rbcsapiversi" + + "on\030\001 \002(\r\022\021\n\tpublicKey\030\002 \002(\t\022\021\n\tlookAhead" + + "\030\003 \002(\r\022\r\n\005after\030\004 \001(\004\"\341\002\n\006Wallet\022\025\n\rbcsa" + + "piversion\030\001 \002(\r\0227\n\004keys\030\002 \003(\0132).com.bits" + + "ofproof.supernode.api.Wallet.Key\022L\n\017publ" + + "icAddresses\030\003 \003(\01323.com.bitsofproof.supe", + "rnode.api.Wallet.PublicAddress\022@\n\014transa" + + "ctions\030\004 \003(\0132*.com.bitsofproof.supernode" + + ".api.Transaction\032G\n\003Key\022\013\n\003key\030\001 \002(\t\022\024\n\014" + + "nextSequence\030\002 \002(\r\022\014\n\004name\030\003 \001(\t\022\017\n\007crea" + + "ted\030\004 \001(\004\032.\n\rPublicAddress\022\017\n\007address\030\001 " + + "\002(\t\022\014\n\004name\030\002 \002(\t" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -10898,7 +10956,7 @@ public com.google.protobuf.ExtensionRegistry assignDescriptors( internal_static_com_bitsofproof_supernode_api_Transaction_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_com_bitsofproof_supernode_api_Transaction_descriptor, - new java.lang.String[] { "Bcsapiversion", "Version", "Locktime", "Inputs", "Outputs", "Block", }, + new java.lang.String[] { "Bcsapiversion", "Version", "Locktime", "Inputs", "Outputs", "Block", "DoubleSpend", }, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.class, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder.class); internal_static_com_bitsofproof_supernode_api_Block_descriptor = diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java b/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java index 98170440..6d884fc8 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java @@ -41,6 +41,7 @@ public class Transaction implements Serializable, Cloneable private long lockTime = 0; private String hash; private String blockHash; + private boolean doubleSpend = false; private List inputs; private List outputs; @@ -368,6 +369,16 @@ public void setOutputs (List outputs) this.outputs = outputs; } + public boolean isDoubleSpend () + { + return doubleSpend; + } + + public void setDoubleSpend (boolean doubleSpend) + { + this.doubleSpend = doubleSpend; + } + public void toWire (WireFormat.Writer writer) { writer.writeUint32 (version); @@ -510,6 +521,10 @@ public BCSAPIMessage.Transaction toProtobuf () { builder.setBlock (ByteString.copyFrom (new Hash (blockHash).toByteArray ())); } + if ( doubleSpend ) + { + builder.setDoubleSpend (true); + } return builder.build (); } @@ -539,6 +554,10 @@ public static Transaction fromProtobuf (BCSAPIMessage.Transaction pt) { transaction.blockHash = new Hash (pt.getBlock ().toByteArray ()).toString (); } + if ( pt.hasDoubleSpend () && pt.getDoubleSpend () ) + { + transaction.doubleSpend = true; + } return transaction; } } diff --git a/api/src/main/protobuf/BCSAPIMessage.proto b/api/src/main/protobuf/BCSAPIMessage.proto index ac041c5f..8e4083e7 100644 --- a/api/src/main/protobuf/BCSAPIMessage.proto +++ b/api/src/main/protobuf/BCSAPIMessage.proto @@ -50,6 +50,7 @@ message Transaction { repeated TransactionInput inputs = 5; repeated TransactionOutput outputs = 6; optional bytes block = 7; + optional bool doubleSpend = 8; // set if this transaction IS a double spend and should be removed/ignored } // a block. diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index f5322317..a22df349 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -222,7 +222,7 @@ public void process (Tx tx) { if ( tx != null ) { - Transaction transaction = toBCSAPITransaction (tx); + Transaction transaction = toBCSAPITransaction (tx, false); BytesMessage m; try { @@ -304,7 +304,7 @@ public void process (Tx tx) { if ( tx != null ) { - Transaction transaction = toBCSAPITransaction (tx); + Transaction transaction = toBCSAPITransaction (tx, false); BytesMessage m; try { @@ -384,7 +384,7 @@ public void process (Tx tx) { if ( tx != null ) { - Transaction transaction = toBCSAPITransaction (tx); + Transaction transaction = toBCSAPITransaction (tx, false); BytesMessage m; try { @@ -755,7 +755,7 @@ public Transaction doInTransaction (TransactionStatus status) t = store.getTransaction (hash); if ( t != null ) { - return toBCSAPITransaction (t); + return toBCSAPITransaction (t, false); } } catch ( ValidationException e ) @@ -767,7 +767,7 @@ public Transaction doInTransaction (TransactionStatus status) } else { - return toBCSAPITransaction (tx); + return toBCSAPITransaction (tx, false); } } finally @@ -921,11 +921,11 @@ private void sendBlock (Block block) throws ValidationException } @Override - public void process (Tx tx) + public void process (Tx tx, boolean doubleSpend) { try { - Transaction transaction = toBCSAPITransaction (tx); + Transaction transaction = toBCSAPITransaction (tx, doubleSpend); BytesMessage m = session.createBytesMessage (); m.writeBytes (transaction.toProtobuf ().toByteArray ()); transactionProducer.send (m); @@ -947,7 +947,7 @@ public void process (Tx tx) } } - private Transaction toBCSAPITransaction (Tx tx) + private Transaction toBCSAPITransaction (Tx tx, boolean doubleSpend) { WireFormat.Writer writer = new WireFormat.Writer (); tx.toWire (writer); @@ -956,6 +956,10 @@ private Transaction toBCSAPITransaction (Tx tx) { transaction.setBlockHash (tx.getBlockHash ()); } + if ( doubleSpend ) + { + transaction.setDoubleSpend (true); + } return transaction; } diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index f5ad8225..f4bbd918 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -233,7 +233,7 @@ public ValidationException doInTransaction (TransactionStatus status) network.getStore ().validateTransaction (t, availableOutput); sendTransaction (t, peer); cacheTransaction (t); - notifyListener (t); + notifyListener (t, false); if ( peer == null ) { synchronized ( own ) @@ -310,12 +310,12 @@ private void sendTransaction (Tx tx, BitcoinPeer peer) log.debug ("relaying transaction " + tx.getHash ()); } - private void notifyListener (Tx tx) + private void notifyListener (Tx tx, boolean doubleSpend) { for ( TxListener l : transactionListener ) { // This further extends transaction and cache context - l.process (tx); + l.process (tx, doubleSpend); } } @@ -341,10 +341,30 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo // this is already running in cache and transaction context synchronized ( unconfirmed ) { + for ( Blk blk : removedBlocks ) + { + boolean coinbase = true; + for ( Tx tx : blk.getTransactions () ) + { + if ( coinbase ) + { + coinbase = false; + continue; + } + unconfirmed.put (tx.getHash (), tx); + dependencyOrderedSet.add (tx); + } + } for ( Blk blk : addedBlocks ) { + boolean coinbase = true; for ( Tx tx : blk.getTransactions () ) { + if ( coinbase ) + { + coinbase = false; + continue; + } if ( unconfirmed.containsKey (tx.getHash ()) ) { unconfirmed.remove (tx.getHash ()); @@ -354,6 +374,10 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo } dependencyOrderedSet.remove (tx); } + else + { + notifyListener (tx, false); + } } } @@ -384,6 +408,7 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo } unconfirmed.remove (tx.getHash ()); txi.remove (); + notifyListener (tx, true); } } } diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxListener.java b/server/src/main/java/com/bitsofproof/supernode/core/TxListener.java index 40395239..df990b8e 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxListener.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxListener.java @@ -19,5 +19,5 @@ public interface TxListener { - public void process (Tx transaction); + public void process (Tx transaction, boolean doubleSpend); } From f0265609c523a502cb9fb2591a409251ae9374e4 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Thu, 6 Jun 2013 19:08:52 +0200 Subject: [PATCH 041/299] send new tx first seen in block after double spend notifications --- .../java/com/bitsofproof/supernode/core/TxHandler.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index f4bbd918..1d95fd77 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -341,6 +341,8 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo // this is already running in cache and transaction context synchronized ( unconfirmed ) { + List firstSeenInBlock = new ArrayList (); + for ( Blk blk : removedBlocks ) { boolean coinbase = true; @@ -376,7 +378,7 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo } else { - notifyListener (tx, false); + firstSeenInBlock.add (tx); } } } @@ -411,6 +413,10 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo notifyListener (tx, true); } } + for ( Tx tx : firstSeenInBlock ) + { + notifyListener (tx, false); + } } } catch ( Exception e ) From 77fe10a99af3ad466680722bbfb9eec5b7787f9c Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Thu, 6 Jun 2013 20:02:00 +0200 Subject: [PATCH 042/299] process double spend notifications --- .../supernode/api/InMemoryAccountManager.java | 45 +++++++------------ .../supernode/api/InMemoryUTXO.java | 6 ++- 2 files changed, 20 insertions(+), 31 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java index 47fe0902..0b052f07 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java @@ -39,7 +39,7 @@ import com.bitsofproof.supernode.common.ScriptFormat.Token; import com.bitsofproof.supernode.common.ValidationException; -class InMemoryAccountManager implements TransactionListener, TrunkListener, AccountManager +class InMemoryAccountManager implements TransactionListener, AccountManager { private static final Logger log = LoggerFactory.getLogger (InMemoryAccountManager.class); @@ -133,7 +133,6 @@ public void process (Transaction t) public void registerFilter () throws BCSAPIException { - api.registerTrunkListener (this); api.registerFilteredListener (filter, this); } @@ -200,6 +199,10 @@ public boolean updateWithTransaction (Transaction t) boolean modified = false; if ( !processedTransaction.containsKey (t.getHash ()) ) { + if ( t.isDoubleSpend () ) + { + return false; + } processedTransaction.put (t.getHash (), t); TransactionOutput spend = null; for ( TransactionInput i : t.getInputs () ) @@ -260,6 +263,17 @@ public boolean updateWithTransaction (Transaction t) ++ix; } } + else if ( t.isDoubleSpend () ) + { + for ( long ix = 0; ix < t.getOutputs ().size (); ++ix ) + { + modified |= confirmed.remove (t.getHash (), ix); + modified |= change.remove (t.getHash (), ix); + modified |= receiving.remove (t.getHash (), ix); + modified |= sending.remove (t.getHash (), ix); + } + processedTransaction.remove (t.getHash ()); + } if ( modified ) { wallet.addTransaction (t); @@ -270,33 +284,6 @@ public boolean updateWithTransaction (Transaction t) } } - @Override - public void trunkUpdate (List removed, List added) - { - List newTransactions = new ArrayList (); - synchronized ( confirmed ) - { - if ( added != null ) - { - for ( Block b : added ) - { - for ( Transaction t : b.getTransactions () ) - { - if ( updateWithTransaction (t) ) - { - t.setBlockHash (b.getHash ()); - newTransactions.add (t); - } - } - } - } - } - for ( Transaction t : newTransactions ) - { - notifyListener (t); - } - } - @Override public void process (Transaction t) { diff --git a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java index 95d77a75..856b6d41 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java @@ -52,17 +52,19 @@ public TransactionOutput get (String tx, long ix) return null; } - public void remove (String tx, long ix) + public boolean remove (String tx, long ix) { HashMap outs = utxo.get (tx); if ( outs != null ) { - outs.remove (ix); + boolean removed = outs.remove (ix) != null; if ( outs.size () == 0 ) { utxo.remove (tx); } + return removed; } + return false; } public long getTotal () From 9d2fc0d9ed9e535e1622b26fde9fc1f17e702080 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Thu, 6 Jun 2013 20:16:22 +0200 Subject: [PATCH 043/299] broadcast coinbase since it could hit a wallet (of a miner) --- .../main/java/com/bitsofproof/supernode/core/TxHandler.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index 1d95fd77..ded27dc7 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -359,14 +359,8 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo } for ( Blk blk : addedBlocks ) { - boolean coinbase = true; for ( Tx tx : blk.getTransactions () ) { - if ( coinbase ) - { - coinbase = false; - continue; - } if ( unconfirmed.containsKey (tx.getHash ()) ) { unconfirmed.remove (tx.getHash ()); From 36733dd2e782c6defac642f738e60644684dde15 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Thu, 6 Jun 2013 20:32:08 +0200 Subject: [PATCH 044/299] there are transaction validations for coinbases --- .../bitsofproof/supernode/test/APITest.java | 31 ++----------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java index ec73fd6a..fb8b63c2 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java @@ -22,7 +22,6 @@ import java.security.Security; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.Semaphore; @@ -108,7 +107,6 @@ public static class ValidationMonitor implements TrunkListener, TransactionListe private final Semaphore blockValidated = new Semaphore (0); private final Semaphore transactionValidated = new Semaphore (0); private String last; - private final List unconfirmed = new ArrayList (); @Override public void trunkUpdate (List removed, List added) @@ -119,35 +117,9 @@ public void trunkUpdate (List removed, List added) { b.computeHash (); last = b.getHash (); - Iterator i = unconfirmed.iterator (); - while ( i.hasNext () ) - { - Transaction t = i.next (); - for ( Transaction tb : b.getTransactions () ) - { - if ( tb.getHash ().equals (t.getHash ()) ) - { - i.remove (); - } - } - } - blockValidated.release (); } } - if ( removed != null ) - { - for ( Block b : added ) - { - b.computeHash (); - unconfirmed.addAll (b.getTransactions ()); - } - } - } - - public List getUnconfirmed () - { - return unconfirmed; } public String getLast () @@ -182,7 +154,6 @@ public void expectTransactionValidations (int n) @Override public void process (Transaction t) { - unconfirmed.add (t); transactionValidated.release (); } } @@ -227,6 +198,7 @@ public void test () throws BCSAPIException, ValidationException validationMonitor.expectBlockValidations (1); assertTrue (validationMonitor.getLast ().equals (block.getHash ())); + validationMonitor.expectTransactionValidations (1); aliceMonitor.expectUpdates (1); // send 10 blocks @@ -243,6 +215,7 @@ public void test () throws BCSAPIException, ValidationException validationMonitor.expectBlockValidations (10); assertTrue (validationMonitor.getLast ().equals (hash)); aliceMonitor.expectUpdates (10); + validationMonitor.expectTransactionValidations (10); // spend some long aliceStartingBalance = alice.getBalance (); From bbd7f652ee78199afb39e530606284835d17ea31 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Wed, 12 Jun 2013 08:04:48 +0200 Subject: [PATCH 045/299] more about the API --- README | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README b/README index 28e2c564..ccdae9c8 100644 --- a/README +++ b/README @@ -18,9 +18,17 @@ STATUS The implementation passed lots of testing and runs since January 2013 in parallel with the network. It is community reviewed, see: https://bitcointalk.org/index.php?topic=122013.0 +BUILD THE SERVER +See build instructions at: http://bitsofproof.com:8082/display/BPD/Build+and+run+the+Server + +CLIENTS +Applications can use the Bitcoin network by embedding a very low footprint client library. The Bitcoin private keys, that are able to command Bitcoins, are generated and stored at the client side and never transmitted to the server. See the primary client API documentation here: http://bitsofproof.com:8082/display/BPD/Build+an+Application+using+BOP+Servers + +Further layers of API are available to support Mobile and Web applications. See more details here: http://bitsofproof.com:8082/display/BPD/Bits+of+Proof+Documentation+Home + SUPPORT AND HOSTING -Bits of Proof offers commercial support and hosts BOP Enterprise Bitcoin Server instances that run this open source code. BOP servers offer the fastest time to market for your Bitcoin application development. Visit http://bitsofproof.com for more details. +Bits of Proof offers commercial support and hosts BOP Enterprise Bitcoin Server instances that run this open source code. BOP servers offer the fastest time to market for your Bitcoin application development. Visit http://bitsofproof.com for more details. LICENCE From 6f3b1e1ee3984edcd3ab14c1623ef4c31e0b695f Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Wed, 12 Jun 2013 08:29:09 +0200 Subject: [PATCH 046/299] simple description point to documentation site --- README | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README b/README index ccdae9c8..7693941e 100644 --- a/README +++ b/README @@ -10,10 +10,6 @@ The validation engine passes most important unit tests of the Satoshi implementa Clients or extensions communicate with the server process through an asynchrounous message bus. The bus is provided by a separate broker process. The messages are in protobuf format hence interoperable with modules in other languages e.g. python. -A Java cient API library provides convenient access to the message bus, deterministic and hierarchical key generation (BIP32) and password encrypted private key storage (BIP38) for multi wallet or multiple client environments. - -Adapters are available for server trusting mobile clients (BCCAPI) and REST API for web site integration. See example Android application, command line client and primitive block explorer in bop-bccapi, bop-api-example and bop-explorer projects of Bits of Proof. - STATUS The implementation passed lots of testing and runs since January 2013 in parallel with the network. It is community reviewed, see: https://bitcointalk.org/index.php?topic=122013.0 From 9c00a4f0ac649126a8cd91295522a8780874cbbb Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Sun, 16 Jun 2013 12:44:00 +0200 Subject: [PATCH 047/299] wrong mode to scan --- .../java/com/bitsofproof/supernode/core/CachedBlockStore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index de593830..5bd29f4f 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -461,7 +461,7 @@ public void filterTransactions (Set matchSet, ExtendedKey ek, int lo Blk b = retrieveBlock (cb); for ( Tx t : b.getTransactions () ) { - if ( t.matches (matchSet, UpdateMode.keys) ) + if ( t.matches (matchSet, UpdateMode.all) ) { processor.process (t); From 0a58a5a1eb9806620ae8b97b1f1bd146711c2148 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Sun, 16 Jun 2013 16:29:40 +0200 Subject: [PATCH 048/299] redesigned wallet --- .../supernode/api/AccountManager.java | 11 +- .../supernode/api/BCSAPIMessage.java | 2377 ++++++----------- .../bitsofproof/supernode/api/FileWallet.java | 183 ++ .../supernode/api/InMemoryAccountManager.java | 169 +- .../supernode/api/InMemoryUTXO.java | 6 +- .../supernode/api/SerializedWallet.java | 483 ---- .../supernode/api/SimpleWalletFactory.java | 22 - .../com/bitsofproof/supernode/api/Wallet.java | 21 +- api/src/main/protobuf/BCSAPIMessage.proto | 22 +- .../supernode/api/WalletFormatTest.java | 46 - .../bitsofproof/supernode/test/APITest.java | 16 +- 11 files changed, 1208 insertions(+), 2148 deletions(-) create mode 100644 api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java delete mode 100644 api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java delete mode 100644 api/src/main/java/com/bitsofproof/supernode/api/SimpleWalletFactory.java diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java index b558e47d..d0e99dac 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java @@ -16,22 +16,29 @@ package com.bitsofproof.supernode.api; import java.util.Collection; +import java.util.List; import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ValidationException; public interface AccountManager { + public void setApi (BCSAPI api); + public String getName (); + public long getCreated (); + + public boolean updateWithTransaction (Transaction t); + + public List getTransactions (); + public Collection getAddresses (); public ExtendedKey getMasterKey (); public Key getKeyForAddress (byte[] address); - public Key getKey (int ix) throws ValidationException; - public Key getNextKey () throws ValidationException; public int getNextSequence (); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java index f134118d..4293a88b 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java @@ -8500,34 +8500,22 @@ public interface WalletOrBuilder boolean hasBcsapiversion(); int getBcsapiversion(); - // repeated .com.bitsofproof.supernode.api.Wallet.Key keys = 2; - java.util.List - getKeysList(); - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key getKeys(int index); - int getKeysCount(); - java.util.List - getKeysOrBuilderList(); - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.KeyOrBuilder getKeysOrBuilder( - int index); + // required bytes encryptedSeed = 2; + boolean hasEncryptedSeed(); + com.google.protobuf.ByteString getEncryptedSeed(); - // repeated .com.bitsofproof.supernode.api.Wallet.PublicAddress publicAddresses = 3; - java.util.List - getPublicAddressesList(); - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress getPublicAddresses(int index); - int getPublicAddressesCount(); - java.util.List - getPublicAddressesOrBuilderList(); - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddressOrBuilder getPublicAddressesOrBuilder( - int index); + // required bytes signature = 3; + boolean hasSignature(); + com.google.protobuf.ByteString getSignature(); - // repeated .com.bitsofproof.supernode.api.Transaction transactions = 4; - java.util.List - getTransactionsList(); - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction getTransactions(int index); - int getTransactionsCount(); - java.util.List - getTransactionsOrBuilderList(); - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder getTransactionsOrBuilder( + // repeated .com.bitsofproof.supernode.api.Wallet.Account accounts = 4; + java.util.List + getAccountsList(); + com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account getAccounts(int index); + int getAccountsCount(); + java.util.List + getAccountsOrBuilderList(); + com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.AccountOrBuilder getAccountsOrBuilder( int index); } public static final class Wallet extends @@ -8558,62 +8546,72 @@ public Wallet getDefaultInstanceForType() { return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_fieldAccessorTable; } - public interface KeyOrBuilder + public interface AccountOrBuilder extends com.google.protobuf.MessageOrBuilder { - // required string key = 1; - boolean hasKey(); - String getKey(); - - // required uint32 nextSequence = 2; + // required string publicKey = 1; + boolean hasPublicKey(); + String getPublicKey(); + + // repeated .com.bitsofproof.supernode.api.Transaction transactions = 2; + java.util.List + getTransactionsList(); + com.bitsofproof.supernode.api.BCSAPIMessage.Transaction getTransactions(int index); + int getTransactionsCount(); + java.util.List + getTransactionsOrBuilderList(); + com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder getTransactionsOrBuilder( + int index); + + // required uint32 nextSequence = 3; boolean hasNextSequence(); int getNextSequence(); - // optional string name = 3; - boolean hasName(); - String getName(); - - // optional uint64 created = 4; + // required uint64 created = 4; boolean hasCreated(); long getCreated(); + + // required string name = 5; + boolean hasName(); + String getName(); } - public static final class Key extends + public static final class Account extends com.google.protobuf.GeneratedMessage - implements KeyOrBuilder { - // Use Key.newBuilder() to construct. - private Key(Builder builder) { + implements AccountOrBuilder { + // Use Account.newBuilder() to construct. + private Account(Builder builder) { super(builder); } - private Key(boolean noInit) {} + private Account(boolean noInit) {} - private static final Key defaultInstance; - public static Key getDefaultInstance() { + private static final Account defaultInstance; + public static Account getDefaultInstance() { return defaultInstance; } - public Key getDefaultInstanceForType() { + public Account getDefaultInstanceForType() { return defaultInstance; } public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_Key_descriptor; + return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_Account_descriptor; } protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_Key_fieldAccessorTable; + return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_Account_fieldAccessorTable; } private int bitField0_; - // required string key = 1; - public static final int KEY_FIELD_NUMBER = 1; - private java.lang.Object key_; - public boolean hasKey() { + // required string publicKey = 1; + public static final int PUBLICKEY_FIELD_NUMBER = 1; + private java.lang.Object publicKey_; + public boolean hasPublicKey() { return ((bitField0_ & 0x00000001) == 0x00000001); } - public String getKey() { - java.lang.Object ref = key_; + public String getPublicKey() { + java.lang.Object ref = publicKey_; if (ref instanceof String) { return (String) ref; } else { @@ -8621,25 +8619,46 @@ public String getKey() { (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); if (com.google.protobuf.Internal.isValidUtf8(bs)) { - key_ = s; + publicKey_ = s; } return s; } } - private com.google.protobuf.ByteString getKeyBytes() { - java.lang.Object ref = key_; + private com.google.protobuf.ByteString getPublicKeyBytes() { + java.lang.Object ref = publicKey_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref); - key_ = b; + publicKey_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } - // required uint32 nextSequence = 2; - public static final int NEXTSEQUENCE_FIELD_NUMBER = 2; + // repeated .com.bitsofproof.supernode.api.Transaction transactions = 2; + public static final int TRANSACTIONS_FIELD_NUMBER = 2; + private java.util.List transactions_; + public java.util.List getTransactionsList() { + return transactions_; + } + public java.util.List + getTransactionsOrBuilderList() { + return transactions_; + } + public int getTransactionsCount() { + return transactions_.size(); + } + public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction getTransactions(int index) { + return transactions_.get(index); + } + public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder getTransactionsOrBuilder( + int index) { + return transactions_.get(index); + } + + // required uint32 nextSequence = 3; + public static final int NEXTSEQUENCE_FIELD_NUMBER = 3; private int nextSequence_; public boolean hasNextSequence() { return ((bitField0_ & 0x00000002) == 0x00000002); @@ -8648,11 +8667,21 @@ public int getNextSequence() { return nextSequence_; } - // optional string name = 3; - public static final int NAME_FIELD_NUMBER = 3; + // required uint64 created = 4; + public static final int CREATED_FIELD_NUMBER = 4; + private long created_; + public boolean hasCreated() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + public long getCreated() { + return created_; + } + + // required string name = 5; + public static final int NAME_FIELD_NUMBER = 5; private java.lang.Object name_; public boolean hasName() { - return ((bitField0_ & 0x00000004) == 0x00000004); + return ((bitField0_ & 0x00000008) == 0x00000008); } public String getName() { java.lang.Object ref = name_; @@ -8680,28 +8709,19 @@ private com.google.protobuf.ByteString getNameBytes() { } } - // optional uint64 created = 4; - public static final int CREATED_FIELD_NUMBER = 4; - private long created_; - public boolean hasCreated() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public long getCreated() { - return created_; - } - private void initFields() { - key_ = ""; + publicKey_ = ""; + transactions_ = java.util.Collections.emptyList(); nextSequence_ = 0; - name_ = ""; created_ = 0L; + name_ = ""; } private byte memoizedIsInitialized = -1; public final boolean isInitialized() { byte isInitialized = memoizedIsInitialized; if (isInitialized != -1) return isInitialized == 1; - if (!hasKey()) { + if (!hasPublicKey()) { memoizedIsInitialized = 0; return false; } @@ -8709,6 +8729,20 @@ public final boolean isInitialized() { memoizedIsInitialized = 0; return false; } + if (!hasCreated()) { + memoizedIsInitialized = 0; + return false; + } + if (!hasName()) { + memoizedIsInitialized = 0; + return false; + } + for (int i = 0; i < getTransactionsCount(); i++) { + if (!getTransactions(i).isInitialized()) { + memoizedIsInitialized = 0; + return false; + } + } memoizedIsInitialized = 1; return true; } @@ -8717,16 +8751,19 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { getSerializedSize(); if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getKeyBytes()); + output.writeBytes(1, getPublicKeyBytes()); + } + for (int i = 0; i < transactions_.size(); i++) { + output.writeMessage(2, transactions_.get(i)); } if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt32(2, nextSequence_); + output.writeUInt32(3, nextSequence_); } if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, getNameBytes()); + output.writeUInt64(4, created_); } if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeUInt64(4, created_); + output.writeBytes(5, getNameBytes()); } getUnknownFields().writeTo(output); } @@ -8739,19 +8776,23 @@ public int getSerializedSize() { size = 0; if (((bitField0_ & 0x00000001) == 0x00000001)) { size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getKeyBytes()); + .computeBytesSize(1, getPublicKeyBytes()); + } + for (int i = 0; i < transactions_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, transactions_.get(i)); } if (((bitField0_ & 0x00000002) == 0x00000002)) { size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(2, nextSequence_); + .computeUInt32Size(3, nextSequence_); } if (((bitField0_ & 0x00000004) == 0x00000004)) { size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, getNameBytes()); + .computeUInt64Size(4, created_); } if (((bitField0_ & 0x00000008) == 0x00000008)) { size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(4, created_); + .computeBytesSize(5, getNameBytes()); } size += getUnknownFields().getSerializedSize(); memoizedSerializedSize = size; @@ -8765,41 +8806,41 @@ protected java.lang.Object writeReplace() return super.writeReplace(); } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key parseFrom( + public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account parseFrom( com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return newBuilder().mergeFrom(data).buildParsed(); } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key parseFrom( + public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return newBuilder().mergeFrom(data, extensionRegistry) .buildParsed(); } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key parseFrom(byte[] data) + public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return newBuilder().mergeFrom(data).buildParsed(); } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key parseFrom( + public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account parseFrom( byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return newBuilder().mergeFrom(data, extensionRegistry) .buildParsed(); } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key parseFrom(java.io.InputStream input) + public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account parseFrom(java.io.InputStream input) throws java.io.IOException { return newBuilder().mergeFrom(input).buildParsed(); } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key parseFrom( + public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account parseFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { return newBuilder().mergeFrom(input, extensionRegistry) .buildParsed(); } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key parseDelimitedFrom(java.io.InputStream input) + public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException { Builder builder = newBuilder(); if (builder.mergeDelimitedFrom(input)) { @@ -8808,7 +8849,7 @@ public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key parseDelimi return null; } } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key parseDelimitedFrom( + public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account parseDelimitedFrom( java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { @@ -8819,12 +8860,12 @@ public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key parseDelimi return null; } } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key parseFrom( + public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account parseFrom( com.google.protobuf.CodedInputStream input) throws java.io.IOException { return newBuilder().mergeFrom(input).buildParsed(); } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key parseFrom( + public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account parseFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException { @@ -8834,7 +8875,7 @@ public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key parseFrom( public static Builder newBuilder() { return Builder.create(); } public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key prototype) { + public static Builder newBuilder(com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account prototype) { return newBuilder().mergeFrom(prototype); } public Builder toBuilder() { return newBuilder(this); } @@ -8847,18 +8888,18 @@ protected Builder newBuilderForType( } public static final class Builder extends com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.KeyOrBuilder { + implements com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.AccountOrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_Key_descriptor; + return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_Account_descriptor; } protected com.google.protobuf.GeneratedMessage.FieldAccessorTable internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_Key_fieldAccessorTable; + return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_Account_fieldAccessorTable; } - // Construct using com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key.newBuilder() + // Construct using com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.newBuilder() private Builder() { maybeForceBuilderInitialization(); } @@ -8869,6 +8910,7 @@ private Builder(BuilderParent parent) { } private void maybeForceBuilderInitialization() { if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getTransactionsFieldBuilder(); } } private static Builder create() { @@ -8877,14 +8919,20 @@ private static Builder create() { public Builder clear() { super.clear(); - key_ = ""; + publicKey_ = ""; bitField0_ = (bitField0_ & ~0x00000001); + if (transactionsBuilder_ == null) { + transactions_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000002); + } else { + transactionsBuilder_.clear(); + } nextSequence_ = 0; - bitField0_ = (bitField0_ & ~0x00000002); - name_ = ""; bitField0_ = (bitField0_ & ~0x00000004); created_ = 0L; bitField0_ = (bitField0_ & ~0x00000008); + name_ = ""; + bitField0_ = (bitField0_ & ~0x00000010); return this; } @@ -8894,24 +8942,24 @@ public Builder clone() { public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key.getDescriptor(); + return com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.getDescriptor(); } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key getDefaultInstanceForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key.getDefaultInstance(); + public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account getDefaultInstanceForType() { + return com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.getDefaultInstance(); } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key build() { - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key result = buildPartial(); + public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account build() { + com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } return result; } - private com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key buildParsed() + private com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account buildParsed() throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key result = buildPartial(); + com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException( result).asInvalidProtocolBufferException(); @@ -8919,60 +8967,95 @@ private com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key buildParsed() return result; } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key buildPartial() { - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key result = new com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key(this); + public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account buildPartial() { + com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account result = new com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account(this); int from_bitField0_ = bitField0_; int to_bitField0_ = 0; if (((from_bitField0_ & 0x00000001) == 0x00000001)) { to_bitField0_ |= 0x00000001; } - result.key_ = key_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + result.publicKey_ = publicKey_; + if (transactionsBuilder_ == null) { + if (((bitField0_ & 0x00000002) == 0x00000002)) { + transactions_ = java.util.Collections.unmodifiableList(transactions_); + bitField0_ = (bitField0_ & ~0x00000002); + } + result.transactions_ = transactions_; + } else { + result.transactions_ = transactionsBuilder_.build(); + } + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { to_bitField0_ |= 0x00000002; } result.nextSequence_ = nextSequence_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { to_bitField0_ |= 0x00000004; } - result.name_ = name_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + result.created_ = created_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { to_bitField0_ |= 0x00000008; } - result.created_ = created_; + result.name_ = name_; result.bitField0_ = to_bitField0_; onBuilt(); return result; } public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key) { - return mergeFrom((com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key)other); + if (other instanceof com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account) { + return mergeFrom((com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account)other); } else { super.mergeFrom(other); return this; } } - public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key other) { - if (other == com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key.getDefaultInstance()) return this; - if (other.hasKey()) { - setKey(other.getKey()); + public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account other) { + if (other == com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.getDefaultInstance()) return this; + if (other.hasPublicKey()) { + setPublicKey(other.getPublicKey()); + } + if (transactionsBuilder_ == null) { + if (!other.transactions_.isEmpty()) { + if (transactions_.isEmpty()) { + transactions_ = other.transactions_; + bitField0_ = (bitField0_ & ~0x00000002); + } else { + ensureTransactionsIsMutable(); + transactions_.addAll(other.transactions_); + } + onChanged(); + } + } else { + if (!other.transactions_.isEmpty()) { + if (transactionsBuilder_.isEmpty()) { + transactionsBuilder_.dispose(); + transactionsBuilder_ = null; + transactions_ = other.transactions_; + bitField0_ = (bitField0_ & ~0x00000002); + transactionsBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getTransactionsFieldBuilder() : null; + } else { + transactionsBuilder_.addAllMessages(other.transactions_); + } + } } if (other.hasNextSequence()) { setNextSequence(other.getNextSequence()); } - if (other.hasName()) { - setName(other.getName()); - } if (other.hasCreated()) { setCreated(other.getCreated()); } + if (other.hasName()) { + setName(other.getName()); + } this.mergeUnknownFields(other.getUnknownFields()); return this; } public final boolean isInitialized() { - if (!hasKey()) { + if (!hasPublicKey()) { return false; } @@ -8980,6 +9063,20 @@ public final boolean isInitialized() { return false; } + if (!hasCreated()) { + + return false; + } + if (!hasName()) { + + return false; + } + for (int i = 0; i < getTransactionsCount(); i++) { + if (!getTransactions(i).isInitialized()) { + + return false; + } + } return true; } @@ -9008,17 +9105,18 @@ public Builder mergeFrom( } case 10: { bitField0_ |= 0x00000001; - key_ = input.readBytes(); + publicKey_ = input.readBytes(); break; } - case 16: { - bitField0_ |= 0x00000002; - nextSequence_ = input.readUInt32(); + case 18: { + com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder subBuilder = com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.newBuilder(); + input.readMessage(subBuilder, extensionRegistry); + addTransactions(subBuilder.buildPartial()); break; } - case 26: { + case 24: { bitField0_ |= 0x00000004; - name_ = input.readBytes(); + nextSequence_ = input.readUInt32(); break; } case 32: { @@ -9026,73 +9124,285 @@ public Builder mergeFrom( created_ = input.readUInt64(); break; } + case 42: { + bitField0_ |= 0x00000010; + name_ = input.readBytes(); + break; + } } } } private int bitField0_; - // required string key = 1; - private java.lang.Object key_ = ""; - public boolean hasKey() { + // required string publicKey = 1; + private java.lang.Object publicKey_ = ""; + public boolean hasPublicKey() { return ((bitField0_ & 0x00000001) == 0x00000001); } - public String getKey() { - java.lang.Object ref = key_; + public String getPublicKey() { + java.lang.Object ref = publicKey_; if (!(ref instanceof String)) { String s = ((com.google.protobuf.ByteString) ref).toStringUtf8(); - key_ = s; + publicKey_ = s; return s; } else { return (String) ref; } } - public Builder setKey(String value) { + public Builder setPublicKey(String value) { if (value == null) { throw new NullPointerException(); } bitField0_ |= 0x00000001; - key_ = value; + publicKey_ = value; onChanged(); return this; } - public Builder clearKey() { + public Builder clearPublicKey() { bitField0_ = (bitField0_ & ~0x00000001); - key_ = getDefaultInstance().getKey(); + publicKey_ = getDefaultInstance().getPublicKey(); onChanged(); return this; } - void setKey(com.google.protobuf.ByteString value) { + void setPublicKey(com.google.protobuf.ByteString value) { bitField0_ |= 0x00000001; - key_ = value; + publicKey_ = value; onChanged(); } - // required uint32 nextSequence = 2; + // repeated .com.bitsofproof.supernode.api.Transaction transactions = 2; + private java.util.List transactions_ = + java.util.Collections.emptyList(); + private void ensureTransactionsIsMutable() { + if (!((bitField0_ & 0x00000002) == 0x00000002)) { + transactions_ = new java.util.ArrayList(transactions_); + bitField0_ |= 0x00000002; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + com.bitsofproof.supernode.api.BCSAPIMessage.Transaction, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder> transactionsBuilder_; + + public java.util.List getTransactionsList() { + if (transactionsBuilder_ == null) { + return java.util.Collections.unmodifiableList(transactions_); + } else { + return transactionsBuilder_.getMessageList(); + } + } + public int getTransactionsCount() { + if (transactionsBuilder_ == null) { + return transactions_.size(); + } else { + return transactionsBuilder_.getCount(); + } + } + public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction getTransactions(int index) { + if (transactionsBuilder_ == null) { + return transactions_.get(index); + } else { + return transactionsBuilder_.getMessage(index); + } + } + public Builder setTransactions( + int index, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction value) { + if (transactionsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureTransactionsIsMutable(); + transactions_.set(index, value); + onChanged(); + } else { + transactionsBuilder_.setMessage(index, value); + } + return this; + } + public Builder setTransactions( + int index, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder builderForValue) { + if (transactionsBuilder_ == null) { + ensureTransactionsIsMutable(); + transactions_.set(index, builderForValue.build()); + onChanged(); + } else { + transactionsBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + public Builder addTransactions(com.bitsofproof.supernode.api.BCSAPIMessage.Transaction value) { + if (transactionsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureTransactionsIsMutable(); + transactions_.add(value); + onChanged(); + } else { + transactionsBuilder_.addMessage(value); + } + return this; + } + public Builder addTransactions( + int index, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction value) { + if (transactionsBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureTransactionsIsMutable(); + transactions_.add(index, value); + onChanged(); + } else { + transactionsBuilder_.addMessage(index, value); + } + return this; + } + public Builder addTransactions( + com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder builderForValue) { + if (transactionsBuilder_ == null) { + ensureTransactionsIsMutable(); + transactions_.add(builderForValue.build()); + onChanged(); + } else { + transactionsBuilder_.addMessage(builderForValue.build()); + } + return this; + } + public Builder addTransactions( + int index, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder builderForValue) { + if (transactionsBuilder_ == null) { + ensureTransactionsIsMutable(); + transactions_.add(index, builderForValue.build()); + onChanged(); + } else { + transactionsBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + public Builder addAllTransactions( + java.lang.Iterable values) { + if (transactionsBuilder_ == null) { + ensureTransactionsIsMutable(); + super.addAll(values, transactions_); + onChanged(); + } else { + transactionsBuilder_.addAllMessages(values); + } + return this; + } + public Builder clearTransactions() { + if (transactionsBuilder_ == null) { + transactions_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000002); + onChanged(); + } else { + transactionsBuilder_.clear(); + } + return this; + } + public Builder removeTransactions(int index) { + if (transactionsBuilder_ == null) { + ensureTransactionsIsMutable(); + transactions_.remove(index); + onChanged(); + } else { + transactionsBuilder_.remove(index); + } + return this; + } + public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder getTransactionsBuilder( + int index) { + return getTransactionsFieldBuilder().getBuilder(index); + } + public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder getTransactionsOrBuilder( + int index) { + if (transactionsBuilder_ == null) { + return transactions_.get(index); } else { + return transactionsBuilder_.getMessageOrBuilder(index); + } + } + public java.util.List + getTransactionsOrBuilderList() { + if (transactionsBuilder_ != null) { + return transactionsBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(transactions_); + } + } + public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder addTransactionsBuilder() { + return getTransactionsFieldBuilder().addBuilder( + com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.getDefaultInstance()); + } + public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder addTransactionsBuilder( + int index) { + return getTransactionsFieldBuilder().addBuilder( + index, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.getDefaultInstance()); + } + public java.util.List + getTransactionsBuilderList() { + return getTransactionsFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + com.bitsofproof.supernode.api.BCSAPIMessage.Transaction, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder> + getTransactionsFieldBuilder() { + if (transactionsBuilder_ == null) { + transactionsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + com.bitsofproof.supernode.api.BCSAPIMessage.Transaction, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder>( + transactions_, + ((bitField0_ & 0x00000002) == 0x00000002), + getParentForChildren(), + isClean()); + transactions_ = null; + } + return transactionsBuilder_; + } + + // required uint32 nextSequence = 3; private int nextSequence_ ; public boolean hasNextSequence() { - return ((bitField0_ & 0x00000002) == 0x00000002); + return ((bitField0_ & 0x00000004) == 0x00000004); } public int getNextSequence() { return nextSequence_; } public Builder setNextSequence(int value) { - bitField0_ |= 0x00000002; + bitField0_ |= 0x00000004; nextSequence_ = value; onChanged(); return this; } public Builder clearNextSequence() { - bitField0_ = (bitField0_ & ~0x00000002); + bitField0_ = (bitField0_ & ~0x00000004); nextSequence_ = 0; onChanged(); return this; } - // optional string name = 3; + // required uint64 created = 4; + private long created_ ; + public boolean hasCreated() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + public long getCreated() { + return created_; + } + public Builder setCreated(long value) { + bitField0_ |= 0x00000008; + created_ = value; + onChanged(); + return this; + } + public Builder clearCreated() { + bitField0_ = (bitField0_ & ~0x00000008); + created_ = 0L; + onChanged(); + return this; + } + + // required string name = 5; private java.lang.Object name_ = ""; public boolean hasName() { - return ((bitField0_ & 0x00000004) == 0x00000004); + return ((bitField0_ & 0x00000010) == 0x00000010); } public String getName() { java.lang.Object ref = name_; @@ -9108,538 +9418,32 @@ public Builder setName(String value) { if (value == null) { throw new NullPointerException(); } - bitField0_ |= 0x00000004; + bitField0_ |= 0x00000010; name_ = value; onChanged(); return this; } public Builder clearName() { - bitField0_ = (bitField0_ & ~0x00000004); + bitField0_ = (bitField0_ & ~0x00000010); name_ = getDefaultInstance().getName(); onChanged(); return this; } void setName(com.google.protobuf.ByteString value) { - bitField0_ |= 0x00000004; + bitField0_ |= 0x00000010; name_ = value; onChanged(); } - // optional uint64 created = 4; - private long created_ ; - public boolean hasCreated() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public long getCreated() { - return created_; - } - public Builder setCreated(long value) { - bitField0_ |= 0x00000008; - created_ = value; - onChanged(); - return this; - } - public Builder clearCreated() { - bitField0_ = (bitField0_ & ~0x00000008); - created_ = 0L; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.Wallet.Key) + // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.Wallet.Account) } static { - defaultInstance = new Key(true); + defaultInstance = new Account(true); defaultInstance.initFields(); } - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.api.Wallet.Key) - } - - public interface PublicAddressOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required string address = 1; - boolean hasAddress(); - String getAddress(); - - // required string name = 2; - boolean hasName(); - String getName(); - } - public static final class PublicAddress extends - com.google.protobuf.GeneratedMessage - implements PublicAddressOrBuilder { - // Use PublicAddress.newBuilder() to construct. - private PublicAddress(Builder builder) { - super(builder); - } - private PublicAddress(boolean noInit) {} - - private static final PublicAddress defaultInstance; - public static PublicAddress getDefaultInstance() { - return defaultInstance; - } - - public PublicAddress getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_PublicAddress_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_PublicAddress_fieldAccessorTable; - } - - private int bitField0_; - // required string address = 1; - public static final int ADDRESS_FIELD_NUMBER = 1; - private java.lang.Object address_; - public boolean hasAddress() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public String getAddress() { - java.lang.Object ref = address_; - if (ref instanceof String) { - return (String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - String s = bs.toStringUtf8(); - if (com.google.protobuf.Internal.isValidUtf8(bs)) { - address_ = s; - } - return s; - } - } - private com.google.protobuf.ByteString getAddressBytes() { - java.lang.Object ref = address_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8((String) ref); - address_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // required string name = 2; - public static final int NAME_FIELD_NUMBER = 2; - private java.lang.Object name_; - public boolean hasName() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public String getName() { - java.lang.Object ref = name_; - if (ref instanceof String) { - return (String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - String s = bs.toStringUtf8(); - if (com.google.protobuf.Internal.isValidUtf8(bs)) { - name_ = s; - } - return s; - } - } - private com.google.protobuf.ByteString getNameBytes() { - java.lang.Object ref = name_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8((String) ref); - name_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - private void initFields() { - address_ = ""; - name_ = ""; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasAddress()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasName()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getAddressBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getNameBytes()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getAddressBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getNameBytes()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddressOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_PublicAddress_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_PublicAddress_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - address_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - name_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress.getDescriptor(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress getDefaultInstanceForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress.getDefaultInstance(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress build() { - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress buildPartial() { - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress result = new com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.address_ = address_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.name_ = name_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress) { - return mergeFrom((com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress other) { - if (other == com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress.getDefaultInstance()) return this; - if (other.hasAddress()) { - setAddress(other.getAddress()); - } - if (other.hasName()) { - setName(other.getName()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasAddress()) { - - return false; - } - if (!hasName()) { - - return false; - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - address_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - name_ = input.readBytes(); - break; - } - } - } - } - - private int bitField0_; - - // required string address = 1; - private java.lang.Object address_ = ""; - public boolean hasAddress() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public String getAddress() { - java.lang.Object ref = address_; - if (!(ref instanceof String)) { - String s = ((com.google.protobuf.ByteString) ref).toStringUtf8(); - address_ = s; - return s; - } else { - return (String) ref; - } - } - public Builder setAddress(String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - address_ = value; - onChanged(); - return this; - } - public Builder clearAddress() { - bitField0_ = (bitField0_ & ~0x00000001); - address_ = getDefaultInstance().getAddress(); - onChanged(); - return this; - } - void setAddress(com.google.protobuf.ByteString value) { - bitField0_ |= 0x00000001; - address_ = value; - onChanged(); - } - - // required string name = 2; - private java.lang.Object name_ = ""; - public boolean hasName() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public String getName() { - java.lang.Object ref = name_; - if (!(ref instanceof String)) { - String s = ((com.google.protobuf.ByteString) ref).toStringUtf8(); - name_ = s; - return s; - } else { - return (String) ref; - } - } - public Builder setName(String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - name_ = value; - onChanged(); - return this; - } - public Builder clearName() { - bitField0_ = (bitField0_ & ~0x00000002); - name_ = getDefaultInstance().getName(); - onChanged(); - return this; - } - void setName(com.google.protobuf.ByteString value) { - bitField0_ |= 0x00000002; - name_ = value; - onChanged(); - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.Wallet.PublicAddress) - } - - static { - defaultInstance = new PublicAddress(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.api.Wallet.PublicAddress) + // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.api.Wallet.Account) } private int bitField0_; @@ -9653,74 +9457,52 @@ public int getBcsapiversion() { return bcsapiversion_; } - // repeated .com.bitsofproof.supernode.api.Wallet.Key keys = 2; - public static final int KEYS_FIELD_NUMBER = 2; - private java.util.List keys_; - public java.util.List getKeysList() { - return keys_; - } - public java.util.List - getKeysOrBuilderList() { - return keys_; - } - public int getKeysCount() { - return keys_.size(); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key getKeys(int index) { - return keys_.get(index); + // required bytes encryptedSeed = 2; + public static final int ENCRYPTEDSEED_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString encryptedSeed_; + public boolean hasEncryptedSeed() { + return ((bitField0_ & 0x00000002) == 0x00000002); } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.KeyOrBuilder getKeysOrBuilder( - int index) { - return keys_.get(index); + public com.google.protobuf.ByteString getEncryptedSeed() { + return encryptedSeed_; } - // repeated .com.bitsofproof.supernode.api.Wallet.PublicAddress publicAddresses = 3; - public static final int PUBLICADDRESSES_FIELD_NUMBER = 3; - private java.util.List publicAddresses_; - public java.util.List getPublicAddressesList() { - return publicAddresses_; - } - public java.util.List - getPublicAddressesOrBuilderList() { - return publicAddresses_; - } - public int getPublicAddressesCount() { - return publicAddresses_.size(); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress getPublicAddresses(int index) { - return publicAddresses_.get(index); + // required bytes signature = 3; + public static final int SIGNATURE_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString signature_; + public boolean hasSignature() { + return ((bitField0_ & 0x00000004) == 0x00000004); } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddressOrBuilder getPublicAddressesOrBuilder( - int index) { - return publicAddresses_.get(index); + public com.google.protobuf.ByteString getSignature() { + return signature_; } - // repeated .com.bitsofproof.supernode.api.Transaction transactions = 4; - public static final int TRANSACTIONS_FIELD_NUMBER = 4; - private java.util.List transactions_; - public java.util.List getTransactionsList() { - return transactions_; + // repeated .com.bitsofproof.supernode.api.Wallet.Account accounts = 4; + public static final int ACCOUNTS_FIELD_NUMBER = 4; + private java.util.List accounts_; + public java.util.List getAccountsList() { + return accounts_; } - public java.util.List - getTransactionsOrBuilderList() { - return transactions_; + public java.util.List + getAccountsOrBuilderList() { + return accounts_; } - public int getTransactionsCount() { - return transactions_.size(); + public int getAccountsCount() { + return accounts_.size(); } - public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction getTransactions(int index) { - return transactions_.get(index); + public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account getAccounts(int index) { + return accounts_.get(index); } - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder getTransactionsOrBuilder( + public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.AccountOrBuilder getAccountsOrBuilder( int index) { - return transactions_.get(index); + return accounts_.get(index); } private void initFields() { bcsapiversion_ = 0; - keys_ = java.util.Collections.emptyList(); - publicAddresses_ = java.util.Collections.emptyList(); - transactions_ = java.util.Collections.emptyList(); + encryptedSeed_ = com.google.protobuf.ByteString.EMPTY; + signature_ = com.google.protobuf.ByteString.EMPTY; + accounts_ = java.util.Collections.emptyList(); } private byte memoizedIsInitialized = -1; public final boolean isInitialized() { @@ -9731,20 +9513,16 @@ public final boolean isInitialized() { memoizedIsInitialized = 0; return false; } - for (int i = 0; i < getKeysCount(); i++) { - if (!getKeys(i).isInitialized()) { - memoizedIsInitialized = 0; - return false; - } + if (!hasEncryptedSeed()) { + memoizedIsInitialized = 0; + return false; } - for (int i = 0; i < getPublicAddressesCount(); i++) { - if (!getPublicAddresses(i).isInitialized()) { - memoizedIsInitialized = 0; - return false; - } + if (!hasSignature()) { + memoizedIsInitialized = 0; + return false; } - for (int i = 0; i < getTransactionsCount(); i++) { - if (!getTransactions(i).isInitialized()) { + for (int i = 0; i < getAccountsCount(); i++) { + if (!getAccounts(i).isInitialized()) { memoizedIsInitialized = 0; return false; } @@ -9759,14 +9537,14 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (((bitField0_ & 0x00000001) == 0x00000001)) { output.writeUInt32(1, bcsapiversion_); } - for (int i = 0; i < keys_.size(); i++) { - output.writeMessage(2, keys_.get(i)); + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, encryptedSeed_); } - for (int i = 0; i < publicAddresses_.size(); i++) { - output.writeMessage(3, publicAddresses_.get(i)); + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, signature_); } - for (int i = 0; i < transactions_.size(); i++) { - output.writeMessage(4, transactions_.get(i)); + for (int i = 0; i < accounts_.size(); i++) { + output.writeMessage(4, accounts_.get(i)); } getUnknownFields().writeTo(output); } @@ -9781,17 +9559,17 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeUInt32Size(1, bcsapiversion_); } - for (int i = 0; i < keys_.size(); i++) { + if (((bitField0_ & 0x00000002) == 0x00000002)) { size += com.google.protobuf.CodedOutputStream - .computeMessageSize(2, keys_.get(i)); + .computeBytesSize(2, encryptedSeed_); } - for (int i = 0; i < publicAddresses_.size(); i++) { + if (((bitField0_ & 0x00000004) == 0x00000004)) { size += com.google.protobuf.CodedOutputStream - .computeMessageSize(3, publicAddresses_.get(i)); + .computeBytesSize(3, signature_); } - for (int i = 0; i < transactions_.size(); i++) { + for (int i = 0; i < accounts_.size(); i++) { size += com.google.protobuf.CodedOutputStream - .computeMessageSize(4, transactions_.get(i)); + .computeMessageSize(4, accounts_.get(i)); } size += getUnknownFields().getSerializedSize(); memoizedSerializedSize = size; @@ -9880,891 +9658,495 @@ public static Builder newBuilder(com.bitsofproof.supernode.api.BCSAPIMessage.Wal public Builder toBuilder() { return newBuilder(this); } @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.api.BCSAPIMessage.WalletOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getKeysFieldBuilder(); - getPublicAddressesFieldBuilder(); - getTransactionsFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - bcsapiversion_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - if (keysBuilder_ == null) { - keys_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000002); - } else { - keysBuilder_.clear(); - } - if (publicAddressesBuilder_ == null) { - publicAddresses_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000004); - } else { - publicAddressesBuilder_.clear(); - } - if (transactionsBuilder_ == null) { - transactions_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); - } else { - transactionsBuilder_.clear(); - } - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.getDescriptor(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet getDefaultInstanceForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.getDefaultInstance(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet build() { - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.api.BCSAPIMessage.Wallet buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet buildPartial() { - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet result = new com.bitsofproof.supernode.api.BCSAPIMessage.Wallet(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.bcsapiversion_ = bcsapiversion_; - if (keysBuilder_ == null) { - if (((bitField0_ & 0x00000002) == 0x00000002)) { - keys_ = java.util.Collections.unmodifiableList(keys_); - bitField0_ = (bitField0_ & ~0x00000002); - } - result.keys_ = keys_; - } else { - result.keys_ = keysBuilder_.build(); - } - if (publicAddressesBuilder_ == null) { - if (((bitField0_ & 0x00000004) == 0x00000004)) { - publicAddresses_ = java.util.Collections.unmodifiableList(publicAddresses_); - bitField0_ = (bitField0_ & ~0x00000004); - } - result.publicAddresses_ = publicAddresses_; - } else { - result.publicAddresses_ = publicAddressesBuilder_.build(); - } - if (transactionsBuilder_ == null) { - if (((bitField0_ & 0x00000008) == 0x00000008)) { - transactions_ = java.util.Collections.unmodifiableList(transactions_); - bitField0_ = (bitField0_ & ~0x00000008); - } - result.transactions_ = transactions_; - } else { - result.transactions_ = transactionsBuilder_.build(); - } - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.api.BCSAPIMessage.Wallet) { - return mergeFrom((com.bitsofproof.supernode.api.BCSAPIMessage.Wallet)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.Wallet other) { - if (other == com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.getDefaultInstance()) return this; - if (other.hasBcsapiversion()) { - setBcsapiversion(other.getBcsapiversion()); - } - if (keysBuilder_ == null) { - if (!other.keys_.isEmpty()) { - if (keys_.isEmpty()) { - keys_ = other.keys_; - bitField0_ = (bitField0_ & ~0x00000002); - } else { - ensureKeysIsMutable(); - keys_.addAll(other.keys_); - } - onChanged(); - } - } else { - if (!other.keys_.isEmpty()) { - if (keysBuilder_.isEmpty()) { - keysBuilder_.dispose(); - keysBuilder_ = null; - keys_ = other.keys_; - bitField0_ = (bitField0_ & ~0x00000002); - keysBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getKeysFieldBuilder() : null; - } else { - keysBuilder_.addAllMessages(other.keys_); - } - } - } - if (publicAddressesBuilder_ == null) { - if (!other.publicAddresses_.isEmpty()) { - if (publicAddresses_.isEmpty()) { - publicAddresses_ = other.publicAddresses_; - bitField0_ = (bitField0_ & ~0x00000004); - } else { - ensurePublicAddressesIsMutable(); - publicAddresses_.addAll(other.publicAddresses_); - } - onChanged(); - } - } else { - if (!other.publicAddresses_.isEmpty()) { - if (publicAddressesBuilder_.isEmpty()) { - publicAddressesBuilder_.dispose(); - publicAddressesBuilder_ = null; - publicAddresses_ = other.publicAddresses_; - bitField0_ = (bitField0_ & ~0x00000004); - publicAddressesBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getPublicAddressesFieldBuilder() : null; - } else { - publicAddressesBuilder_.addAllMessages(other.publicAddresses_); - } - } - } - if (transactionsBuilder_ == null) { - if (!other.transactions_.isEmpty()) { - if (transactions_.isEmpty()) { - transactions_ = other.transactions_; - bitField0_ = (bitField0_ & ~0x00000008); - } else { - ensureTransactionsIsMutable(); - transactions_.addAll(other.transactions_); - } - onChanged(); - } - } else { - if (!other.transactions_.isEmpty()) { - if (transactionsBuilder_.isEmpty()) { - transactionsBuilder_.dispose(); - transactionsBuilder_ = null; - transactions_ = other.transactions_; - bitField0_ = (bitField0_ & ~0x00000008); - transactionsBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getTransactionsFieldBuilder() : null; - } else { - transactionsBuilder_.addAllMessages(other.transactions_); - } - } - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements com.bitsofproof.supernode.api.BCSAPIMessage.WalletOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_descriptor; } - public final boolean isInitialized() { - if (!hasBcsapiversion()) { - - return false; - } - for (int i = 0; i < getKeysCount(); i++) { - if (!getKeys(i).isInitialized()) { - - return false; - } - } - for (int i = 0; i < getPublicAddressesCount(); i++) { - if (!getPublicAddresses(i).isInitialized()) { - - return false; - } - } - for (int i = 0; i < getTransactionsCount(); i++) { - if (!getTransactions(i).isInitialized()) { - - return false; - } - } - return true; + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_fieldAccessorTable; } - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - bcsapiversion_ = input.readUInt32(); - break; - } - case 18: { - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key.Builder subBuilder = com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key.newBuilder(); - input.readMessage(subBuilder, extensionRegistry); - addKeys(subBuilder.buildPartial()); - break; - } - case 26: { - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress.Builder subBuilder = com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress.newBuilder(); - input.readMessage(subBuilder, extensionRegistry); - addPublicAddresses(subBuilder.buildPartial()); - break; - } - case 34: { - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder subBuilder = com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.newBuilder(); - input.readMessage(subBuilder, extensionRegistry); - addTransactions(subBuilder.buildPartial()); - break; - } - } - } + // Construct using com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); } - private int bitField0_; - - // required uint32 bcsapiversion = 1; - private int bcsapiversion_ ; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); + private Builder(BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); } - public int getBcsapiversion() { - return bcsapiversion_; + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getAccountsFieldBuilder(); + } } - public Builder setBcsapiversion(int value) { - bitField0_ |= 0x00000001; - bcsapiversion_ = value; - onChanged(); - return this; + private static Builder create() { + return new Builder(); } - public Builder clearBcsapiversion() { - bitField0_ = (bitField0_ & ~0x00000001); + + public Builder clear() { + super.clear(); bcsapiversion_ = 0; - onChanged(); + bitField0_ = (bitField0_ & ~0x00000001); + encryptedSeed_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + signature_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + if (accountsBuilder_ == null) { + accounts_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000008); + } else { + accountsBuilder_.clear(); + } return this; } - // repeated .com.bitsofproof.supernode.api.Wallet.Key keys = 2; - private java.util.List keys_ = - java.util.Collections.emptyList(); - private void ensureKeysIsMutable() { - if (!((bitField0_ & 0x00000002) == 0x00000002)) { - keys_ = new java.util.ArrayList(keys_); - bitField0_ |= 0x00000002; - } + public Builder clone() { + return create().mergeFrom(buildPartial()); } - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.KeyOrBuilder> keysBuilder_; + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.getDescriptor(); + } - public java.util.List getKeysList() { - if (keysBuilder_ == null) { - return java.util.Collections.unmodifiableList(keys_); - } else { - return keysBuilder_.getMessageList(); - } + public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet getDefaultInstanceForType() { + return com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.getDefaultInstance(); } - public int getKeysCount() { - if (keysBuilder_ == null) { - return keys_.size(); - } else { - return keysBuilder_.getCount(); + + public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet build() { + com.bitsofproof.supernode.api.BCSAPIMessage.Wallet result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); } + return result; } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key getKeys(int index) { - if (keysBuilder_ == null) { - return keys_.get(index); - } else { - return keysBuilder_.getMessage(index); + + private com.bitsofproof.supernode.api.BCSAPIMessage.Wallet buildParsed() + throws com.google.protobuf.InvalidProtocolBufferException { + com.bitsofproof.supernode.api.BCSAPIMessage.Wallet result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException( + result).asInvalidProtocolBufferException(); } + return result; } - public Builder setKeys( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key value) { - if (keysBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureKeysIsMutable(); - keys_.set(index, value); - onChanged(); - } else { - keysBuilder_.setMessage(index, value); + + public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet buildPartial() { + com.bitsofproof.supernode.api.BCSAPIMessage.Wallet result = new com.bitsofproof.supernode.api.BCSAPIMessage.Wallet(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; } - return this; - } - public Builder setKeys( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key.Builder builderForValue) { - if (keysBuilder_ == null) { - ensureKeysIsMutable(); - keys_.set(index, builderForValue.build()); - onChanged(); - } else { - keysBuilder_.setMessage(index, builderForValue.build()); + result.bcsapiversion_ = bcsapiversion_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; } - return this; - } - public Builder addKeys(com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key value) { - if (keysBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureKeysIsMutable(); - keys_.add(value); - onChanged(); - } else { - keysBuilder_.addMessage(value); + result.encryptedSeed_ = encryptedSeed_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; } - return this; - } - public Builder addKeys( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key value) { - if (keysBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); + result.signature_ = signature_; + if (accountsBuilder_ == null) { + if (((bitField0_ & 0x00000008) == 0x00000008)) { + accounts_ = java.util.Collections.unmodifiableList(accounts_); + bitField0_ = (bitField0_ & ~0x00000008); } - ensureKeysIsMutable(); - keys_.add(index, value); - onChanged(); - } else { - keysBuilder_.addMessage(index, value); - } - return this; - } - public Builder addKeys( - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key.Builder builderForValue) { - if (keysBuilder_ == null) { - ensureKeysIsMutable(); - keys_.add(builderForValue.build()); - onChanged(); - } else { - keysBuilder_.addMessage(builderForValue.build()); - } - return this; - } - public Builder addKeys( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key.Builder builderForValue) { - if (keysBuilder_ == null) { - ensureKeysIsMutable(); - keys_.add(index, builderForValue.build()); - onChanged(); + result.accounts_ = accounts_; } else { - keysBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - public Builder addAllKeys( - java.lang.Iterable values) { - if (keysBuilder_ == null) { - ensureKeysIsMutable(); - super.addAll(values, keys_); - onChanged(); - } else { - keysBuilder_.addAllMessages(values); - } - return this; - } - public Builder clearKeys() { - if (keysBuilder_ == null) { - keys_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000002); - onChanged(); - } else { - keysBuilder_.clear(); - } - return this; - } - public Builder removeKeys(int index) { - if (keysBuilder_ == null) { - ensureKeysIsMutable(); - keys_.remove(index); - onChanged(); - } else { - keysBuilder_.remove(index); - } - return this; - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key.Builder getKeysBuilder( - int index) { - return getKeysFieldBuilder().getBuilder(index); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.KeyOrBuilder getKeysOrBuilder( - int index) { - if (keysBuilder_ == null) { - return keys_.get(index); } else { - return keysBuilder_.getMessageOrBuilder(index); + result.accounts_ = accountsBuilder_.build(); } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; } - public java.util.List - getKeysOrBuilderList() { - if (keysBuilder_ != null) { - return keysBuilder_.getMessageOrBuilderList(); + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof com.bitsofproof.supernode.api.BCSAPIMessage.Wallet) { + return mergeFrom((com.bitsofproof.supernode.api.BCSAPIMessage.Wallet)other); } else { - return java.util.Collections.unmodifiableList(keys_); - } - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key.Builder addKeysBuilder() { - return getKeysFieldBuilder().addBuilder( - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key.getDefaultInstance()); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key.Builder addKeysBuilder( - int index) { - return getKeysFieldBuilder().addBuilder( - index, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key.getDefaultInstance()); - } - public java.util.List - getKeysBuilderList() { - return getKeysFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.KeyOrBuilder> - getKeysFieldBuilder() { - if (keysBuilder_ == null) { - keysBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.KeyOrBuilder>( - keys_, - ((bitField0_ & 0x00000002) == 0x00000002), - getParentForChildren(), - isClean()); - keys_ = null; + super.mergeFrom(other); + return this; } - return keysBuilder_; - } - - // repeated .com.bitsofproof.supernode.api.Wallet.PublicAddress publicAddresses = 3; - private java.util.List publicAddresses_ = - java.util.Collections.emptyList(); - private void ensurePublicAddressesIsMutable() { - if (!((bitField0_ & 0x00000004) == 0x00000004)) { - publicAddresses_ = new java.util.ArrayList(publicAddresses_); - bitField0_ |= 0x00000004; - } } - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddressOrBuilder> publicAddressesBuilder_; - - public java.util.List getPublicAddressesList() { - if (publicAddressesBuilder_ == null) { - return java.util.Collections.unmodifiableList(publicAddresses_); - } else { - return publicAddressesBuilder_.getMessageList(); + public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.Wallet other) { + if (other == com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.getDefaultInstance()) return this; + if (other.hasBcsapiversion()) { + setBcsapiversion(other.getBcsapiversion()); } - } - public int getPublicAddressesCount() { - if (publicAddressesBuilder_ == null) { - return publicAddresses_.size(); - } else { - return publicAddressesBuilder_.getCount(); + if (other.hasEncryptedSeed()) { + setEncryptedSeed(other.getEncryptedSeed()); } - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress getPublicAddresses(int index) { - if (publicAddressesBuilder_ == null) { - return publicAddresses_.get(index); - } else { - return publicAddressesBuilder_.getMessage(index); + if (other.hasSignature()) { + setSignature(other.getSignature()); } - } - public Builder setPublicAddresses( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress value) { - if (publicAddressesBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); + if (accountsBuilder_ == null) { + if (!other.accounts_.isEmpty()) { + if (accounts_.isEmpty()) { + accounts_ = other.accounts_; + bitField0_ = (bitField0_ & ~0x00000008); + } else { + ensureAccountsIsMutable(); + accounts_.addAll(other.accounts_); + } + onChanged(); } - ensurePublicAddressesIsMutable(); - publicAddresses_.set(index, value); - onChanged(); } else { - publicAddressesBuilder_.setMessage(index, value); + if (!other.accounts_.isEmpty()) { + if (accountsBuilder_.isEmpty()) { + accountsBuilder_.dispose(); + accountsBuilder_ = null; + accounts_ = other.accounts_; + bitField0_ = (bitField0_ & ~0x00000008); + accountsBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getAccountsFieldBuilder() : null; + } else { + accountsBuilder_.addAllMessages(other.accounts_); + } + } } + this.mergeUnknownFields(other.getUnknownFields()); return this; } - public Builder setPublicAddresses( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress.Builder builderForValue) { - if (publicAddressesBuilder_ == null) { - ensurePublicAddressesIsMutable(); - publicAddresses_.set(index, builderForValue.build()); - onChanged(); - } else { - publicAddressesBuilder_.setMessage(index, builderForValue.build()); + + public final boolean isInitialized() { + if (!hasBcsapiversion()) { + + return false; } - return this; - } - public Builder addPublicAddresses(com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress value) { - if (publicAddressesBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); + if (!hasEncryptedSeed()) { + + return false; + } + if (!hasSignature()) { + + return false; + } + for (int i = 0; i < getAccountsCount(); i++) { + if (!getAccounts(i).isInitialized()) { + + return false; } - ensurePublicAddressesIsMutable(); - publicAddresses_.add(value); - onChanged(); - } else { - publicAddressesBuilder_.addMessage(value); } - return this; + return true; } - public Builder addPublicAddresses( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress value) { - if (publicAddressesBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder( + this.getUnknownFields()); + while (true) { + int tag = input.readTag(); + switch (tag) { + case 0: + this.setUnknownFields(unknownFields.build()); + onChanged(); + return this; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + this.setUnknownFields(unknownFields.build()); + onChanged(); + return this; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + bcsapiversion_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + encryptedSeed_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + signature_ = input.readBytes(); + break; + } + case 34: { + com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder subBuilder = com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.newBuilder(); + input.readMessage(subBuilder, extensionRegistry); + addAccounts(subBuilder.buildPartial()); + break; + } } - ensurePublicAddressesIsMutable(); - publicAddresses_.add(index, value); - onChanged(); - } else { - publicAddressesBuilder_.addMessage(index, value); } - return this; } - public Builder addPublicAddresses( - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress.Builder builderForValue) { - if (publicAddressesBuilder_ == null) { - ensurePublicAddressesIsMutable(); - publicAddresses_.add(builderForValue.build()); - onChanged(); - } else { - publicAddressesBuilder_.addMessage(builderForValue.build()); - } - return this; + + private int bitField0_; + + // required uint32 bcsapiversion = 1; + private int bcsapiversion_ ; + public boolean hasBcsapiversion() { + return ((bitField0_ & 0x00000001) == 0x00000001); } - public Builder addPublicAddresses( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress.Builder builderForValue) { - if (publicAddressesBuilder_ == null) { - ensurePublicAddressesIsMutable(); - publicAddresses_.add(index, builderForValue.build()); - onChanged(); - } else { - publicAddressesBuilder_.addMessage(index, builderForValue.build()); - } - return this; + public int getBcsapiversion() { + return bcsapiversion_; } - public Builder addAllPublicAddresses( - java.lang.Iterable values) { - if (publicAddressesBuilder_ == null) { - ensurePublicAddressesIsMutable(); - super.addAll(values, publicAddresses_); - onChanged(); - } else { - publicAddressesBuilder_.addAllMessages(values); - } + public Builder setBcsapiversion(int value) { + bitField0_ |= 0x00000001; + bcsapiversion_ = value; + onChanged(); return this; } - public Builder clearPublicAddresses() { - if (publicAddressesBuilder_ == null) { - publicAddresses_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000004); - onChanged(); - } else { - publicAddressesBuilder_.clear(); - } + public Builder clearBcsapiversion() { + bitField0_ = (bitField0_ & ~0x00000001); + bcsapiversion_ = 0; + onChanged(); return this; } - public Builder removePublicAddresses(int index) { - if (publicAddressesBuilder_ == null) { - ensurePublicAddressesIsMutable(); - publicAddresses_.remove(index); - onChanged(); - } else { - publicAddressesBuilder_.remove(index); - } - return this; + + // required bytes encryptedSeed = 2; + private com.google.protobuf.ByteString encryptedSeed_ = com.google.protobuf.ByteString.EMPTY; + public boolean hasEncryptedSeed() { + return ((bitField0_ & 0x00000002) == 0x00000002); } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress.Builder getPublicAddressesBuilder( - int index) { - return getPublicAddressesFieldBuilder().getBuilder(index); + public com.google.protobuf.ByteString getEncryptedSeed() { + return encryptedSeed_; } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddressOrBuilder getPublicAddressesOrBuilder( - int index) { - if (publicAddressesBuilder_ == null) { - return publicAddresses_.get(index); } else { - return publicAddressesBuilder_.getMessageOrBuilder(index); - } + public Builder setEncryptedSeed(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + encryptedSeed_ = value; + onChanged(); + return this; } - public java.util.List - getPublicAddressesOrBuilderList() { - if (publicAddressesBuilder_ != null) { - return publicAddressesBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(publicAddresses_); - } + public Builder clearEncryptedSeed() { + bitField0_ = (bitField0_ & ~0x00000002); + encryptedSeed_ = getDefaultInstance().getEncryptedSeed(); + onChanged(); + return this; } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress.Builder addPublicAddressesBuilder() { - return getPublicAddressesFieldBuilder().addBuilder( - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress.getDefaultInstance()); + + // required bytes signature = 3; + private com.google.protobuf.ByteString signature_ = com.google.protobuf.ByteString.EMPTY; + public boolean hasSignature() { + return ((bitField0_ & 0x00000004) == 0x00000004); } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress.Builder addPublicAddressesBuilder( - int index) { - return getPublicAddressesFieldBuilder().addBuilder( - index, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress.getDefaultInstance()); + public com.google.protobuf.ByteString getSignature() { + return signature_; } - public java.util.List - getPublicAddressesBuilderList() { - return getPublicAddressesFieldBuilder().getBuilderList(); + public Builder setSignature(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + signature_ = value; + onChanged(); + return this; } - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddressOrBuilder> - getPublicAddressesFieldBuilder() { - if (publicAddressesBuilder_ == null) { - publicAddressesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddressOrBuilder>( - publicAddresses_, - ((bitField0_ & 0x00000004) == 0x00000004), - getParentForChildren(), - isClean()); - publicAddresses_ = null; - } - return publicAddressesBuilder_; + public Builder clearSignature() { + bitField0_ = (bitField0_ & ~0x00000004); + signature_ = getDefaultInstance().getSignature(); + onChanged(); + return this; } - // repeated .com.bitsofproof.supernode.api.Transaction transactions = 4; - private java.util.List transactions_ = + // repeated .com.bitsofproof.supernode.api.Wallet.Account accounts = 4; + private java.util.List accounts_ = java.util.Collections.emptyList(); - private void ensureTransactionsIsMutable() { + private void ensureAccountsIsMutable() { if (!((bitField0_ & 0x00000008) == 0x00000008)) { - transactions_ = new java.util.ArrayList(transactions_); + accounts_ = new java.util.ArrayList(accounts_); bitField0_ |= 0x00000008; } } private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder> transactionsBuilder_; + com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.AccountOrBuilder> accountsBuilder_; - public java.util.List getTransactionsList() { - if (transactionsBuilder_ == null) { - return java.util.Collections.unmodifiableList(transactions_); + public java.util.List getAccountsList() { + if (accountsBuilder_ == null) { + return java.util.Collections.unmodifiableList(accounts_); } else { - return transactionsBuilder_.getMessageList(); + return accountsBuilder_.getMessageList(); } } - public int getTransactionsCount() { - if (transactionsBuilder_ == null) { - return transactions_.size(); + public int getAccountsCount() { + if (accountsBuilder_ == null) { + return accounts_.size(); } else { - return transactionsBuilder_.getCount(); + return accountsBuilder_.getCount(); } } - public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction getTransactions(int index) { - if (transactionsBuilder_ == null) { - return transactions_.get(index); + public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account getAccounts(int index) { + if (accountsBuilder_ == null) { + return accounts_.get(index); } else { - return transactionsBuilder_.getMessage(index); + return accountsBuilder_.getMessage(index); } } - public Builder setTransactions( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction value) { - if (transactionsBuilder_ == null) { + public Builder setAccounts( + int index, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account value) { + if (accountsBuilder_ == null) { if (value == null) { throw new NullPointerException(); } - ensureTransactionsIsMutable(); - transactions_.set(index, value); + ensureAccountsIsMutable(); + accounts_.set(index, value); onChanged(); } else { - transactionsBuilder_.setMessage(index, value); + accountsBuilder_.setMessage(index, value); } return this; } - public Builder setTransactions( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder builderForValue) { - if (transactionsBuilder_ == null) { - ensureTransactionsIsMutable(); - transactions_.set(index, builderForValue.build()); + public Builder setAccounts( + int index, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder builderForValue) { + if (accountsBuilder_ == null) { + ensureAccountsIsMutable(); + accounts_.set(index, builderForValue.build()); onChanged(); } else { - transactionsBuilder_.setMessage(index, builderForValue.build()); + accountsBuilder_.setMessage(index, builderForValue.build()); } return this; } - public Builder addTransactions(com.bitsofproof.supernode.api.BCSAPIMessage.Transaction value) { - if (transactionsBuilder_ == null) { + public Builder addAccounts(com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account value) { + if (accountsBuilder_ == null) { if (value == null) { throw new NullPointerException(); } - ensureTransactionsIsMutable(); - transactions_.add(value); + ensureAccountsIsMutable(); + accounts_.add(value); onChanged(); } else { - transactionsBuilder_.addMessage(value); + accountsBuilder_.addMessage(value); } return this; } - public Builder addTransactions( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction value) { - if (transactionsBuilder_ == null) { + public Builder addAccounts( + int index, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account value) { + if (accountsBuilder_ == null) { if (value == null) { throw new NullPointerException(); } - ensureTransactionsIsMutable(); - transactions_.add(index, value); + ensureAccountsIsMutable(); + accounts_.add(index, value); onChanged(); } else { - transactionsBuilder_.addMessage(index, value); + accountsBuilder_.addMessage(index, value); } return this; } - public Builder addTransactions( - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder builderForValue) { - if (transactionsBuilder_ == null) { - ensureTransactionsIsMutable(); - transactions_.add(builderForValue.build()); + public Builder addAccounts( + com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder builderForValue) { + if (accountsBuilder_ == null) { + ensureAccountsIsMutable(); + accounts_.add(builderForValue.build()); onChanged(); } else { - transactionsBuilder_.addMessage(builderForValue.build()); + accountsBuilder_.addMessage(builderForValue.build()); } return this; } - public Builder addTransactions( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder builderForValue) { - if (transactionsBuilder_ == null) { - ensureTransactionsIsMutable(); - transactions_.add(index, builderForValue.build()); + public Builder addAccounts( + int index, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder builderForValue) { + if (accountsBuilder_ == null) { + ensureAccountsIsMutable(); + accounts_.add(index, builderForValue.build()); onChanged(); } else { - transactionsBuilder_.addMessage(index, builderForValue.build()); + accountsBuilder_.addMessage(index, builderForValue.build()); } return this; } - public Builder addAllTransactions( - java.lang.Iterable values) { - if (transactionsBuilder_ == null) { - ensureTransactionsIsMutable(); - super.addAll(values, transactions_); + public Builder addAllAccounts( + java.lang.Iterable values) { + if (accountsBuilder_ == null) { + ensureAccountsIsMutable(); + super.addAll(values, accounts_); onChanged(); } else { - transactionsBuilder_.addAllMessages(values); + accountsBuilder_.addAllMessages(values); } return this; } - public Builder clearTransactions() { - if (transactionsBuilder_ == null) { - transactions_ = java.util.Collections.emptyList(); + public Builder clearAccounts() { + if (accountsBuilder_ == null) { + accounts_ = java.util.Collections.emptyList(); bitField0_ = (bitField0_ & ~0x00000008); onChanged(); } else { - transactionsBuilder_.clear(); + accountsBuilder_.clear(); } return this; } - public Builder removeTransactions(int index) { - if (transactionsBuilder_ == null) { - ensureTransactionsIsMutable(); - transactions_.remove(index); + public Builder removeAccounts(int index) { + if (accountsBuilder_ == null) { + ensureAccountsIsMutable(); + accounts_.remove(index); onChanged(); } else { - transactionsBuilder_.remove(index); + accountsBuilder_.remove(index); } return this; } - public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder getTransactionsBuilder( + public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder getAccountsBuilder( int index) { - return getTransactionsFieldBuilder().getBuilder(index); + return getAccountsFieldBuilder().getBuilder(index); } - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder getTransactionsOrBuilder( + public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.AccountOrBuilder getAccountsOrBuilder( int index) { - if (transactionsBuilder_ == null) { - return transactions_.get(index); } else { - return transactionsBuilder_.getMessageOrBuilder(index); + if (accountsBuilder_ == null) { + return accounts_.get(index); } else { + return accountsBuilder_.getMessageOrBuilder(index); } } - public java.util.List - getTransactionsOrBuilderList() { - if (transactionsBuilder_ != null) { - return transactionsBuilder_.getMessageOrBuilderList(); + public java.util.List + getAccountsOrBuilderList() { + if (accountsBuilder_ != null) { + return accountsBuilder_.getMessageOrBuilderList(); } else { - return java.util.Collections.unmodifiableList(transactions_); + return java.util.Collections.unmodifiableList(accounts_); } } - public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder addTransactionsBuilder() { - return getTransactionsFieldBuilder().addBuilder( - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.getDefaultInstance()); + public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder addAccountsBuilder() { + return getAccountsFieldBuilder().addBuilder( + com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.getDefaultInstance()); } - public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder addTransactionsBuilder( + public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder addAccountsBuilder( int index) { - return getTransactionsFieldBuilder().addBuilder( - index, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.getDefaultInstance()); + return getAccountsFieldBuilder().addBuilder( + index, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.getDefaultInstance()); } - public java.util.List - getTransactionsBuilderList() { - return getTransactionsFieldBuilder().getBuilderList(); + public java.util.List + getAccountsBuilderList() { + return getAccountsFieldBuilder().getBuilderList(); } private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder> - getTransactionsFieldBuilder() { - if (transactionsBuilder_ == null) { - transactionsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder>( - transactions_, + com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.AccountOrBuilder> + getAccountsFieldBuilder() { + if (accountsBuilder_ == null) { + accountsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.AccountOrBuilder>( + accounts_, ((bitField0_ & 0x00000008) == 0x00000008), getParentForChildren(), isClean()); - transactions_ = null; + accounts_ = null; } - return transactionsBuilder_; + return accountsBuilder_; } // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.Wallet) @@ -10849,15 +10231,10 @@ public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder addTransa com.google.protobuf.GeneratedMessage.FieldAccessorTable internal_static_com_bitsofproof_supernode_api_Wallet_fieldAccessorTable; private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_api_Wallet_Key_descriptor; + internal_static_com_bitsofproof_supernode_api_Wallet_Account_descriptor; private static com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_api_Wallet_Key_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_api_Wallet_PublicAddress_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_api_Wallet_PublicAddress_fieldAccessorTable; + internal_static_com_bitsofproof_supernode_api_Wallet_Account_fieldAccessorTable; public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { @@ -10903,16 +10280,14 @@ public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder addTransa "\002(\r\022\014\n\004mode\030\002 \002(\r\022\r\n\005match\030\003 \003(\014\022\r\n\005afte" + "r\030\004 \001(\004\"\\\n\016AccountRequest\022\025\n\rbcsapiversi" + "on\030\001 \002(\r\022\021\n\tpublicKey\030\002 \002(\t\022\021\n\tlookAhead" + - "\030\003 \002(\r\022\r\n\005after\030\004 \001(\004\"\341\002\n\006Wallet\022\025\n\rbcsa" + - "piversion\030\001 \002(\r\0227\n\004keys\030\002 \003(\0132).com.bits" + - "ofproof.supernode.api.Wallet.Key\022L\n\017publ" + - "icAddresses\030\003 \003(\01323.com.bitsofproof.supe", - "rnode.api.Wallet.PublicAddress\022@\n\014transa" + - "ctions\030\004 \003(\0132*.com.bitsofproof.supernode" + - ".api.Transaction\032G\n\003Key\022\013\n\003key\030\001 \002(\t\022\024\n\014" + - "nextSequence\030\002 \002(\r\022\014\n\004name\030\003 \001(\t\022\017\n\007crea" + - "ted\030\004 \001(\004\032.\n\rPublicAddress\022\017\n\007address\030\001 " + - "\002(\t\022\014\n\004name\030\002 \002(\t" + "\030\003 \002(\r\022\r\n\005after\030\004 \001(\004\"\240\002\n\006Wallet\022\025\n\rbcsa" + + "piversion\030\001 \002(\r\022\025\n\rencryptedSeed\030\002 \002(\014\022\021" + + "\n\tsignature\030\003 \002(\014\022?\n\010accounts\030\004 \003(\0132-.co" + + "m.bitsofproof.supernode.api.Wallet.Accou", + "nt\032\223\001\n\007Account\022\021\n\tpublicKey\030\001 \002(\t\022@\n\014tra" + + "nsactions\030\002 \003(\0132*.com.bitsofproof.supern" + + "ode.api.Transaction\022\024\n\014nextSequence\030\003 \002(" + + "\r\022\017\n\007created\030\004 \002(\004\022\014\n\004name\030\005 \002(\t" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -11028,25 +10403,17 @@ public com.google.protobuf.ExtensionRegistry assignDescriptors( internal_static_com_bitsofproof_supernode_api_Wallet_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_com_bitsofproof_supernode_api_Wallet_descriptor, - new java.lang.String[] { "Bcsapiversion", "Keys", "PublicAddresses", "Transactions", }, + new java.lang.String[] { "Bcsapiversion", "EncryptedSeed", "Signature", "Accounts", }, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.class, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Builder.class); - internal_static_com_bitsofproof_supernode_api_Wallet_Key_descriptor = + internal_static_com_bitsofproof_supernode_api_Wallet_Account_descriptor = internal_static_com_bitsofproof_supernode_api_Wallet_descriptor.getNestedTypes().get(0); - internal_static_com_bitsofproof_supernode_api_Wallet_Key_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_api_Wallet_Key_descriptor, - new java.lang.String[] { "Key", "NextSequence", "Name", "Created", }, - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key.class, - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Key.Builder.class); - internal_static_com_bitsofproof_supernode_api_Wallet_PublicAddress_descriptor = - internal_static_com_bitsofproof_supernode_api_Wallet_descriptor.getNestedTypes().get(1); - internal_static_com_bitsofproof_supernode_api_Wallet_PublicAddress_fieldAccessorTable = new + internal_static_com_bitsofproof_supernode_api_Wallet_Account_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_api_Wallet_PublicAddress_descriptor, - new java.lang.String[] { "Address", "Name", }, - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress.class, - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.PublicAddress.Builder.class); + internal_static_com_bitsofproof_supernode_api_Wallet_Account_descriptor, + new java.lang.String[] { "PublicKey", "Transactions", "NextSequence", "Created", "Name", }, + com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.class, + com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder.class); return null; } }; diff --git a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java new file mode 100644 index 00000000..add90343 --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java @@ -0,0 +1,183 @@ +package com.bitsofproof.supernode.api; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.SecureRandom; +import java.util.HashMap; +import java.util.Map; + +import com.bitsofproof.supernode.common.Key; +import com.bitsofproof.supernode.common.ValidationException; +import com.google.protobuf.ByteString; + +public class FileWallet implements Wallet +{ + private BCSAPI api; + + private static final SecureRandom random = new SecureRandom (); + private transient ExtendedKey master; + private byte[] encryptedSeed; + private byte[] signature; + private String fileName; + + private final Map accounts = new HashMap (); + + public FileWallet (String fileName) + { + this.fileName = fileName; + } + + public boolean exists () + { + return new File (fileName).exists (); + } + + @Override + public void setApi (BCSAPI api) + { + this.api = api; + } + + @Override + public void init (String passphrase) + { + master = null; + encryptedSeed = new byte[32]; + random.nextBytes (encryptedSeed); + try + { + signature = ExtendedKey.createFromPassphrase (passphrase, encryptedSeed).getMaster ().sign (encryptedSeed); + } + catch ( ValidationException e ) + { + } + } + + @Override + public void unlock (String passphrase) throws ValidationException + { + master = ExtendedKey.createFromPassphrase (passphrase, encryptedSeed); + if ( !master.getMaster ().verify (encryptedSeed, signature) ) + { + throw new ValidationException ("incorrect passphrase"); + } + } + + @Override + public void lock () + { + master = null; + } + + public void setFileName (String fileName) + { + this.fileName = fileName; + } + + @Override + public void read (String fileName, int lookAhead) throws IOException, ValidationException, BCSAPIException + { + this.fileName = fileName; + master = null; + File f = new File (fileName); + InputStream in = new FileInputStream (f); + try + { + BCSAPIMessage.Wallet walletMessage = BCSAPIMessage.Wallet.parseFrom (in); + encryptedSeed = walletMessage.getEncryptedSeed ().toByteArray (); + signature = walletMessage.getSignature ().toByteArray (); + for ( BCSAPIMessage.Wallet.Account account : walletMessage.getAccountsList () ) + { + InMemoryAccountManager am = + new InMemoryAccountManager (this, account.getName (), ExtendedKey.parse (account.getPublicKey ()), account.getNextSequence (), + account.getCreated ()); + am.setApi (api); + accounts.put (account.getName (), am); + for ( BCSAPIMessage.Transaction t : account.getTransactionsList () ) + { + am.updateWithTransaction (Transaction.fromProtobuf (t)); + } + am.sync (lookAhead, account.getCreated ()); + } + } + finally + { + in.close (); + } + } + + @Override + public synchronized AccountManager getAccountManager (String name) + { + return accounts.get (name); + } + + @Override + public synchronized AccountManager createAccountManager (String name) throws ValidationException, IOException + { + if ( accounts.containsKey (name) ) + { + return accounts.get (name); + } + else + { + if ( master == null ) + { + throw new ValidationException ("The wallet is locked"); + } + InMemoryAccountManager account = + new InMemoryAccountManager (this, name, master.getChild (accounts.size () | 0x80000000), 0, System.currentTimeMillis () / 1000); + account.setApi (api); + accounts.put (name, account); + persist (); + return account; + } + } + + @Override + public Key getKey (AccountManager am, int sequence) throws ValidationException + { + if ( master == null ) + { + return am.getMasterKey ().getKey (sequence); + } + else + { + return master.getChild (am.getMasterKey ().getSequence ()).getKey (sequence); + } + } + + @Override + public void persist () throws IOException + { + FileOutputStream out = new FileOutputStream (fileName); + try + { + BCSAPIMessage.Wallet.Builder builder = BCSAPIMessage.Wallet.newBuilder (); + builder.setBcsapiversion (1); + builder.setEncryptedSeed (ByteString.copyFrom (encryptedSeed)); + builder.setSignature (ByteString.copyFrom (signature)); + for ( AccountManager am : accounts.values () ) + { + BCSAPIMessage.Wallet.Account.Builder ab = BCSAPIMessage.Wallet.Account.newBuilder (); + ab.setName (am.getName ()); + ab.setCreated (am.getCreated ()); + ab.setNextSequence (am.getNextSequence ()); + ab.setPublicKey (am.getMasterKey ().getReadOnly ().serialize (true)); + for ( Transaction t : am.getTransactions () ) + { + ab.addTransactions (t.toProtobuf ()); + } + builder.addAccounts (ab.build ()); + } + builder.build ().writeTo (out); + } + finally + { + out.close (); + } + } +} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java index 0b052f07..e474020f 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -31,8 +32,6 @@ import com.bitsofproof.supernode.api.Transaction.TransactionSink; import com.bitsofproof.supernode.api.Transaction.TransactionSource; -import com.bitsofproof.supernode.common.BloomFilter; -import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.common.ByteVector; import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ScriptFormat; @@ -45,41 +44,49 @@ class InMemoryAccountManager implements TransactionListener, AccountManager private BCSAPI api; - private final Wallet wallet; - private final InMemoryUTXO confirmed = new InMemoryUTXO (); private final InMemoryUTXO change = new InMemoryUTXO (); private final InMemoryUTXO receiving = new InMemoryUTXO (); private final InMemoryUTXO sending = new InMemoryUTXO (); private final List accountListener = Collections.synchronizedList (new ArrayList ()); - private final Map processedTransaction = new HashMap (); + private final Set processedTransaction = new HashSet (); + private final Map relevantTransaction = new HashMap (); private final String name; private final ExtendedKey extended; - private final Map keyForAddress = new HashMap (); - private final BloomFilter filter = BloomFilter.createOptimalFilter (500, 1e-10, UpdateMode.all); - private final Set addressSet = new HashSet (); + private final Map keyIDForAddress = new HashMap (); + private int nextKey; + private final long created; + private final Wallet wallet; + @Override public void setApi (BCSAPI api) { this.api = api; } - public InMemoryAccountManager (Wallet wallet, String name, ExtendedKey extended) + @Override + public long getCreated () + { + return created; + } + + public InMemoryAccountManager (Wallet wallet, String name, ExtendedKey extended, int nextKey, long created) { - this.wallet = wallet; this.name = name; this.extended = extended; + this.nextKey = nextKey; + this.created = created; + this.wallet = wallet; } - public void init (final int lookAhead, long after) throws BCSAPIException, ValidationException + public void sync (final int lookAhead, long after) throws BCSAPIException, ValidationException { - final Map addressUse = new HashMap (); - for ( int i = 0; i < lookAhead; ++i ) + for ( int i = nextKey; i < nextKey + lookAhead; ++i ) { Key key = extended.getKey (i); - addressUse.put (new ByteVector (key.getAddress ()), i); + keyIDForAddress.put (new ByteVector (key.getAddress ()), i); } final AtomicInteger lastUsedKey = new AtomicInteger (-1); api.scanTransactions (extended, lookAhead, after, new TransactionListener () @@ -95,19 +102,19 @@ public void process (Transaction t) { if ( token.data != null ) { - Integer thisKey = addressUse.get (new ByteVector (token.data)); + Integer thisKey = keyIDForAddress.get (new ByteVector (token.data)); if ( thisKey != null ) { lastUsedKey.set (Math.max (thisKey, lastUsedKey.get ())); } else { - while ( thisKey == null && (addressUse.size () - lastUsedKey.get ()) < lookAhead ) + while ( thisKey == null && (keyIDForAddress.size () - lastUsedKey.get ()) < lookAhead ) { - Key key = extended.getKey (addressUse.size ()); - addressUse.put (new ByteVector (key.getAddress ()), addressUse.size ()); + Key key = extended.getKey (keyIDForAddress.size ()); + keyIDForAddress.put (new ByteVector (key.getAddress ()), keyIDForAddress.size ()); } - thisKey = addressUse.get (new ByteVector (token.data)); + thisKey = keyIDForAddress.get (new ByteVector (token.data)); if ( thisKey != null ) { lastUsedKey.set (Math.max (thisKey, lastUsedKey.get ())); @@ -119,27 +126,18 @@ public void process (Transaction t) catch ( ValidationException e ) { } + updateWithTransaction (t); } } }); - for ( int i = 0; i <= lastUsedKey.get (); ++i ) - { - Key key = extended.getKey (i); - keyForAddress.put (new ByteVector (key.getAddress ()), key); - filter.add (key.getAddress ()); - addressSet.add (new ByteVector (key.getAddress ())); - } - } - - public void registerFilter () throws BCSAPIException - { - api.registerFilteredListener (filter, this); + nextKey = Math.max (lastUsedKey.get (), nextKey); + api.registerTransactionListener (this); } @Override public int getNextSequence () { - return keyForAddress.size (); + return nextKey; } @Override @@ -152,7 +150,7 @@ public String getName () public Collection getAddresses () { List addresses = new ArrayList (); - for ( ByteVector v : keyForAddress.keySet () ) + for ( ByteVector v : keyIDForAddress.keySet () ) { addresses.add (v.toByteArray ()); } @@ -162,48 +160,40 @@ public Collection getAddresses () @Override public Key getKeyForAddress (byte[] address) { - return keyForAddress.get (new ByteVector (address)); - } - - @Override - public Key getNextKey () throws ValidationException - { - Key key = extended.getKey (keyForAddress.size ()); - keyForAddress.put (new ByteVector (key.getAddress ()), key); - filter.add (key.getAddress ()); - addressSet.add (new ByteVector (key.getAddress ())); + Integer id = keyIDForAddress.get (new ByteVector (address)); + Key key = null; try { - api.registerFilteredListener (filter, this); + key = wallet.getKey (this, id); } - catch ( BCSAPIException e ) + catch ( ValidationException e ) { } return key; } @Override - public Key getKey (int ix) throws ValidationException + public Key getNextKey () throws ValidationException { - if ( ix >= keyForAddress.size () ) - { - throw new ValidationException ("Use consequtive keys"); - } - return extended.getKey (ix); + Key key = extended.getKey (nextKey); + keyIDForAddress.put (new ByteVector (key.getAddress ()), nextKey); + ++nextKey; + return key; } + @Override public boolean updateWithTransaction (Transaction t) { synchronized ( confirmed ) { boolean modified = false; - if ( !processedTransaction.containsKey (t.getHash ()) ) + if ( !processedTransaction.contains (t.getHash ()) ) { if ( t.isDoubleSpend () ) { return false; } - processedTransaction.put (t.getHash (), t); + processedTransaction.add (t.getHash ()); TransactionOutput spend = null; for ( TransactionInput i : t.getInputs () ) { @@ -211,6 +201,7 @@ public boolean updateWithTransaction (Transaction t) if ( spend != null ) { confirmed.remove (i.getSourceHash (), i.getIx ()); + log.trace ("Spend settled output " + i.getSourceHash () + " " + i.getIx () + " " + spend.getValue ()); } else { @@ -218,6 +209,7 @@ public boolean updateWithTransaction (Transaction t) if ( spend != null ) { change.remove (i.getSourceHash (), i.getIx ()); + log.trace ("Spend change output " + i.getSourceHash () + " " + i.getIx () + " " + spend.getValue ()); } else { @@ -225,6 +217,7 @@ public boolean updateWithTransaction (Transaction t) if ( spend != null ) { receiving.remove (i.getSourceHash (), i.getIx ()); + log.trace ("Spend receiving output " + i.getSourceHash () + " " + i.getIx () + " " + spend.getValue ()); } } } @@ -233,22 +226,25 @@ public boolean updateWithTransaction (Transaction t) long ix = 0; for ( TransactionOutput o : t.getOutputs () ) { - if ( addressSet.contains (new ByteVector (o.getOutputAddress ())) ) + if ( keyIDForAddress.containsKey (new ByteVector (o.getOutputAddress ())) ) { modified = true; if ( t.getBlockHash () != null ) { confirmed.add (t.getHash (), ix, o); + log.trace ("Settled " + ix + " " + o.getValue ()); } else { if ( spend != null ) { change.add (t.getHash (), ix, o); + log.trace ("Change " + ix + " " + o.getValue ()); } else { receiving.add (t.getHash (), ix, o); + log.trace ("Receiving " + ix + " " + o.getValue ()); } } } @@ -258,32 +254,77 @@ public boolean updateWithTransaction (Transaction t) { modified = true; sending.add (t.getHash (), ix, o); + log.trace ("Sending " + ix + " " + o.getValue ()); } } ++ix; } + if ( modified ) + { + relevantTransaction.put (t.getHash (), t); + } } else if ( t.isDoubleSpend () ) { for ( long ix = 0; ix < t.getOutputs ().size (); ++ix ) { - modified |= confirmed.remove (t.getHash (), ix); - modified |= change.remove (t.getHash (), ix); - modified |= receiving.remove (t.getHash (), ix); - modified |= sending.remove (t.getHash (), ix); + TransactionOutput out = null; + out = confirmed.remove (t.getHash (), ix); + if ( out == null ) + { + out = change.remove (t.getHash (), ix); + } + if ( out == null ) + { + out = receiving.remove (t.getHash (), ix); + } + if ( out == null ) + { + out = sending.remove (t.getHash (), ix); + } + if ( out != null ) + { + log.trace ("Remove double spend from account " + name + " tx: " + t.getHash () + " ix " + ix + " " + out.getValue ()); + } + modified |= out != null; } processedTransaction.remove (t.getHash ()); + relevantTransaction.remove (t.getHash ()); } - if ( modified ) - { - wallet.addTransaction (t); - log.trace ("Updated account " + name + " with " + t.getHash ()); - } - return modified; } } + @Override + public List getTransactions () + { + ArrayList tl = new ArrayList (); + tl.addAll (relevantTransaction.values ()); + Collections.sort (tl, new Comparator () + { + @Override + public int compare (Transaction a, Transaction b) + { + for ( TransactionInput in : b.getInputs () ) + { + if ( in.getSourceHash ().equals (a.getHash ()) ) + { + return -1; + } + } + for ( TransactionInput in : a.getInputs () ) + { + if ( in.getSourceHash ().equals (b.getHash ()) ) + { + return 1; + } + } + return 0; + } + }); + return tl; + } + @Override public void process (Transaction t) { diff --git a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java index 856b6d41..d600b35f 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java @@ -52,19 +52,19 @@ public TransactionOutput get (String tx, long ix) return null; } - public boolean remove (String tx, long ix) + public TransactionOutput remove (String tx, long ix) { HashMap outs = utxo.get (tx); if ( outs != null ) { - boolean removed = outs.remove (ix) != null; + TransactionOutput removed = outs.remove (ix); if ( outs.size () == 0 ) { utxo.remove (tx); } return removed; } - return false; + return null; } public long getTotal () diff --git a/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java deleted file mode 100644 index c92c2360..00000000 --- a/api/src/main/java/com/bitsofproof/supernode/api/SerializedWallet.java +++ /dev/null @@ -1,483 +0,0 @@ -/* - * Copyright 2013 bits of proof zrt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.bitsofproof.supernode.api; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; - -import javax.crypto.Cipher; -import javax.crypto.CipherInputStream; -import javax.crypto.CipherOutputStream; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -import org.bouncycastle.crypto.generators.SCrypt; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; -import com.bitsofproof.supernode.common.ValidationException; - -public class SerializedWallet implements Wallet -{ - private static final Logger log = LoggerFactory.getLogger (SerializedWallet.class); - - private String passphrase; - private String fileName; - private long timeStamp; - private boolean production; - private SecretKeySpec keyspec = null; - private final byte[] iv = new byte[16]; - - private BCSAPI api; - - public void setApi (BCSAPI api) - { - this.api = api; - } - - public long getTimeStamp () - { - return timeStamp; - } - - @Override - public void setTimeStamp (long time) - { - timeStamp = time; - } - - @Override - public AccountManager getAccountManager (String name) throws BCSAPIException - { - try - { - ExtendedKey extended = null; - - InMemoryAccountManager am = null; - for ( WalletKey key : getKeys () ) - { - if ( key.name.equals (name) ) - { - if ( key.am != null ) - { - return key.am; - } - extended = ExtendedKey.parse (key.key); - key.am = am = new InMemoryAccountManager (this, name, extended); - am.setApi (api); - am.init (10, 0); - break; - } - } - if ( extended == null ) - { - WalletKey wk = new WalletKey (); - extended = ExtendedKey.createNew (); - - wk.created = System.currentTimeMillis () / 1000; - wk.name = name; - wk.key = extended.serialize (production); - addKey (wk); - wk.am = am = new InMemoryAccountManager (this, name, extended); - am.setApi (api); - } - am.registerFilter (); - if ( getTimeStamp () > 0 ) - { - for ( Transaction t : getTransactions () ) - { - am.updateWithTransaction (t); - } - } - else - { - transactions.clear (); - } - final InMemoryAccountManager fam = am; - long ts = getTimeStamp (); - if ( ts > 0 ) - { - // go back a day to ensure possible re-orgs are included. - ts -= 60 * 60 * 24; - } - api.scanTransactions (am.getAddresses (), UpdateMode.all, ts, new TransactionListener () - { - @Override - public void process (Transaction t) - { - if ( fam.updateWithTransaction (t) ) - { - addTransaction (t); - } - } - }); - persist (); - am.addAccountListener (new AccountListener () - { - @Override - public void accountChanged (AccountManager account, Transaction t) - { - try - { - persist (); - } - catch ( BCSAPIException e ) - { - log.error ("Failed to persist wallet", e); - } - } - }); - return am; - } - catch ( ValidationException e ) - { - throw new BCSAPIException (e); - } - } - - public static SerializedWallet read (String fileName, String passphrase, boolean production) throws BCSAPIException - { - try - { - File f = new File (fileName); - if ( f.exists () ) - { - long timeStamp = f.lastModified () / 1000; - FileInputStream in = null; - try - { - in = new FileInputStream (f); - SerializedWallet wallet = readWallet (in, passphrase, production); - wallet.fileName = fileName; - wallet.passphrase = passphrase; - wallet.timeStamp = timeStamp; - wallet.production = production; - return wallet; - } - finally - { - in.close (); - } - } - else - { - SerializedWallet wallet = new SerializedWallet (); - wallet.timeStamp = System.currentTimeMillis () / 1000; - wallet.fileName = fileName; - wallet.passphrase = passphrase; - wallet.production = production; - return wallet; - } - } - catch ( FileNotFoundException e ) - { - throw new BCSAPIException (e); - } - catch ( IOException e ) - { - throw new BCSAPIException (e); - } - catch ( ValidationException e ) - { - throw new BCSAPIException (e); - } - } - - @Override - public void persist () throws BCSAPIException - { - try - { - if ( fileName != null ) - { - File f = new File (fileName); - File tmp = File.createTempFile ("tmp", ".wallet", f.getParentFile ()); - tmp.setReadable (true, true); - tmp.setWritable (true, true); - FileOutputStream out = new FileOutputStream (tmp); - writeWallet (out, passphrase); - out.close (); - f.delete (); - tmp.renameTo (f); - timeStamp = f.lastModified () / 1000; - } - } - catch ( IOException e ) - { - throw new BCSAPIException (e); - } - catch ( ValidationException e ) - { - throw new BCSAPIException (e); - } - } - - public static class WalletKey - { - String key; - String name; - long created; - int nextSequence; - AccountManager am; - } - - private List keys = new ArrayList (); - private final Set hasTransaction = new HashSet (); - private List transactions = new ArrayList (); - private Map addresses = new HashMap (); - - private static final String MAGIC = "bopwallet"; - - private static SecureRandom rnd = new SecureRandom (); - - public void addKey (WalletKey key) - { - keys.add (key); - } - - @Override - public void addTransaction (Transaction t) - { - if ( !hasTransaction.contains (t.getHash ()) ) - { - transactions.add (t); - hasTransaction.add (t.getHash ()); - } - else - { - for ( Transaction s : transactions ) - { - if ( s.getHash ().equals (t.getHash ()) ) - { - s.setBlockHash (t.getBlockHash ()); - } - } - } - } - - public void addAddress (String name, String address) - { - addresses.put (name, address); - } - - public List getKeys () - { - return keys; - } - - public List getTransactions () - { - return Collections.unmodifiableList (transactions); - } - - public Map getAddresses () - { - return addresses; - } - - public void writeWallet (OutputStream output, String passphrase) throws ValidationException - { - try - { - output.write (MAGIC.getBytes ("US-ASCII")); - if ( keyspec == null || !passphrase.equals (this.passphrase) ) - { - this.passphrase = passphrase; - rnd.nextBytes (iv); - byte[] derived = SCrypt.generate (passphrase.getBytes ("UTF-8"), iv, 16384, 8, 8, 32); - keyspec = new SecretKeySpec (derived, "AES"); - } - output.write (iv); - Cipher cipher = Cipher.getInstance ("AES/CBC/PKCS5Padding", "BC"); - cipher.init (Cipher.ENCRYPT_MODE, keyspec, new IvParameterSpec (iv)); - CipherOutputStream cip = new CipherOutputStream (output, cipher); - GZIPOutputStream zip = new GZIPOutputStream (cip); - BCSAPIMessage.Wallet.Builder builder = BCSAPIMessage.Wallet.newBuilder (); - builder.setBcsapiversion (1); - for ( WalletKey key : keys ) - { - BCSAPIMessage.Wallet.Key.Builder kb = BCSAPIMessage.Wallet.Key.newBuilder (); - kb.setKey (key.key); - kb.setNextSequence (key.am != null ? key.am.getNextSequence () : 0); - if ( key.name != null ) - { - kb.setName (key.name); - } - if ( key.created != 0 ) - { - kb.setCreated (key.created); - } - builder.addKeys (kb.build ()); - } - for ( Transaction t : transactions ) - { - builder.addTransactions (t.toProtobuf ()); - } - for ( Map.Entry e : addresses.entrySet () ) - { - BCSAPIMessage.Wallet.PublicAddress.Builder pa = BCSAPIMessage.Wallet.PublicAddress.newBuilder (); - pa.setName (e.getKey ()); - pa.setAddress (e.getValue ()); - builder.addPublicAddresses (pa.build ()); - } - builder.build ().writeTo (zip); - zip.finish (); - zip.flush (); - cip.close (); - } - catch ( UnsupportedEncodingException e ) - { - } - catch ( IOException e ) - { - throw new ValidationException (e); - } - catch ( NoSuchAlgorithmException e ) - { - throw new ValidationException (e); - } - catch ( NoSuchProviderException e ) - { - throw new ValidationException (e); - } - catch ( NoSuchPaddingException e ) - { - throw new ValidationException (e); - } - catch ( InvalidKeyException e ) - { - throw new ValidationException (e); - } - catch ( InvalidAlgorithmParameterException e ) - { - throw new ValidationException (e); - } - } - - public static SerializedWallet readWallet (InputStream input, String passphrase, boolean production) throws ValidationException - { - SerializedWallet wallet = new SerializedWallet (); - - try - { - byte[] derived; - byte[] magic = new byte[MAGIC.getBytes ("US-ASCII").length]; - input.read (magic); - if ( !Arrays.equals (magic, MAGIC.getBytes ("US-ASCII")) ) - { - throw new ValidationException ("Not a wallet"); - } - input.read (wallet.iv); - derived = SCrypt.generate (passphrase.getBytes ("UTF-8"), wallet.iv, 16384, 8, 8, 32); - wallet.keyspec = new SecretKeySpec (derived, "AES"); - Cipher cipher = Cipher.getInstance ("AES/CBC/PKCS5Padding", "BC"); - cipher.init (Cipher.DECRYPT_MODE, wallet.keyspec, new IvParameterSpec (wallet.iv)); - - CipherInputStream cis = new CipherInputStream (input, cipher); - GZIPInputStream unzip = new GZIPInputStream (cis); - - BCSAPIMessage.Wallet walletMessage = BCSAPIMessage.Wallet.parseFrom (unzip); - - wallet.keys = new ArrayList (); - for ( BCSAPIMessage.Wallet.Key k : walletMessage.getKeysList () ) - { - WalletKey key = new WalletKey (); - key.key = k.getKey (); - if ( production && !key.key.startsWith ("x") ) - { - throw new ValidationException ("Expected production keys"); - } - if ( !production && !key.key.startsWith ("t") ) - { - throw new ValidationException ("Expected test keys"); - } - key.nextSequence = k.getNextSequence (); - if ( k.hasName () ) - { - key.name = k.getName (); - } - if ( k.hasCreated () ) - { - key.created = k.getCreated (); - } - wallet.keys.add (key); - } - wallet.transactions = new ArrayList (); - for ( BCSAPIMessage.Transaction t : walletMessage.getTransactionsList () ) - { - Transaction transaction = Transaction.fromProtobuf (t); - transaction.computeHash (); - wallet.addTransaction (transaction); - } - wallet.addresses = new HashMap (); - for ( BCSAPIMessage.Wallet.PublicAddress p : walletMessage.getPublicAddressesList () ) - { - wallet.addresses.put (p.getName (), p.getAddress ()); - } - } - catch ( UnsupportedEncodingException e ) - { - } - catch ( NoSuchAlgorithmException e ) - { - throw new ValidationException (e); - } - catch ( NoSuchProviderException e ) - { - throw new ValidationException (e); - } - catch ( NoSuchPaddingException e ) - { - throw new ValidationException (e); - } - catch ( InvalidKeyException e ) - { - throw new ValidationException (e); - } - catch ( IOException e ) - { - throw new ValidationException (e); - } - catch ( InvalidAlgorithmParameterException e ) - { - throw new ValidationException (e); - } - return wallet; - } -} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/SimpleWalletFactory.java b/api/src/main/java/com/bitsofproof/supernode/api/SimpleWalletFactory.java deleted file mode 100644 index a8050d2b..00000000 --- a/api/src/main/java/com/bitsofproof/supernode/api/SimpleWalletFactory.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.bitsofproof.supernode.api; - - -public class SimpleWalletFactory implements WalletFactory -{ - - private BCSAPI api; - - public void setApi (BCSAPI api) - { - this.api = api; - } - - @Override - public Wallet getWallet (String fileName, String passphrase) throws BCSAPIException - { - SerializedWallet wallet = SerializedWallet.read (fileName, passphrase, api.isProduction ()); - wallet.setApi (api); - return wallet; - } - -} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java b/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java index b074d9be..6969ebd8 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java @@ -1,14 +1,27 @@ package com.bitsofproof.supernode.api; +import java.io.IOException; + +import com.bitsofproof.supernode.common.Key; +import com.bitsofproof.supernode.common.ValidationException; public interface Wallet { + public void setApi (BCSAPI api); + + public void init (String passphrase); + + public void unlock (String passphrase) throws ValidationException; + + public void lock (); + + public AccountManager getAccountManager (String name); - public AccountManager getAccountManager (String name) throws BCSAPIException; + public AccountManager createAccountManager (String name) throws ValidationException, IOException; - public void addTransaction (Transaction t); + public void read (String name, int lookAhead) throws IOException, ValidationException, BCSAPIException; - public void persist () throws BCSAPIException; + public void persist () throws IOException; - public void setTimeStamp (long time); + public Key getKey (AccountManager am, int sequence) throws ValidationException; } \ No newline at end of file diff --git a/api/src/main/protobuf/BCSAPIMessage.proto b/api/src/main/protobuf/BCSAPIMessage.proto index 8e4083e7..af1e918e 100644 --- a/api/src/main/protobuf/BCSAPIMessage.proto +++ b/api/src/main/protobuf/BCSAPIMessage.proto @@ -120,19 +120,15 @@ message AccountRequest message Wallet { required uint32 bcsapiversion = 1; - message Key + required bytes encryptedSeed = 2; + required bytes signature = 3; + message Account { - required string key = 1; - required uint32 nextSequence = 2; - optional string name = 3; - optional uint64 created = 4; + required string publicKey = 1; + repeated Transaction transactions = 2; + required uint32 nextSequence = 3; + required uint64 created = 4; + required string name = 5; } - message PublicAddress - { - required string address = 1; - required string name = 2; - } - repeated Key keys = 2; - repeated PublicAddress publicAddresses = 3; - repeated Transaction transactions = 4; + repeated Account accounts = 4; } \ No newline at end of file diff --git a/api/src/test/java/com/bitsofproof/supernode/api/WalletFormatTest.java b/api/src/test/java/com/bitsofproof/supernode/api/WalletFormatTest.java index 95d4bcfc..ad034515 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/WalletFormatTest.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/WalletFormatTest.java @@ -15,26 +15,13 @@ */ package com.bitsofproof.supernode.api; -import static org.junit.Assert.assertTrue; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.security.Security; -import java.util.HashMap; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.BeforeClass; import org.junit.Test; -import com.bitsofproof.supernode.api.AddressConverter; -import com.bitsofproof.supernode.api.ExtendedKey; -import com.bitsofproof.supernode.api.SerializedWallet; -import com.bitsofproof.supernode.api.Transaction; -import com.bitsofproof.supernode.common.ByteVector; -import com.bitsofproof.supernode.common.Key; -import com.bitsofproof.supernode.common.ScriptFormat; import com.bitsofproof.supernode.common.ValidationException; -import com.bitsofproof.supernode.common.ScriptFormat.Token; public class WalletFormatTest { @@ -48,38 +35,5 @@ public static void init () @Test public void testInOut () throws ValidationException { - SerializedWallet sw = new SerializedWallet (); - for ( int i = 0; i < 5; ++i ) - { - SerializedWallet.WalletKey wk = new SerializedWallet.WalletKey (); - ExtendedKey k = ExtendedKey.createNew (); - wk.key = k.serialize (true); - wk.created = System.currentTimeMillis () / 1000; - wk.nextSequence = 0; - sw.addKey (wk); - sw.addTransaction (Transaction.createCoinbase (k.getKey (i), 5000000, i)); - sw.addAddress ("coinbase" + i, AddressConverter.toSatoshiStyle (k.getKey (i).getAddress (), 0x0)); - } - - ByteArrayOutputStream out = new ByteArrayOutputStream (); - sw.writeWallet (out, "passphrase"); - SerializedWallet read = SerializedWallet.readWallet (new ByteArrayInputStream (out.toByteArray ()), "passphrase", true); - HashMap keyForAddress = new HashMap (); - for ( int i = 0; i < 5; ++i ) - { - assertTrue (read.getKeys ().get (i).key.equals (sw.getKeys ().get (i).key)); - ExtendedKey k = ExtendedKey.parse (read.getKeys ().get (i).key); - keyForAddress.put (new ByteVector (k.getKey (i).getAddress ()), k.getKey (i)); - } - for ( Transaction t : read.getTransactions () ) - { - for ( Token token : ScriptFormat.parse (t.getOutputs ().get (0).getScript ()) ) - { - if ( token.data != null ) - { - assertTrue (keyForAddress.get (new ByteVector (token.data)) != null); - } - } - } } } diff --git a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java index fb8b63c2..33f4320d 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java @@ -18,6 +18,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import java.io.IOException; import java.math.BigInteger; import java.security.Security; import java.util.ArrayList; @@ -40,7 +41,7 @@ import com.bitsofproof.supernode.api.BCSAPI; import com.bitsofproof.supernode.api.BCSAPIException; import com.bitsofproof.supernode.api.Block; -import com.bitsofproof.supernode.api.SerializedWallet; +import com.bitsofproof.supernode.api.FileWallet; import com.bitsofproof.supernode.api.Transaction; import com.bitsofproof.supernode.api.TransactionListener; import com.bitsofproof.supernode.api.TrunkListener; @@ -170,14 +171,17 @@ public static void provider () } @Test - public void test () throws BCSAPIException, ValidationException + public void test () throws BCSAPIException, ValidationException, IOException { store.resetStore (chain); store.cache (chain, 0); - wallet = new SerializedWallet (); - ((SerializedWallet) wallet).setApi (api); - alice = wallet.getAccountManager ("Alice"); - bob = wallet.getAccountManager ("Bob"); + wallet = new FileWallet ("test.wallet"); + wallet.init ("passphrase"); + wallet.setApi (api); + wallet.unlock ("passphrase"); + + alice = wallet.createAccountManager ("Alice"); + bob = wallet.createAccountManager ("Bob"); alice.addAccountListener (aliceMonitor); bob.addAccountListener (bobMonitor); From 9e210669f0428f5d6fc7cd419ee83fee3a633b9f Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Sun, 16 Jun 2013 16:52:09 +0200 Subject: [PATCH 049/299] register transaction listener for new account manager --- .../main/java/com/bitsofproof/supernode/api/FileWallet.java | 3 ++- api/src/main/java/com/bitsofproof/supernode/api/Wallet.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java index add90343..d9b98c3a 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java @@ -116,7 +116,7 @@ public synchronized AccountManager getAccountManager (String name) } @Override - public synchronized AccountManager createAccountManager (String name) throws ValidationException, IOException + public synchronized AccountManager createAccountManager (String name) throws ValidationException, IOException, BCSAPIException { if ( accounts.containsKey (name) ) { @@ -131,6 +131,7 @@ public synchronized AccountManager createAccountManager (String name) throws Val InMemoryAccountManager account = new InMemoryAccountManager (this, name, master.getChild (accounts.size () | 0x80000000), 0, System.currentTimeMillis () / 1000); account.setApi (api); + api.registerTransactionListener (account); accounts.put (name, account); persist (); return account; diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java b/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java index 6969ebd8..aa5433ab 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java @@ -17,7 +17,7 @@ public interface Wallet public AccountManager getAccountManager (String name); - public AccountManager createAccountManager (String name) throws ValidationException, IOException; + public AccountManager createAccountManager (String name) throws ValidationException, IOException, BCSAPIException; public void read (String name, int lookAhead) throws IOException, ValidationException, BCSAPIException; From 1ff60eeea9ae698fdf64c36581849a79e1319eed Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Sun, 16 Jun 2013 18:13:51 +0200 Subject: [PATCH 050/299] interpret after also as height in scans fix scan chain to include latest block --- .../com/bitsofproof/supernode/core/CachedBlockStore.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 5bd29f4f..9f9387e4 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -401,6 +401,11 @@ else if ( blockChain.getLast () != p ) blockChain.removeLast (); appendList.add (q); } + else + { + appendList.add (q); + break; + } q = p; p = q.previous; } @@ -430,7 +435,7 @@ public void filterTransactions (Set matchSet, ExtendedKey ek, int lo for ( CachedBlock cb : blockChain ) { - if ( cb.time <= after ) + if ( after > 1231006505 && cb.time <= after || after < 1231006505 && cb.height <= after ) { continue; } @@ -518,7 +523,7 @@ public void filterTransactions (Set matchSet, UpdateMode update, lon for ( CachedBlock cb : blockChain ) { - if ( cb.time <= after ) + if ( after > 1231006505 && cb.time <= after || after < 1231006505 && cb.height <= after ) { continue; } From 6927012091d9a5dcb8c75a57ebd3316e729f7de4 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Sun, 16 Jun 2013 18:15:20 +0200 Subject: [PATCH 051/299] send transaction height --- .../supernode/api/BCSAPIMessage.java | 122 +++++++++++++----- .../bitsofproof/supernode/api/FileWallet.java | 13 +- .../supernode/api/Transaction.java | 19 +++ .../com/bitsofproof/supernode/api/Wallet.java | 2 +- api/src/main/protobuf/BCSAPIMessage.proto | 1 + .../supernode/core/ImplementBCSAPI.java | 1 + 6 files changed, 122 insertions(+), 36 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java index 4293a88b..962786b3 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java @@ -1995,6 +1995,10 @@ com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutputOrBuilder getOutput // optional bool doubleSpend = 8; boolean hasDoubleSpend(); boolean getDoubleSpend(); + + // optional uint32 height = 9; + boolean hasHeight(); + int getHeight(); } public static final class Transaction extends com.google.protobuf.GeneratedMessage @@ -2117,6 +2121,16 @@ public boolean getDoubleSpend() { return doubleSpend_; } + // optional uint32 height = 9; + public static final int HEIGHT_FIELD_NUMBER = 9; + private int height_; + public boolean hasHeight() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + public int getHeight() { + return height_; + } + private void initFields() { bcsapiversion_ = 0; version_ = 0; @@ -2125,6 +2139,7 @@ private void initFields() { outputs_ = java.util.Collections.emptyList(); block_ = com.google.protobuf.ByteString.EMPTY; doubleSpend_ = false; + height_ = 0; } private byte memoizedIsInitialized = -1; public final boolean isInitialized() { @@ -2183,6 +2198,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (((bitField0_ & 0x00000010) == 0x00000010)) { output.writeBool(8, doubleSpend_); } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeUInt32(9, height_); + } getUnknownFields().writeTo(output); } @@ -2220,6 +2238,10 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeBoolSize(8, doubleSpend_); } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(9, height_); + } size += getUnknownFields().getSerializedSize(); memoizedSerializedSize = size; return size; @@ -2368,6 +2390,8 @@ public Builder clear() { bitField0_ = (bitField0_ & ~0x00000020); doubleSpend_ = false; bitField0_ = (bitField0_ & ~0x00000040); + height_ = 0; + bitField0_ = (bitField0_ & ~0x00000080); return this; } @@ -2444,6 +2468,10 @@ public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction buildPartial() { to_bitField0_ |= 0x00000010; } result.doubleSpend_ = doubleSpend_; + if (((from_bitField0_ & 0x00000080) == 0x00000080)) { + to_bitField0_ |= 0x00000020; + } + result.height_ = height_; result.bitField0_ = to_bitField0_; onBuilt(); return result; @@ -2527,6 +2555,9 @@ public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.Transaction if (other.hasDoubleSpend()) { setDoubleSpend(other.getDoubleSpend()); } + if (other.hasHeight()) { + setHeight(other.getHeight()); + } this.mergeUnknownFields(other.getUnknownFields()); return this; } @@ -2619,6 +2650,11 @@ public Builder mergeFrom( doubleSpend_ = input.readBool(); break; } + case 72: { + bitField0_ |= 0x00000080; + height_ = input.readUInt32(); + break; + } } } } @@ -3105,6 +3141,27 @@ public Builder clearDoubleSpend() { return this; } + // optional uint32 height = 9; + private int height_ ; + public boolean hasHeight() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + public int getHeight() { + return height_; + } + public Builder setHeight(int value) { + bitField0_ |= 0x00000080; + height_ = value; + onChanged(); + return this; + } + public Builder clearHeight() { + bitField0_ = (bitField0_ & ~0x00000080); + height_ = 0; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.Transaction) } @@ -10252,42 +10309,43 @@ public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder addAcc "\006source\030\001 \002(\014\022\020\n\010sourceix\030\002 \002(\r\022\020\n\010seque" + "nce\030\003 \002(\r\022\016\n\006script\030\004 \002(\014\"A\n\021Transaction" + "Output\022\r\n\005value\030\001 \002(\004\022\016\n\006script\030\002 \002(\014\022\r\n" + - "\005color\030\003 \001(\014\"\357\001\n\013Transaction\022\025\n\rbcsapive" + + "\005color\030\003 \001(\014\"\377\001\n\013Transaction\022\025\n\rbcsapive" + "rsion\030\001 \002(\r\022\017\n\007version\030\003 \002(\r\022\020\n\010locktime", "\030\004 \002(\r\022?\n\006inputs\030\005 \003(\0132/.com.bitsofproof" + ".supernode.api.TransactionInput\022A\n\007outpu" + "ts\030\006 \003(\01320.com.bitsofproof.supernode.api" + ".TransactionOutput\022\r\n\005block\030\007 \001(\014\022\023\n\013dou" + - "bleSpend\030\010 \001(\010\"\342\001\n\005Block\022\025\n\rbcsapiversio" + - "n\030\001 \002(\r\022\017\n\007version\030\002 \002(\r\022\025\n\rpreviousBloc" + - "k\030\003 \002(\014\022\022\n\nmerkleRoot\030\004 \002(\014\022\021\n\ttimestamp" + - "\030\005 \002(\r\022\022\n\ndifficulty\030\006 \002(\r\022\r\n\005nonce\030\007 \002(" + - "\r\022@\n\014transactions\030\010 \003(\0132*.com.bitsofproo" + - "f.supernode.api.Transaction\022\016\n\006height\030\t ", - "\001(\r\"\220\001\n\013TrunkUpdate\022\025\n\rbcsapiversion\030\001 \002" + - "(\r\0223\n\005added\030\002 \003(\0132$.com.bitsofproof.supe" + - "rnode.api.Block\0225\n\007removed\030\003 \003(\0132$.com.b" + - "itsofproof.supernode.api.Block\"+\n\004Hash\022\025" + - "\n\rbcsapiversion\030\001 \002(\r\022\014\n\004hash\030\002 \003(\014\":\n\020E" + - "xceptionMessage\022\025\n\rbcsapiversion\030\001 \002(\r\022\017" + - "\n\007message\030\002 \003(\t\"r\n\005Color\022\023\n\013transaction\030" + - "\001 \002(\014\022\r\n\005terms\030\002 \002(\t\022\014\n\004unit\030\003 \002(\004\022\024\n\014ex" + - "piryHeight\030\004 \002(\r\022\016\n\006pubkey\030\005 \002(\014\022\021\n\tsign" + - "ature\030\006 \002(\014\"j\n\rFilterRequest\022\025\n\rbcsapive", - "rsion\030\001 \002(\r\022\016\n\006filter\030\002 \002(\014\022\025\n\rhashFunct" + - "ions\030\003 \002(\r\022\r\n\005tweak\030\004 \002(\r\022\014\n\004mode\030\005 \002(\r\"" + - "V\n\021ExactMatchRequest\022\025\n\rbcsapiversion\030\001 " + - "\002(\r\022\014\n\004mode\030\002 \002(\r\022\r\n\005match\030\003 \003(\014\022\r\n\005afte" + - "r\030\004 \001(\004\"\\\n\016AccountRequest\022\025\n\rbcsapiversi" + - "on\030\001 \002(\r\022\021\n\tpublicKey\030\002 \002(\t\022\021\n\tlookAhead" + - "\030\003 \002(\r\022\r\n\005after\030\004 \001(\004\"\240\002\n\006Wallet\022\025\n\rbcsa" + - "piversion\030\001 \002(\r\022\025\n\rencryptedSeed\030\002 \002(\014\022\021" + - "\n\tsignature\030\003 \002(\014\022?\n\010accounts\030\004 \003(\0132-.co" + - "m.bitsofproof.supernode.api.Wallet.Accou", - "nt\032\223\001\n\007Account\022\021\n\tpublicKey\030\001 \002(\t\022@\n\014tra" + - "nsactions\030\002 \003(\0132*.com.bitsofproof.supern" + - "ode.api.Transaction\022\024\n\014nextSequence\030\003 \002(" + - "\r\022\017\n\007created\030\004 \002(\004\022\014\n\004name\030\005 \002(\t" + "bleSpend\030\010 \001(\010\022\016\n\006height\030\t \001(\r\"\342\001\n\005Block" + + "\022\025\n\rbcsapiversion\030\001 \002(\r\022\017\n\007version\030\002 \002(\r" + + "\022\025\n\rpreviousBlock\030\003 \002(\014\022\022\n\nmerkleRoot\030\004 " + + "\002(\014\022\021\n\ttimestamp\030\005 \002(\r\022\022\n\ndifficulty\030\006 \002" + + "(\r\022\r\n\005nonce\030\007 \002(\r\022@\n\014transactions\030\010 \003(\0132" + + "*.com.bitsofproof.supernode.api.Transact", + "ion\022\016\n\006height\030\t \001(\r\"\220\001\n\013TrunkUpdate\022\025\n\rb" + + "csapiversion\030\001 \002(\r\0223\n\005added\030\002 \003(\0132$.com." + + "bitsofproof.supernode.api.Block\0225\n\007remov" + + "ed\030\003 \003(\0132$.com.bitsofproof.supernode.api" + + ".Block\"+\n\004Hash\022\025\n\rbcsapiversion\030\001 \002(\r\022\014\n" + + "\004hash\030\002 \003(\014\":\n\020ExceptionMessage\022\025\n\rbcsap" + + "iversion\030\001 \002(\r\022\017\n\007message\030\002 \003(\t\"r\n\005Color" + + "\022\023\n\013transaction\030\001 \002(\014\022\r\n\005terms\030\002 \002(\t\022\014\n\004" + + "unit\030\003 \002(\004\022\024\n\014expiryHeight\030\004 \002(\r\022\016\n\006pubk" + + "ey\030\005 \002(\014\022\021\n\tsignature\030\006 \002(\014\"j\n\rFilterReq", + "uest\022\025\n\rbcsapiversion\030\001 \002(\r\022\016\n\006filter\030\002 " + + "\002(\014\022\025\n\rhashFunctions\030\003 \002(\r\022\r\n\005tweak\030\004 \002(" + + "\r\022\014\n\004mode\030\005 \002(\r\"V\n\021ExactMatchRequest\022\025\n\r" + + "bcsapiversion\030\001 \002(\r\022\014\n\004mode\030\002 \002(\r\022\r\n\005mat" + + "ch\030\003 \003(\014\022\r\n\005after\030\004 \001(\004\"\\\n\016AccountReques" + + "t\022\025\n\rbcsapiversion\030\001 \002(\r\022\021\n\tpublicKey\030\002 " + + "\002(\t\022\021\n\tlookAhead\030\003 \002(\r\022\r\n\005after\030\004 \001(\004\"\240\002" + + "\n\006Wallet\022\025\n\rbcsapiversion\030\001 \002(\r\022\025\n\rencry" + + "ptedSeed\030\002 \002(\014\022\021\n\tsignature\030\003 \002(\014\022?\n\010acc" + + "ounts\030\004 \003(\0132-.com.bitsofproof.supernode.", + "api.Wallet.Account\032\223\001\n\007Account\022\021\n\tpublic" + + "Key\030\001 \002(\t\022@\n\014transactions\030\002 \003(\0132*.com.bi" + + "tsofproof.supernode.api.Transaction\022\024\n\014n" + + "extSequence\030\003 \002(\r\022\017\n\007created\030\004 \002(\004\022\014\n\004na" + + "me\030\005 \002(\t" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -10331,7 +10389,7 @@ public com.google.protobuf.ExtensionRegistry assignDescriptors( internal_static_com_bitsofproof_supernode_api_Transaction_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_com_bitsofproof_supernode_api_Transaction_descriptor, - new java.lang.String[] { "Bcsapiversion", "Version", "Locktime", "Inputs", "Outputs", "Block", "DoubleSpend", }, + new java.lang.String[] { "Bcsapiversion", "Version", "Locktime", "Inputs", "Outputs", "Block", "DoubleSpend", "Height", }, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.class, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder.class); internal_static_com_bitsofproof_supernode_api_Block_descriptor = diff --git a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java index d9b98c3a..3e93651d 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java @@ -9,6 +9,7 @@ import java.util.HashMap; import java.util.Map; +import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ValidationException; import com.google.protobuf.ByteString; @@ -96,11 +97,13 @@ public void read (String fileName, int lookAhead) throws IOException, Validation account.getCreated ()); am.setApi (api); accounts.put (account.getName (), am); + int txHeight = 0; for ( BCSAPIMessage.Transaction t : account.getTransactionsList () ) { am.updateWithTransaction (Transaction.fromProtobuf (t)); + txHeight = Math.max (txHeight, t.getHeight ()); } - am.sync (lookAhead, account.getCreated ()); + am.sync (lookAhead, txHeight == 0 ? account.getCreated () : txHeight); } } finally @@ -152,11 +155,12 @@ public Key getKey (AccountManager am, int sequence) throws ValidationException } @Override - public void persist () throws IOException + public void persist () throws IOException, BCSAPIException { FileOutputStream out = new FileOutputStream (fileName); try { + int headHeight = api.getBlock (Hash.ZERO_HASH_STRING).getHeight (); BCSAPIMessage.Wallet.Builder builder = BCSAPIMessage.Wallet.newBuilder (); builder.setBcsapiversion (1); builder.setEncryptedSeed (ByteString.copyFrom (encryptedSeed)); @@ -170,7 +174,10 @@ public void persist () throws IOException ab.setPublicKey (am.getMasterKey ().getReadOnly ().serialize (true)); for ( Transaction t : am.getTransactions () ) { - ab.addTransactions (t.toProtobuf ()); + if ( t.getHeight () < headHeight - 100 ) + { + ab.addTransactions (t.toProtobuf ()); + } } builder.addAccounts (ab.build ()); } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java b/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java index 6d884fc8..c7be4d4a 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java @@ -42,6 +42,7 @@ public class Transaction implements Serializable, Cloneable private String hash; private String blockHash; private boolean doubleSpend = false; + private int height = 0; private List inputs; private List outputs; @@ -379,6 +380,16 @@ public void setDoubleSpend (boolean doubleSpend) this.doubleSpend = doubleSpend; } + public int getHeight () + { + return height; + } + + public void setHeight (int height) + { + this.height = height; + } + public void toWire (WireFormat.Writer writer) { writer.writeUint32 (version); @@ -525,6 +536,10 @@ public BCSAPIMessage.Transaction toProtobuf () { builder.setDoubleSpend (true); } + if ( height != 0 ) + { + builder.setHeight (height); + } return builder.build (); } @@ -558,6 +573,10 @@ public static Transaction fromProtobuf (BCSAPIMessage.Transaction pt) { transaction.doubleSpend = true; } + if ( pt.hasHeight () ) + { + transaction.height = pt.getHeight (); + } return transaction; } } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java b/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java index aa5433ab..bac7b2f9 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java @@ -21,7 +21,7 @@ public interface Wallet public void read (String name, int lookAhead) throws IOException, ValidationException, BCSAPIException; - public void persist () throws IOException; + public void persist () throws IOException, BCSAPIException; public Key getKey (AccountManager am, int sequence) throws ValidationException; } \ No newline at end of file diff --git a/api/src/main/protobuf/BCSAPIMessage.proto b/api/src/main/protobuf/BCSAPIMessage.proto index af1e918e..f5e37790 100644 --- a/api/src/main/protobuf/BCSAPIMessage.proto +++ b/api/src/main/protobuf/BCSAPIMessage.proto @@ -51,6 +51,7 @@ message Transaction { repeated TransactionOutput outputs = 6; optional bytes block = 7; optional bool doubleSpend = 8; // set if this transaction IS a double spend and should be removed/ignored + optional uint32 height = 9; } // a block. diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index a22df349..ff494939 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -955,6 +955,7 @@ private Transaction toBCSAPITransaction (Tx tx, boolean doubleSpend) if ( tx.getBlockHash () != null ) { transaction.setBlockHash (tx.getBlockHash ()); + transaction.setHeight (store.getBlockHeight (tx.getBlockHash ())); } if ( doubleSpend ) { From fc14a9fbbe22ad13f0f557602ecb1cad3f6ec4aa Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Mon, 17 Jun 2013 04:39:52 +0200 Subject: [PATCH 052/299] start with key 0 to nextKey + lookAhead --- .../com/bitsofproof/supernode/api/InMemoryAccountManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java index e474020f..50511eec 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java @@ -83,7 +83,7 @@ public InMemoryAccountManager (Wallet wallet, String name, ExtendedKey extended, public void sync (final int lookAhead, long after) throws BCSAPIException, ValidationException { - for ( int i = nextKey; i < nextKey + lookAhead; ++i ) + for ( int i = 0; i < nextKey + lookAhead; ++i ) { Key key = extended.getKey (i); keyIDForAddress.put (new ByteVector (key.getAddress ()), i); From e7ebe6e7544082e439b472393636b8cdf8d4e903 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 18 Jun 2013 09:15:31 +0200 Subject: [PATCH 053/299] make default wallet read only except creating new accounts --- .../supernode/api/BCSAPIMessage.java | 421 ++---------------- .../bitsofproof/supernode/api/FileWallet.java | 23 +- .../supernode/api/InMemoryAccountManager.java | 3 +- .../supernode/api/JMSServerConnector.java | 6 +- api/src/main/protobuf/BCSAPIMessage.proto | 6 +- 5 files changed, 43 insertions(+), 416 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java index 962786b3..7716124d 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java @@ -8610,25 +8610,11 @@ public interface AccountOrBuilder boolean hasPublicKey(); String getPublicKey(); - // repeated .com.bitsofproof.supernode.api.Transaction transactions = 2; - java.util.List - getTransactionsList(); - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction getTransactions(int index); - int getTransactionsCount(); - java.util.List - getTransactionsOrBuilderList(); - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder getTransactionsOrBuilder( - int index); - - // required uint32 nextSequence = 3; - boolean hasNextSequence(); - int getNextSequence(); - - // required uint64 created = 4; + // required uint64 created = 2; boolean hasCreated(); long getCreated(); - // required string name = 5; + // required string name = 3; boolean hasName(); String getName(); } @@ -8693,52 +8679,21 @@ private com.google.protobuf.ByteString getPublicKeyBytes() { } } - // repeated .com.bitsofproof.supernode.api.Transaction transactions = 2; - public static final int TRANSACTIONS_FIELD_NUMBER = 2; - private java.util.List transactions_; - public java.util.List getTransactionsList() { - return transactions_; - } - public java.util.List - getTransactionsOrBuilderList() { - return transactions_; - } - public int getTransactionsCount() { - return transactions_.size(); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction getTransactions(int index) { - return transactions_.get(index); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder getTransactionsOrBuilder( - int index) { - return transactions_.get(index); - } - - // required uint32 nextSequence = 3; - public static final int NEXTSEQUENCE_FIELD_NUMBER = 3; - private int nextSequence_; - public boolean hasNextSequence() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public int getNextSequence() { - return nextSequence_; - } - - // required uint64 created = 4; - public static final int CREATED_FIELD_NUMBER = 4; + // required uint64 created = 2; + public static final int CREATED_FIELD_NUMBER = 2; private long created_; public boolean hasCreated() { - return ((bitField0_ & 0x00000004) == 0x00000004); + return ((bitField0_ & 0x00000002) == 0x00000002); } public long getCreated() { return created_; } - // required string name = 5; - public static final int NAME_FIELD_NUMBER = 5; + // required string name = 3; + public static final int NAME_FIELD_NUMBER = 3; private java.lang.Object name_; public boolean hasName() { - return ((bitField0_ & 0x00000008) == 0x00000008); + return ((bitField0_ & 0x00000004) == 0x00000004); } public String getName() { java.lang.Object ref = name_; @@ -8768,8 +8723,6 @@ private com.google.protobuf.ByteString getNameBytes() { private void initFields() { publicKey_ = ""; - transactions_ = java.util.Collections.emptyList(); - nextSequence_ = 0; created_ = 0L; name_ = ""; } @@ -8782,10 +8735,6 @@ public final boolean isInitialized() { memoizedIsInitialized = 0; return false; } - if (!hasNextSequence()) { - memoizedIsInitialized = 0; - return false; - } if (!hasCreated()) { memoizedIsInitialized = 0; return false; @@ -8794,12 +8743,6 @@ public final boolean isInitialized() { memoizedIsInitialized = 0; return false; } - for (int i = 0; i < getTransactionsCount(); i++) { - if (!getTransactions(i).isInitialized()) { - memoizedIsInitialized = 0; - return false; - } - } memoizedIsInitialized = 1; return true; } @@ -8810,17 +8753,11 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (((bitField0_ & 0x00000001) == 0x00000001)) { output.writeBytes(1, getPublicKeyBytes()); } - for (int i = 0; i < transactions_.size(); i++) { - output.writeMessage(2, transactions_.get(i)); - } if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt32(3, nextSequence_); + output.writeUInt64(2, created_); } if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeUInt64(4, created_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(5, getNameBytes()); + output.writeBytes(3, getNameBytes()); } getUnknownFields().writeTo(output); } @@ -8835,21 +8772,13 @@ public int getSerializedSize() { size += com.google.protobuf.CodedOutputStream .computeBytesSize(1, getPublicKeyBytes()); } - for (int i = 0; i < transactions_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(2, transactions_.get(i)); - } if (((bitField0_ & 0x00000002) == 0x00000002)) { size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(3, nextSequence_); + .computeUInt64Size(2, created_); } if (((bitField0_ & 0x00000004) == 0x00000004)) { size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(4, created_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(5, getNameBytes()); + .computeBytesSize(3, getNameBytes()); } size += getUnknownFields().getSerializedSize(); memoizedSerializedSize = size; @@ -8967,7 +8896,6 @@ private Builder(BuilderParent parent) { } private void maybeForceBuilderInitialization() { if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getTransactionsFieldBuilder(); } } private static Builder create() { @@ -8978,18 +8906,10 @@ public Builder clear() { super.clear(); publicKey_ = ""; bitField0_ = (bitField0_ & ~0x00000001); - if (transactionsBuilder_ == null) { - transactions_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000002); - } else { - transactionsBuilder_.clear(); - } - nextSequence_ = 0; - bitField0_ = (bitField0_ & ~0x00000004); created_ = 0L; - bitField0_ = (bitField0_ & ~0x00000008); + bitField0_ = (bitField0_ & ~0x00000002); name_ = ""; - bitField0_ = (bitField0_ & ~0x00000010); + bitField0_ = (bitField0_ & ~0x00000004); return this; } @@ -9032,25 +8952,12 @@ public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account buildPartial() to_bitField0_ |= 0x00000001; } result.publicKey_ = publicKey_; - if (transactionsBuilder_ == null) { - if (((bitField0_ & 0x00000002) == 0x00000002)) { - transactions_ = java.util.Collections.unmodifiableList(transactions_); - bitField0_ = (bitField0_ & ~0x00000002); - } - result.transactions_ = transactions_; - } else { - result.transactions_ = transactionsBuilder_.build(); - } - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { to_bitField0_ |= 0x00000002; } - result.nextSequence_ = nextSequence_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000004; - } result.created_ = created_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000008; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; } result.name_ = name_; result.bitField0_ = to_bitField0_; @@ -9072,35 +8979,6 @@ public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Acco if (other.hasPublicKey()) { setPublicKey(other.getPublicKey()); } - if (transactionsBuilder_ == null) { - if (!other.transactions_.isEmpty()) { - if (transactions_.isEmpty()) { - transactions_ = other.transactions_; - bitField0_ = (bitField0_ & ~0x00000002); - } else { - ensureTransactionsIsMutable(); - transactions_.addAll(other.transactions_); - } - onChanged(); - } - } else { - if (!other.transactions_.isEmpty()) { - if (transactionsBuilder_.isEmpty()) { - transactionsBuilder_.dispose(); - transactionsBuilder_ = null; - transactions_ = other.transactions_; - bitField0_ = (bitField0_ & ~0x00000002); - transactionsBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getTransactionsFieldBuilder() : null; - } else { - transactionsBuilder_.addAllMessages(other.transactions_); - } - } - } - if (other.hasNextSequence()) { - setNextSequence(other.getNextSequence()); - } if (other.hasCreated()) { setCreated(other.getCreated()); } @@ -9116,10 +8994,6 @@ public final boolean isInitialized() { return false; } - if (!hasNextSequence()) { - - return false; - } if (!hasCreated()) { return false; @@ -9128,12 +9002,6 @@ public final boolean isInitialized() { return false; } - for (int i = 0; i < getTransactionsCount(); i++) { - if (!getTransactions(i).isInitialized()) { - - return false; - } - } return true; } @@ -9165,24 +9033,13 @@ public Builder mergeFrom( publicKey_ = input.readBytes(); break; } - case 18: { - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder subBuilder = com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.newBuilder(); - input.readMessage(subBuilder, extensionRegistry); - addTransactions(subBuilder.buildPartial()); - break; - } - case 24: { - bitField0_ |= 0x00000004; - nextSequence_ = input.readUInt32(); - break; - } - case 32: { - bitField0_ |= 0x00000008; + case 16: { + bitField0_ |= 0x00000002; created_ = input.readUInt64(); break; } - case 42: { - bitField0_ |= 0x00000010; + case 26: { + bitField0_ |= 0x00000004; name_ = input.readBytes(); break; } @@ -9228,238 +9085,31 @@ void setPublicKey(com.google.protobuf.ByteString value) { onChanged(); } - // repeated .com.bitsofproof.supernode.api.Transaction transactions = 2; - private java.util.List transactions_ = - java.util.Collections.emptyList(); - private void ensureTransactionsIsMutable() { - if (!((bitField0_ & 0x00000002) == 0x00000002)) { - transactions_ = new java.util.ArrayList(transactions_); - bitField0_ |= 0x00000002; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder> transactionsBuilder_; - - public java.util.List getTransactionsList() { - if (transactionsBuilder_ == null) { - return java.util.Collections.unmodifiableList(transactions_); - } else { - return transactionsBuilder_.getMessageList(); - } - } - public int getTransactionsCount() { - if (transactionsBuilder_ == null) { - return transactions_.size(); - } else { - return transactionsBuilder_.getCount(); - } - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction getTransactions(int index) { - if (transactionsBuilder_ == null) { - return transactions_.get(index); - } else { - return transactionsBuilder_.getMessage(index); - } - } - public Builder setTransactions( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction value) { - if (transactionsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureTransactionsIsMutable(); - transactions_.set(index, value); - onChanged(); - } else { - transactionsBuilder_.setMessage(index, value); - } - return this; - } - public Builder setTransactions( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder builderForValue) { - if (transactionsBuilder_ == null) { - ensureTransactionsIsMutable(); - transactions_.set(index, builderForValue.build()); - onChanged(); - } else { - transactionsBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - public Builder addTransactions(com.bitsofproof.supernode.api.BCSAPIMessage.Transaction value) { - if (transactionsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureTransactionsIsMutable(); - transactions_.add(value); - onChanged(); - } else { - transactionsBuilder_.addMessage(value); - } - return this; - } - public Builder addTransactions( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction value) { - if (transactionsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureTransactionsIsMutable(); - transactions_.add(index, value); - onChanged(); - } else { - transactionsBuilder_.addMessage(index, value); - } - return this; - } - public Builder addTransactions( - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder builderForValue) { - if (transactionsBuilder_ == null) { - ensureTransactionsIsMutable(); - transactions_.add(builderForValue.build()); - onChanged(); - } else { - transactionsBuilder_.addMessage(builderForValue.build()); - } - return this; - } - public Builder addTransactions( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder builderForValue) { - if (transactionsBuilder_ == null) { - ensureTransactionsIsMutable(); - transactions_.add(index, builderForValue.build()); - onChanged(); - } else { - transactionsBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - public Builder addAllTransactions( - java.lang.Iterable values) { - if (transactionsBuilder_ == null) { - ensureTransactionsIsMutable(); - super.addAll(values, transactions_); - onChanged(); - } else { - transactionsBuilder_.addAllMessages(values); - } - return this; - } - public Builder clearTransactions() { - if (transactionsBuilder_ == null) { - transactions_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000002); - onChanged(); - } else { - transactionsBuilder_.clear(); - } - return this; - } - public Builder removeTransactions(int index) { - if (transactionsBuilder_ == null) { - ensureTransactionsIsMutable(); - transactions_.remove(index); - onChanged(); - } else { - transactionsBuilder_.remove(index); - } - return this; - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder getTransactionsBuilder( - int index) { - return getTransactionsFieldBuilder().getBuilder(index); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder getTransactionsOrBuilder( - int index) { - if (transactionsBuilder_ == null) { - return transactions_.get(index); } else { - return transactionsBuilder_.getMessageOrBuilder(index); - } - } - public java.util.List - getTransactionsOrBuilderList() { - if (transactionsBuilder_ != null) { - return transactionsBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(transactions_); - } - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder addTransactionsBuilder() { - return getTransactionsFieldBuilder().addBuilder( - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.getDefaultInstance()); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder addTransactionsBuilder( - int index) { - return getTransactionsFieldBuilder().addBuilder( - index, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.getDefaultInstance()); - } - public java.util.List - getTransactionsBuilderList() { - return getTransactionsFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder> - getTransactionsFieldBuilder() { - if (transactionsBuilder_ == null) { - transactionsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder>( - transactions_, - ((bitField0_ & 0x00000002) == 0x00000002), - getParentForChildren(), - isClean()); - transactions_ = null; - } - return transactionsBuilder_; - } - - // required uint32 nextSequence = 3; - private int nextSequence_ ; - public boolean hasNextSequence() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public int getNextSequence() { - return nextSequence_; - } - public Builder setNextSequence(int value) { - bitField0_ |= 0x00000004; - nextSequence_ = value; - onChanged(); - return this; - } - public Builder clearNextSequence() { - bitField0_ = (bitField0_ & ~0x00000004); - nextSequence_ = 0; - onChanged(); - return this; - } - - // required uint64 created = 4; + // required uint64 created = 2; private long created_ ; public boolean hasCreated() { - return ((bitField0_ & 0x00000008) == 0x00000008); + return ((bitField0_ & 0x00000002) == 0x00000002); } public long getCreated() { return created_; } public Builder setCreated(long value) { - bitField0_ |= 0x00000008; + bitField0_ |= 0x00000002; created_ = value; onChanged(); return this; } public Builder clearCreated() { - bitField0_ = (bitField0_ & ~0x00000008); + bitField0_ = (bitField0_ & ~0x00000002); created_ = 0L; onChanged(); return this; } - // required string name = 5; + // required string name = 3; private java.lang.Object name_ = ""; public boolean hasName() { - return ((bitField0_ & 0x00000010) == 0x00000010); + return ((bitField0_ & 0x00000004) == 0x00000004); } public String getName() { java.lang.Object ref = name_; @@ -9475,19 +9125,19 @@ public Builder setName(String value) { if (value == null) { throw new NullPointerException(); } - bitField0_ |= 0x00000010; + bitField0_ |= 0x00000004; name_ = value; onChanged(); return this; } public Builder clearName() { - bitField0_ = (bitField0_ & ~0x00000010); + bitField0_ = (bitField0_ & ~0x00000004); name_ = getDefaultInstance().getName(); onChanged(); return this; } void setName(com.google.protobuf.ByteString value) { - bitField0_ |= 0x00000010; + bitField0_ |= 0x00000004; name_ = value; onChanged(); } @@ -10337,15 +9987,12 @@ public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder addAcc "bcsapiversion\030\001 \002(\r\022\014\n\004mode\030\002 \002(\r\022\r\n\005mat" + "ch\030\003 \003(\014\022\r\n\005after\030\004 \001(\004\"\\\n\016AccountReques" + "t\022\025\n\rbcsapiversion\030\001 \002(\r\022\021\n\tpublicKey\030\002 " + - "\002(\t\022\021\n\tlookAhead\030\003 \002(\r\022\r\n\005after\030\004 \001(\004\"\240\002" + + "\002(\t\022\021\n\tlookAhead\030\003 \002(\r\022\r\n\005after\030\004 \001(\004\"\307\001" + "\n\006Wallet\022\025\n\rbcsapiversion\030\001 \002(\r\022\025\n\rencry" + "ptedSeed\030\002 \002(\014\022\021\n\tsignature\030\003 \002(\014\022?\n\010acc" + "ounts\030\004 \003(\0132-.com.bitsofproof.supernode.", - "api.Wallet.Account\032\223\001\n\007Account\022\021\n\tpublic" + - "Key\030\001 \002(\t\022@\n\014transactions\030\002 \003(\0132*.com.bi" + - "tsofproof.supernode.api.Transaction\022\024\n\014n" + - "extSequence\030\003 \002(\r\022\017\n\007created\030\004 \002(\004\022\014\n\004na" + - "me\030\005 \002(\t" + "api.Wallet.Account\032;\n\007Account\022\021\n\tpublicK" + + "ey\030\001 \002(\t\022\017\n\007created\030\002 \002(\004\022\014\n\004name\030\003 \002(\t" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -10469,7 +10116,7 @@ public com.google.protobuf.ExtensionRegistry assignDescriptors( internal_static_com_bitsofproof_supernode_api_Wallet_Account_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_com_bitsofproof_supernode_api_Wallet_Account_descriptor, - new java.lang.String[] { "PublicKey", "Transactions", "NextSequence", "Created", "Name", }, + new java.lang.String[] { "PublicKey", "Created", "Name", }, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.class, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder.class); return null; diff --git a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java index 3e93651d..047f6146 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java @@ -9,7 +9,6 @@ import java.util.HashMap; import java.util.Map; -import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ValidationException; import com.google.protobuf.ByteString; @@ -93,17 +92,10 @@ public void read (String fileName, int lookAhead) throws IOException, Validation for ( BCSAPIMessage.Wallet.Account account : walletMessage.getAccountsList () ) { InMemoryAccountManager am = - new InMemoryAccountManager (this, account.getName (), ExtendedKey.parse (account.getPublicKey ()), account.getNextSequence (), - account.getCreated ()); + new InMemoryAccountManager (this, account.getName (), ExtendedKey.parse (account.getPublicKey ()), account.getCreated ()); am.setApi (api); accounts.put (account.getName (), am); - int txHeight = 0; - for ( BCSAPIMessage.Transaction t : account.getTransactionsList () ) - { - am.updateWithTransaction (Transaction.fromProtobuf (t)); - txHeight = Math.max (txHeight, t.getHeight ()); - } - am.sync (lookAhead, txHeight == 0 ? account.getCreated () : txHeight); + am.sync (lookAhead, account.getCreated ()); } } finally @@ -132,7 +124,7 @@ public synchronized AccountManager createAccountManager (String name) throws Val throw new ValidationException ("The wallet is locked"); } InMemoryAccountManager account = - new InMemoryAccountManager (this, name, master.getChild (accounts.size () | 0x80000000), 0, System.currentTimeMillis () / 1000); + new InMemoryAccountManager (this, name, master.getChild (accounts.size () | 0x80000000), System.currentTimeMillis () / 1000); account.setApi (api); api.registerTransactionListener (account); accounts.put (name, account); @@ -160,7 +152,6 @@ public void persist () throws IOException, BCSAPIException FileOutputStream out = new FileOutputStream (fileName); try { - int headHeight = api.getBlock (Hash.ZERO_HASH_STRING).getHeight (); BCSAPIMessage.Wallet.Builder builder = BCSAPIMessage.Wallet.newBuilder (); builder.setBcsapiversion (1); builder.setEncryptedSeed (ByteString.copyFrom (encryptedSeed)); @@ -170,15 +161,7 @@ public void persist () throws IOException, BCSAPIException BCSAPIMessage.Wallet.Account.Builder ab = BCSAPIMessage.Wallet.Account.newBuilder (); ab.setName (am.getName ()); ab.setCreated (am.getCreated ()); - ab.setNextSequence (am.getNextSequence ()); ab.setPublicKey (am.getMasterKey ().getReadOnly ().serialize (true)); - for ( Transaction t : am.getTransactions () ) - { - if ( t.getHeight () < headHeight - 100 ) - { - ab.addTransactions (t.toProtobuf ()); - } - } builder.addAccounts (ab.build ()); } builder.build ().writeTo (out); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java index 50511eec..c7c21b58 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java @@ -72,11 +72,10 @@ public long getCreated () return created; } - public InMemoryAccountManager (Wallet wallet, String name, ExtendedKey extended, int nextKey, long created) + public InMemoryAccountManager (Wallet wallet, String name, ExtendedKey extended, long created) { this.name = name; this.extended = extended; - this.nextKey = nextKey; this.created = created; this.wallet = wallet; } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java index cb7b9ce4..efbda9a9 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java @@ -660,7 +660,7 @@ public void onMessage (Message arg0) } catch ( Exception e ) { - log.error ("Transaction message error", e); + log.debug ("Transaction message error", e); } } @@ -712,7 +712,7 @@ public void onMessage (Message message) } catch ( Exception e ) { - log.error ("Block message error", e); + log.debug ("Block message error", e); } } }); @@ -801,7 +801,7 @@ public void onMessage (Message message) } catch ( JMSException e ) { - log.error ("Can not parse reply", e); + log.debug ("Can not parse reply", e); } } }); diff --git a/api/src/main/protobuf/BCSAPIMessage.proto b/api/src/main/protobuf/BCSAPIMessage.proto index f5e37790..739dd256 100644 --- a/api/src/main/protobuf/BCSAPIMessage.proto +++ b/api/src/main/protobuf/BCSAPIMessage.proto @@ -126,10 +126,8 @@ message Wallet { message Account { required string publicKey = 1; - repeated Transaction transactions = 2; - required uint32 nextSequence = 3; - required uint64 created = 4; - required string name = 5; + required uint64 created = 2; + required string name = 3; } repeated Account accounts = 4; } \ No newline at end of file From e70c18fc22c07aecfe4141fbcfa9c30a7d639067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1rtfai=20Tam=C3=A1s?= Date: Tue, 18 Jun 2013 13:15:00 +0200 Subject: [PATCH 054/299] Don't store null values for collections in TrunkUpdateMessage. --- .../com/bitsofproof/supernode/api/TrunkUpdateMessage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/TrunkUpdateMessage.java b/api/src/main/java/com/bitsofproof/supernode/api/TrunkUpdateMessage.java index b61fa37e..fa84ee00 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/TrunkUpdateMessage.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/TrunkUpdateMessage.java @@ -67,8 +67,8 @@ public BCSAPIMessage.TrunkUpdate toProtobuf () public static TrunkUpdateMessage fromProtobuf (BCSAPIMessage.TrunkUpdate pu) { - List added = null; - List removed = null; + List added = new ArrayList(); + List removed = new ArrayList(); if ( pu.getAddedCount () > 0 ) { added = new ArrayList (); From 25910b131f6cb17becaf3f71eea2c231cdd6a281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1rtfai=20Tam=C3=A1s?= Date: Tue, 18 Jun 2013 13:47:21 +0200 Subject: [PATCH 055/299] Adding support for ActiveMQ. Two new spring configuration profiles added: * apollo * activemq Running the application with the BCSAPI profile will need one of the above profiles. --- server/pom.xml | 7 +++ .../main/resources/context/BCSAPI-profile.xml | 61 +++++++++++-------- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/server/pom.xml b/server/pom.xml index 6e9c8dd7..19e66154 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -158,6 +158,13 @@ geronimo-jms_1.1_spec 1.1 + + + org.apache.activemq + activemq-client + 5.8.0 + + org.fusesource.stompjms stompjms-client diff --git a/server/src/main/resources/context/BCSAPI-profile.xml b/server/src/main/resources/context/BCSAPI-profile.xml index 252cab9c..ccd89770 100644 --- a/server/src/main/resources/context/BCSAPI-profile.xml +++ b/server/src/main/resources/context/BCSAPI-profile.xml @@ -1,29 +1,38 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - From adb0279b7fe7e883e7d8592a3ab1a4286031a8fa Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Fri, 21 Jun 2013 16:37:17 +0200 Subject: [PATCH 056/299] introduce slave mode, print pool size --- .../supernode/core/BlocktesterChain.java | 6 ++++++ .../supernode/core/CachedBlockStore.java | 2 +- .../java/com/bitsofproof/supernode/core/Chain.java | 2 ++ .../bitsofproof/supernode/core/SatoshiChain.java | 13 +++++++++++++ .../bitsofproof/supernode/core/Testnet3Chain.java | 13 +++++++++++++ .../com/bitsofproof/supernode/core/TxHandler.java | 2 +- .../bitsofproof/supernode/core/UnitTestChain.java | 6 ++++++ 7 files changed, 42 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/BlocktesterChain.java b/server/src/main/java/com/bitsofproof/supernode/core/BlocktesterChain.java index 047b4509..8a5700cb 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/BlocktesterChain.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/BlocktesterChain.java @@ -36,6 +36,12 @@ public class BlocktesterChain implements Chain static final BigInteger minTarget = BigInteger.valueOf (1).shiftLeft (256).subtract (BigInteger.ONE); + @Override + public boolean isSlave () + { + return false; + } + @Override public boolean checkBeforeCheckpoint () { diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 9f9387e4..1f8ce2cd 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -989,7 +989,7 @@ private void lockedStoreBlock (Blk b) throws ValidationException } } - if ( !chain.isProduction () || b.getHeight () > lastCheckPoint || chain.checkBeforeCheckpoint () ) + if ( !chain.isSlave () && !chain.isProduction () || b.getHeight () > lastCheckPoint || chain.checkBeforeCheckpoint () ) { log.trace ("validating block " + b.getHash ()); diff --git a/server/src/main/java/com/bitsofproof/supernode/core/Chain.java b/server/src/main/java/com/bitsofproof/supernode/core/Chain.java index 5c187ab6..deb67058 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/Chain.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/Chain.java @@ -46,4 +46,6 @@ public interface Chain public int getAddressFlag (); public int getP2SHAddressFlag (); + + public boolean isSlave (); } \ No newline at end of file diff --git a/server/src/main/java/com/bitsofproof/supernode/core/SatoshiChain.java b/server/src/main/java/com/bitsofproof/supernode/core/SatoshiChain.java index 67641378..7952b680 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/SatoshiChain.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/SatoshiChain.java @@ -37,6 +37,19 @@ public class SatoshiChain implements Chain static final BigInteger minTarget = BigInteger.valueOf (0xFFFFL).shiftLeft (8 * (0x1d - 3)); + private boolean slave = false; + + public void setSlave (boolean slave) + { + this.slave = slave; + } + + @Override + public boolean isSlave () + { + return slave; + } + @Override public boolean checkBeforeCheckpoint () { diff --git a/server/src/main/java/com/bitsofproof/supernode/core/Testnet3Chain.java b/server/src/main/java/com/bitsofproof/supernode/core/Testnet3Chain.java index 63489930..2d00864d 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/Testnet3Chain.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/Testnet3Chain.java @@ -37,6 +37,19 @@ public class Testnet3Chain implements Chain static final BigInteger minTarget = BigInteger.valueOf (0xFFFFL).shiftLeft (8 * (0x1d - 3)); + private boolean slave = false; + + public void setSlave (boolean slave) + { + this.slave = slave; + } + + @Override + public boolean isSlave () + { + return slave; + } + @Override public BigInteger getMinimumTarget () { diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index ded27dc7..0d18931c 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -275,10 +275,10 @@ public Tx getTransaction (String hash) private void cacheTransaction (Tx tx) { - log.trace ("Caching unconfirmed transaction " + tx.getHash ()); synchronized ( unconfirmed ) { unconfirmed.put (tx.getHash (), tx); + log.trace ("Caching unconfirmed transaction " + tx.getHash () + " pool size: " + unconfirmed.size ()); for ( TxOut out : tx.getOutputs () ) { diff --git a/server/src/main/java/com/bitsofproof/supernode/core/UnitTestChain.java b/server/src/main/java/com/bitsofproof/supernode/core/UnitTestChain.java index 9bf61ba5..c6d23469 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/UnitTestChain.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/UnitTestChain.java @@ -36,6 +36,12 @@ public class UnitTestChain implements Chain static final BigInteger minTarget = BigInteger.valueOf (1).shiftLeft (256).subtract (BigInteger.ONE); + @Override + public boolean isSlave () + { + return false; + } + @Override public boolean checkBeforeCheckpoint () { From 84fa9d6dddeb0c602a7b7f0f06c78891fe26abb8 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Fri, 21 Jun 2013 19:14:16 +0200 Subject: [PATCH 057/299] discovery should return socket address not only host address. add slave-production profile. --- server/bop-service.sh | 0 .../supernode/core/AddressHandler.java | 4 +-- .../supernode/core/BitcoinNetwork.java | 7 ++--- .../supernode/core/CachedBlockStore.java | 2 +- .../supernode/core/DNSDiscovery.java | 11 +++++-- .../bitsofproof/supernode/core/Discovery.java | 4 +-- .../supernode/core/DiscoveryChain.java | 8 ++--- .../supernode/core/FixedAddressDiscovery.java | 25 +++++++++++----- .../supernode/core/IRCDiscovery.java | 12 +++++--- .../com/bitsofproof/supernode/core/P2P.java | 11 ++----- .../bitsofproof/supernode/model/JpaStore.java | 17 ++++++++--- .../bitsofproof/supernode/model/LvlStore.java | 18 ++++++++---- .../context/slave-production-profile.xml | 29 +++++++++++++++++++ 13 files changed, 104 insertions(+), 44 deletions(-) mode change 100644 => 100755 server/bop-service.sh create mode 100644 server/src/main/resources/context/slave-production-profile.xml diff --git a/server/bop-service.sh b/server/bop-service.sh old mode 100644 new mode 100755 diff --git a/server/src/main/java/com/bitsofproof/supernode/core/AddressHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/AddressHandler.java index bb15696d..3433f37a 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/AddressHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/AddressHandler.java @@ -65,7 +65,7 @@ public void process (AddrMessage am, BitcoinPeer peer) for ( Address a : am.getAddresses () ) { log.trace ("received address " + a.address + " from " + peer.getAddress ().getAddress ()); - peer.getNetwork ().addPeer (a.address, (int) a.port); + peer.getNetwork ().addPeer (peer.getAddress ()); if ( network.getPeerStore () != null ) { KnownPeer p; @@ -75,7 +75,7 @@ public void process (AddrMessage am, BitcoinPeer peer) if ( p == null ) { p = new KnownPeer (); - p.setAddress (a.address.getHostAddress ()); + p.setAddress (a.address.getHostAddress () + ":" + a.port); p.setResponseTime (Integer.MAX_VALUE); network.getPeerStore ().store (p); } diff --git a/server/src/main/java/com/bitsofproof/supernode/core/BitcoinNetwork.java b/server/src/main/java/com/bitsofproof/supernode/core/BitcoinNetwork.java index d3cfdd9e..a4fac209 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/BitcoinNetwork.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/BitcoinNetwork.java @@ -16,7 +16,6 @@ package com.bitsofproof.supernode.core; import java.io.IOException; -import java.net.InetAddress; import java.net.InetSocketAddress; import java.security.SecureRandom; import java.util.ArrayList; @@ -271,11 +270,11 @@ public long getChainHeight () @Override protected boolean discover () { - List al = discovery.discover (); + List al = discovery.discover (); - for ( InetAddress a : al ) + for ( InetSocketAddress a : al ) { - addPeer (a, chain.getPort ()); + addPeer (a); } return !al.isEmpty (); diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 1f8ce2cd..8fecfd79 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -93,7 +93,7 @@ public abstract class CachedBlockStore implements BlockStore lastCheckPoint = 232000; } - private Chain chain; + protected Chain chain; @Autowired PlatformTransactionManager transactionManager; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/DNSDiscovery.java b/server/src/main/java/com/bitsofproof/supernode/core/DNSDiscovery.java index a913bd62..2f1a8a1d 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/DNSDiscovery.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/DNSDiscovery.java @@ -16,6 +16,7 @@ package com.bitsofproof.supernode.core; import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collections; @@ -23,18 +24,22 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; public class DNSDiscovery implements Discovery { private static final Logger log = LoggerFactory.getLogger (BitcoinNetwork.class); private String[] seedHosts; + @Autowired + Chain chain; + @Override - public List discover () + public List discover () { log.trace ("Discovering network using DNS seed"); int n = 0; - List al = new ArrayList (); + List al = new ArrayList (); for ( String hostName : seedHosts ) { log.trace ("Obtain addresses from " + hostName); @@ -44,7 +49,7 @@ public List discover () hostAddresses = InetAddress.getAllByName (hostName); for ( InetAddress inetAddress : hostAddresses ) { - al.add (inetAddress); + al.add (new InetSocketAddress (inetAddress, chain.getPort ())); ++n; } } diff --git a/server/src/main/java/com/bitsofproof/supernode/core/Discovery.java b/server/src/main/java/com/bitsofproof/supernode/core/Discovery.java index 7b13e414..1ba25793 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/Discovery.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/Discovery.java @@ -15,10 +15,10 @@ */ package com.bitsofproof.supernode.core; -import java.net.InetAddress; +import java.net.InetSocketAddress; import java.util.List; public interface Discovery { - public List discover (); + public List discover (); } diff --git a/server/src/main/java/com/bitsofproof/supernode/core/DiscoveryChain.java b/server/src/main/java/com/bitsofproof/supernode/core/DiscoveryChain.java index ff34de3e..5d162a8b 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/DiscoveryChain.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/DiscoveryChain.java @@ -15,7 +15,7 @@ */ package com.bitsofproof.supernode.core; -import java.net.InetAddress; +import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; @@ -29,16 +29,16 @@ public DiscoveryChain (List chain) } @Override - public List discover () + public List discover () { for ( Discovery discovery : discoveryChain ) { - List addresses = discovery.discover (); + List addresses = discovery.discover (); if ( !addresses.isEmpty () ) { return addresses; } } - return new ArrayList (); + return new ArrayList (); } } diff --git a/server/src/main/java/com/bitsofproof/supernode/core/FixedAddressDiscovery.java b/server/src/main/java/com/bitsofproof/supernode/core/FixedAddressDiscovery.java index cfeff6cc..ba7af564 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/FixedAddressDiscovery.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/FixedAddressDiscovery.java @@ -15,13 +15,13 @@ */ package com.bitsofproof.supernode.core; -import java.net.InetAddress; -import java.net.UnknownHostException; +import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; public class FixedAddressDiscovery implements Discovery { @@ -29,17 +29,28 @@ public class FixedAddressDiscovery implements Discovery private String connectTo; + @Autowired + private Chain chain; + @Override - public List discover () + public List discover () { - List al = new ArrayList (); + List al = new ArrayList (); try { - al.add (InetAddress.getByName (connectTo)); + String[] split = connectTo.split (":"); + if ( split.length == 2 ) + { + al.add (InetSocketAddress.createUnresolved (split[0], Integer.valueOf (split[1]))); + } + else + { + al.add (InetSocketAddress.createUnresolved (split[0], chain.getPort ())); + } } - catch ( UnknownHostException e ) + catch ( Exception e ) { - log.error ("can not connect to " + connectTo); + log.error ("can not connect to " + connectTo, e); } return al; } diff --git a/server/src/main/java/com/bitsofproof/supernode/core/IRCDiscovery.java b/server/src/main/java/com/bitsofproof/supernode/core/IRCDiscovery.java index 25b61a43..a4591222 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/IRCDiscovery.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/IRCDiscovery.java @@ -21,6 +21,7 @@ import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.Socket; import java.net.UnknownHostException; import java.security.SecureRandom; @@ -30,11 +31,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import com.bitsofproof.supernode.common.ByteUtils; import com.bitsofproof.supernode.common.ValidationException; - public class IRCDiscovery implements Discovery { private static final Logger log = LoggerFactory.getLogger (IRCDiscovery.class); @@ -42,10 +43,13 @@ public class IRCDiscovery implements Discovery private int port; private String channel; + @Autowired + private Chain chain; + @Override - public List discover () + public List discover () { - List al = new ArrayList (); + List al = new ArrayList (); try { @@ -113,7 +117,7 @@ public List discover () byte[] m = ByteUtils.fromBase58WithChecksum (w); byte[] addr = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xff, (byte) 0xff, 0, 0, 0, 0 }; System.arraycopy (m, 0, addr, 12, 4); - al.add (InetAddress.getByAddress (addr)); + al.add (new InetSocketAddress (InetAddress.getByAddress (addr), chain.getPort ())); } catch ( ValidationException e ) { diff --git a/server/src/main/java/com/bitsofproof/supernode/core/P2P.java b/server/src/main/java/com/bitsofproof/supernode/core/P2P.java index 450addc1..0c106be5 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/P2P.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/P2P.java @@ -18,7 +18,6 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.CancelledKeyException; @@ -336,15 +335,11 @@ public void send (Message m) protected abstract boolean discover (); - protected void addPeer (InetAddress addr, int port) + protected void addPeer (InetSocketAddress address) { - if ( !addr.isAnyLocalAddress () ) + if ( !runqueue.contains (address) ) { - InetSocketAddress address = new InetSocketAddress (addr, port); - if ( !runqueue.contains (address) ) - { - runqueue.add (address); - } + runqueue.add (address); } } diff --git a/server/src/main/java/com/bitsofproof/supernode/model/JpaStore.java b/server/src/main/java/com/bitsofproof/supernode/model/JpaStore.java index 75bfa6c1..142a2592 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/JpaStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/JpaStore.java @@ -16,6 +16,7 @@ package com.bitsofproof.supernode.model; import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collection; @@ -379,17 +380,25 @@ public KnownPeer findPeer (InetAddress address) } @Override - public List discover () + public List discover () { log.trace ("Discovering stored peers"); - List peers = new ArrayList (); + List peers = new ArrayList (); for ( KnownPeer kp : getConnectablePeers () ) { try { - peers.add (InetAddress.getByName (kp.getAddress ())); + String[] split = kp.getAddress ().split (":"); + if ( split.length == 2 ) + { + peers.add (InetSocketAddress.createUnresolved (split[0], Integer.valueOf (split[1]))); + } + else + { + peers.add (InetSocketAddress.createUnresolved (split[0], chain.getPort ())); + } } - catch ( UnknownHostException e ) + catch ( Exception e ) { } } diff --git a/server/src/main/java/com/bitsofproof/supernode/model/LvlStore.java b/server/src/main/java/com/bitsofproof/supernode/model/LvlStore.java index 299febc6..091f32c6 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/LvlStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/LvlStore.java @@ -16,7 +16,7 @@ package com.bitsofproof.supernode.model; import java.net.InetAddress; -import java.net.UnknownHostException; +import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -548,17 +548,25 @@ public KnownPeer findPeer (InetAddress address) throws ValidationException } @Override - public List discover () + public List discover () { log.trace ("Discovering stored peers"); - List peers = new ArrayList (); + List peers = new ArrayList (); for ( KnownPeer kp : getConnectablePeers () ) { try { - peers.add (InetAddress.getByName (kp.getAddress ())); + String[] split = kp.getAddress ().split (":"); + if ( split.length == 2 ) + { + peers.add (InetSocketAddress.createUnresolved (split[0], Integer.valueOf (split[1]))); + } + else + { + peers.add (InetSocketAddress.createUnresolved (split[0], chain.getPort ())); + } } - catch ( UnknownHostException e ) + catch ( Exception e ) { } } diff --git a/server/src/main/resources/context/slave-production-profile.xml b/server/src/main/resources/context/slave-production-profile.xml new file mode 100644 index 00000000..f0b928bd --- /dev/null +++ b/server/src/main/resources/context/slave-production-profile.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + 1 + + + + + + + From de3e68d5f89fc8e3d31e2d1e06b078f90214c9a3 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Fri, 21 Jun 2013 19:50:38 +0200 Subject: [PATCH 058/299] spring context exception dump. --- .../com/bitsofproof/supernode/main/Main.java | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/main/Main.java b/server/src/main/java/com/bitsofproof/supernode/main/Main.java index 3ef94afe..f41a610e 100644 --- a/server/src/main/java/com/bitsofproof/supernode/main/Main.java +++ b/server/src/main/java/com/bitsofproof/supernode/main/Main.java @@ -45,31 +45,38 @@ public static void main (String[] args) throws Exception return; } - GenericXmlApplicationContext ctx = new GenericXmlApplicationContext (); - List a = new ArrayList (); - boolean profiles = true; - for ( String s : args ) + try { - if ( s.equals ("--") ) + GenericXmlApplicationContext ctx = new GenericXmlApplicationContext (); + List a = new ArrayList (); + boolean profiles = true; + for ( String s : args ) { - profiles = false; - } - else - { - if ( profiles ) + if ( s.equals ("--") ) { - log.info ("Loading profile: " + s); - ctx.getEnvironment ().addActiveProfile (s); + profiles = false; } else { - a.add (s); + if ( profiles ) + { + log.info ("Loading profile: " + s); + ctx.getEnvironment ().addActiveProfile (s); + } + else + { + a.add (s); + } } } + ctx.load ("classpath:context/server.xml"); + ctx.load ("classpath:context/*-profile.xml"); + ctx.refresh (); + ctx.getBean (App.class).start (a.toArray (new String[0])); + } + catch ( Exception e ) + { + log.error ("Error setting up spring context", e); } - ctx.load ("classpath:context/server.xml"); - ctx.load ("classpath:context/*-profile.xml"); - ctx.refresh (); - ctx.getBean (App.class).start (a.toArray (new String[0])); } } From e7e96ff76487ca2aee2ebd2ba3f730395a9c71a4 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Fri, 21 Jun 2013 19:53:43 +0200 Subject: [PATCH 059/299] call it slave --- .../context/{slave-production-profile.xml => slave-profile.xml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename server/src/main/resources/context/{slave-production-profile.xml => slave-profile.xml} (100%) diff --git a/server/src/main/resources/context/slave-production-profile.xml b/server/src/main/resources/context/slave-profile.xml similarity index 100% rename from server/src/main/resources/context/slave-production-profile.xml rename to server/src/main/resources/context/slave-profile.xml From ec269c3a367fe1e48cc680953f51104ec0062b7b Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Fri, 21 Jun 2013 19:56:55 +0200 Subject: [PATCH 060/299] profile name amended --- server/src/main/resources/context/slave-profile.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/resources/context/slave-profile.xml b/server/src/main/resources/context/slave-profile.xml index f0b928bd..891037e5 100644 --- a/server/src/main/resources/context/slave-profile.xml +++ b/server/src/main/resources/context/slave-profile.xml @@ -1,5 +1,5 @@ - Date: Fri, 21 Jun 2013 20:06:47 +0200 Subject: [PATCH 061/299] resolve address --- .../bitsofproof/supernode/core/FixedAddressDiscovery.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/FixedAddressDiscovery.java b/server/src/main/java/com/bitsofproof/supernode/core/FixedAddressDiscovery.java index ba7af564..4063ce4f 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/FixedAddressDiscovery.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/FixedAddressDiscovery.java @@ -15,6 +15,7 @@ */ package com.bitsofproof.supernode.core; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; @@ -41,11 +42,11 @@ public List discover () String[] split = connectTo.split (":"); if ( split.length == 2 ) { - al.add (InetSocketAddress.createUnresolved (split[0], Integer.valueOf (split[1]))); + al.add (new InetSocketAddress (InetAddress.getByName (split[0]), Integer.valueOf (split[1]))); } else { - al.add (InetSocketAddress.createUnresolved (split[0], chain.getPort ())); + al.add (new InetSocketAddress (InetAddress.getByName (split[0]), chain.getPort ())); } } catch ( Exception e ) From c971e66bbe5157b00f6ee99bafc2520a3bdbd816 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Fri, 21 Jun 2013 20:15:08 +0200 Subject: [PATCH 062/299] no listen for slaves --- server/src/main/resources/context/slave-profile.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/main/resources/context/slave-profile.xml b/server/src/main/resources/context/slave-profile.xml index 891037e5..095e28ca 100644 --- a/server/src/main/resources/context/slave-profile.xml +++ b/server/src/main/resources/context/slave-profile.xml @@ -24,6 +24,7 @@ + From f6efeaa37d8f28ac4d93239788959d6c99717cfd Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Fri, 21 Jun 2013 21:12:40 +0200 Subject: [PATCH 063/299] turn off serving protocols if slave --- server/src/main/resources/context/server.xml | 99 ++++++++++---------- 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/server/src/main/resources/context/server.xml b/server/src/main/resources/context/server.xml index 4f4a1ba5..d314beab 100644 --- a/server/src/main/resources/context/server.xml +++ b/server/src/main/resources/context/server.xml @@ -8,8 +8,8 @@ http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> - - + + @@ -22,63 +22,64 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + From b9192f53e50b12b48f294f1c1a2b1dee908b051e Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Sat, 22 Jun 2013 10:52:32 +0200 Subject: [PATCH 064/299] set version to 1.1 --- api/pom.xml | 2 +- pom.xml | 2 +- server/.gitignore | 1 + server/pom.xml | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index a7203d26..8d7e5454 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1-SNAPSHOT + 1.1 ../ diff --git a/pom.xml b/pom.xml index 4e7d7f0c..0a91dc36 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.1-SNAPSHOT + 1.1 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/.gitignore b/server/.gitignore index 07e58182..a0f43e75 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -2,3 +2,4 @@ BCSAPIPASSWORD bcsapi_server.keystore /derby.log logs +/test.wallet diff --git a/server/pom.xml b/server/pom.xml index 19e66154..88792611 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1-SNAPSHOT + 1.1 ../ From f32f6b47418bb1b0a0aabfc8e5e3c22c569a58ba Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Sat, 22 Jun 2013 12:29:42 +0200 Subject: [PATCH 065/299] use 1.1 --- server/bop-service.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/bop-service.sh b/server/bop-service.sh index 1bb37b1d..6ae9ee78 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.0.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.0.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" From 7c3b7cc1ee8fb4b41c06742932d16dca829ed7a4 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Sun, 23 Jun 2013 11:58:38 +0200 Subject: [PATCH 066/299] reduce link of transaction and account manager by introducting an address to key map --- .../com/bitsofproof/supernode/api/AccountManager.java | 4 +--- .../com/bitsofproof/supernode/api/AddressToKeyMap.java | 8 ++++++++ .../java/com/bitsofproof/supernode/api/Transaction.java | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 api/src/main/java/com/bitsofproof/supernode/api/AddressToKeyMap.java diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java index d0e99dac..ab223a07 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java @@ -21,7 +21,7 @@ import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ValidationException; -public interface AccountManager +public interface AccountManager extends AddressToKeyMap { public void setApi (BCSAPI api); @@ -37,8 +37,6 @@ public interface AccountManager public ExtendedKey getMasterKey (); - public Key getKeyForAddress (byte[] address); - public Key getNextKey () throws ValidationException; public int getNextSequence (); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AddressToKeyMap.java b/api/src/main/java/com/bitsofproof/supernode/api/AddressToKeyMap.java new file mode 100644 index 00000000..fa005582 --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/api/AddressToKeyMap.java @@ -0,0 +1,8 @@ +package com.bitsofproof.supernode.api; + +import com.bitsofproof.supernode.common.Key; + +public interface AddressToKeyMap +{ + public Key getKeyForAddress (byte[] address); +} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java b/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java index c7be4d4a..7b72a316 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java @@ -131,7 +131,7 @@ public long getValue () } } - public static Transaction createSpend (AccountManager am, List sources, List sinks, long fee) + public static Transaction createSpend (AddressToKeyMap am, List sources, List sinks, long fee) throws ValidationException { if ( fee < 0 || fee > 1000000 ) From b529594cf573dd0d36561cf5bb6804342e77c95c Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Sun, 23 Jun 2013 17:04:58 +0200 Subject: [PATCH 067/299] re-send transaction if in block to update confirmation in wallet. --- .../src/main/java/com/bitsofproof/supernode/core/TxHandler.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index 0d18931c..a829f3ba 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -369,6 +369,7 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo own.remove (tx.getHash ()); } dependencyOrderedSet.remove (tx); + notifyListener (tx, false); } else { From 5738092cb547b31ce7d7a3d395561cd528d4ed3d Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Sun, 23 Jun 2013 17:13:31 +0200 Subject: [PATCH 068/299] no need to store processedTransactions in memory --- .../supernode/api/InMemoryAccountManager.java | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java index c7c21b58..3c25ebec 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java @@ -21,10 +21,8 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.slf4j.Logger; @@ -50,7 +48,6 @@ class InMemoryAccountManager implements TransactionListener, AccountManager private final InMemoryUTXO sending = new InMemoryUTXO (); private final List accountListener = Collections.synchronizedList (new ArrayList ()); - private final Set processedTransaction = new HashSet (); private final Map relevantTransaction = new HashMap (); private final String name; @@ -186,13 +183,8 @@ public boolean updateWithTransaction (Transaction t) synchronized ( confirmed ) { boolean modified = false; - if ( !processedTransaction.contains (t.getHash ()) ) + if ( !t.isDoubleSpend () ) { - if ( t.isDoubleSpend () ) - { - return false; - } - processedTransaction.add (t.getHash ()); TransactionOutput spend = null; for ( TransactionInput i : t.getInputs () ) { @@ -231,10 +223,13 @@ public boolean updateWithTransaction (Transaction t) if ( t.getBlockHash () != null ) { confirmed.add (t.getHash (), ix, o); - log.trace ("Settled " + ix + " " + o.getValue ()); + change.remove (t.getHash (), ix); + receiving.remove (t.getHash (), ix); + log.trace ("Settled " + ix + " " + t.getHash () + " " + o.getValue ()); } else { + confirmed.remove (t.getHash (), ix); if ( spend != null ) { change.add (t.getHash (), ix, o); @@ -253,6 +248,7 @@ public boolean updateWithTransaction (Transaction t) { modified = true; sending.add (t.getHash (), ix, o); + confirmed.remove (t.getHash (), ix); log.trace ("Sending " + ix + " " + o.getValue ()); } } @@ -263,7 +259,7 @@ public boolean updateWithTransaction (Transaction t) relevantTransaction.put (t.getHash (), t); } } - else if ( t.isDoubleSpend () ) + else { for ( long ix = 0; ix < t.getOutputs ().size (); ++ix ) { @@ -287,7 +283,6 @@ else if ( t.isDoubleSpend () ) } modified |= out != null; } - processedTransaction.remove (t.getHash ()); relevantTransaction.remove (t.getHash ()); } return modified; From f8d6875515ae53267ada996a20d028bd25e78162 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Sun, 23 Jun 2013 17:52:53 +0200 Subject: [PATCH 069/299] further simplified output cathegorisation. --- .../supernode/api/InMemoryAccountManager.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java index 3c25ebec..93802797 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java @@ -217,19 +217,21 @@ public boolean updateWithTransaction (Transaction t) long ix = 0; for ( TransactionOutput o : t.getOutputs () ) { + confirmed.remove (t.getHash (), ix); + change.remove (t.getHash (), ix); + receiving.remove (t.getHash (), ix); + sending.remove (t.getHash (), ix); + if ( keyIDForAddress.containsKey (new ByteVector (o.getOutputAddress ())) ) { modified = true; if ( t.getBlockHash () != null ) { confirmed.add (t.getHash (), ix, o); - change.remove (t.getHash (), ix); - receiving.remove (t.getHash (), ix); log.trace ("Settled " + ix + " " + t.getHash () + " " + o.getValue ()); } else { - confirmed.remove (t.getHash (), ix); if ( spend != null ) { change.add (t.getHash (), ix, o); @@ -248,7 +250,6 @@ public boolean updateWithTransaction (Transaction t) { modified = true; sending.add (t.getHash (), ix, o); - confirmed.remove (t.getHash (), ix); log.trace ("Sending " + ix + " " + o.getValue ()); } } From 2f714c4b336046b419c1191679eabe51d9d0af2e Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Sun, 23 Jun 2013 18:38:26 +0200 Subject: [PATCH 070/299] missed last block in scan --- .../java/com/bitsofproof/supernode/core/CachedBlockStore.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 8fecfd79..7303499a 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -1242,10 +1242,11 @@ else if ( currentHead.getLast () == cachedPrevious ) addedBlocks.add (b); currentHead = usingHead; } - updateBlockChainCache (); usingHead.setLast (m); + updateBlockChainCache (); + log.debug ("stored block " + b.getHeight () + " " + b.getHash ()); if ( !removedBlocks.isEmpty () || !addedBlocks.isEmpty () ) { From 387bf5b49b71f3e7df26256bb38e497339d691d5 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Sun, 23 Jun 2013 20:43:25 +0200 Subject: [PATCH 071/299] simplify in memory utxo. add txhash and ix to transaction output java object, initialized as hash calculated. --- .../supernode/api/AccountManager.java | 2 - .../supernode/api/InMemoryAccountManager.java | 27 ++--- .../supernode/api/InMemoryUTXO.java | 110 +++++++++--------- .../supernode/api/Transaction.java | 52 +++------ .../supernode/api/TransactionOutput.java | 24 ++++ 5 files changed, 103 insertions(+), 112 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java index ab223a07..27ba6ff1 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java @@ -39,8 +39,6 @@ public interface AccountManager extends AddressToKeyMap public Key getNextKey () throws ValidationException; - public int getNextSequence (); - public Transaction pay (byte[] receiver, long amount, long fee) throws ValidationException, BCSAPIException; public Transaction split (long[] amounts, long fee) throws ValidationException, BCSAPIException; diff --git a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java index 93802797..0e7ed83e 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java @@ -29,7 +29,6 @@ import org.slf4j.LoggerFactory; import com.bitsofproof.supernode.api.Transaction.TransactionSink; -import com.bitsofproof.supernode.api.Transaction.TransactionSource; import com.bitsofproof.supernode.common.ByteVector; import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ScriptFormat; @@ -130,12 +129,6 @@ public void process (Transaction t) api.registerTransactionListener (this); } - @Override - public int getNextSequence () - { - return nextKey; - } - @Override public String getName () { @@ -227,19 +220,19 @@ public boolean updateWithTransaction (Transaction t) modified = true; if ( t.getBlockHash () != null ) { - confirmed.add (t.getHash (), ix, o); + confirmed.add (o); log.trace ("Settled " + ix + " " + t.getHash () + " " + o.getValue ()); } else { if ( spend != null ) { - change.add (t.getHash (), ix, o); + change.add (o); log.trace ("Change " + ix + " " + o.getValue ()); } else { - receiving.add (t.getHash (), ix, o); + receiving.add (o); log.trace ("Receiving " + ix + " " + o.getValue ()); } } @@ -249,7 +242,7 @@ public boolean updateWithTransaction (Transaction t) if ( t.getBlockHash () == null && spend != null ) { modified = true; - sending.add (t.getHash (), ix, o); + sending.add (o); log.trace ("Sending " + ix + " " + o.getValue ()); } } @@ -334,7 +327,7 @@ public Transaction pay (byte[] receiver, long amount, long fee) throws Validatio { synchronized ( confirmed ) { - List sources = confirmed.getSufficientSources (amount, fee, null); + List sources = confirmed.getSufficientSources (amount, fee, null); if ( sources == null ) { throw new ValidationException ("Insufficient funds to pay " + (amount + fee)); @@ -342,9 +335,9 @@ public Transaction pay (byte[] receiver, long amount, long fee) throws Validatio List sinks = new ArrayList (); long in = 0; - for ( TransactionSource o : sources ) + for ( TransactionOutput o : sources ) { - in += o.getOutput ().getValue (); + in += o.getValue (); } TransactionSink target = new TransactionSink (receiver, amount); if ( (in - amount - fee) > 0 ) @@ -381,15 +374,15 @@ public Transaction split (long[] amounts, long fee) throws ValidationException, { amount += a; } - List sources = confirmed.getSufficientSources (amount, fee, null); + List sources = confirmed.getSufficientSources (amount, fee, null); if ( sources == null ) { throw new ValidationException ("Insufficient funds to pay " + (amount + fee)); } long in = 0; - for ( TransactionSource o : sources ) + for ( TransactionOutput o : sources ) { - in += o.getOutput ().getValue (); + in += o.getValue (); } for ( long a : amounts ) { diff --git a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java index d600b35f..c40d56b1 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java @@ -16,104 +16,102 @@ package com.bitsofproof.supernode.api; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; -import com.bitsofproof.supernode.api.Transaction.TransactionSource; - public class InMemoryUTXO { - private final Map> utxo = new HashMap> (); - - public void add (String source, long ix, TransactionOutput out) + private static class TxOutKey { - HashMap outs = utxo.get (source); - if ( outs == null ) + String hash; + long ix; + + public TxOutKey (String hash, long ix) + { + this.hash = hash; + this.ix = ix; + } + + @Override + public boolean equals (Object obj) + { + TxOutKey o = (TxOutKey) obj; + return o.hash.equals (hash) && o.ix == ix; + } + + @Override + public int hashCode () { - outs = new HashMap (); - utxo.put (source, outs); + return (int) (hash.hashCode () + ix); } - outs.put (ix, out); + + } + + private final Map utxo = new HashMap (); + + public void add (TransactionOutput out) + { + utxo.put (new TxOutKey (out.getTxHash (), out.getIx ()), out); } - public Map> getUTXO () + public Collection getUTXO () { - return utxo; + return utxo.values (); } public TransactionOutput get (String tx, long ix) { - HashMap outs = utxo.get (tx); - if ( outs != null ) - { - return outs.get (ix); - } - return null; + return utxo.get (new TxOutKey (tx, ix)); } public TransactionOutput remove (String tx, long ix) { - HashMap outs = utxo.get (tx); - if ( outs != null ) - { - TransactionOutput removed = outs.remove (ix); - if ( outs.size () == 0 ) - { - utxo.remove (tx); - } - return removed; - } - return null; + return utxo.remove (new TxOutKey (tx, ix)); } public long getTotal () { long s = 0; - for ( HashMap entry : utxo.values () ) + for ( TransactionOutput o : utxo.values () ) { - for ( TransactionOutput o : entry.values () ) - { - s += o.getValue (); - } + s += o.getValue (); } return s; } - public List getSufficientSources (long amount, long fee, String color) + public List getSufficientSources (long amount, long fee, String color) { - List result = new ArrayList (); + List result = new ArrayList (); long sum = 0; - for ( Map.Entry> txs : utxo.entrySet () ) + for ( TransactionOutput o : utxo.values () ) { - for ( Map.Entry o : txs.getValue ().entrySet () ) + if ( color == null ) { - if ( color == null ) + if ( o.getColor () == null ) { - if ( o.getValue ().getColor () == null ) + sum += o.getValue (); + result.add (o); + if ( sum >= (amount + fee) ) { - sum += o.getValue ().getValue (); - result.add (new TransactionSource (txs.getKey (), o.getKey (), o.getValue ())); - if ( sum >= (amount + fee) ) - { - return result; - } + return result; } } - else + } + else + { + if ( o.getColor ().equals (color) ) { - if ( o.getValue ().getColor ().equals (color) ) + sum += o.getValue (); + result.add (o); + if ( sum >= amount ) { - sum += o.getValue ().getValue (); - result.add (new TransactionSource (txs.getKey (), o.getKey (), o.getValue ())); - if ( sum >= amount ) + if ( fee > 0 ) { - if ( fee > 0 ) - { - result.addAll (getSufficientSources (0, fee, null)); - } - return result; + result.addAll (getSufficientSources (0, fee, null)); } + return result; } } } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java b/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java index 7b72a316..a309abde 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java @@ -79,35 +79,6 @@ public static Transaction createCoinbase (Key receiver, long value, int blockHei return cb; } - public static class TransactionSource - { - private final String source; - private final long ix; - private final TransactionOutput output; - - public TransactionSource (String source, long ix, TransactionOutput output) - { - this.source = source; - this.ix = ix; - this.output = output; - } - - public TransactionOutput getOutput () - { - return output; - } - - public String getSource () - { - return source; - } - - public long getIx () - { - return ix; - } - } - public static class TransactionSink { private final byte[] address; @@ -131,7 +102,7 @@ public long getValue () } } - public static Transaction createSpend (AddressToKeyMap am, List sources, List sinks, long fee) + public static Transaction createSpend (AddressToKeyMap am, List sources, List sinks, long fee) throws ValidationException { if ( fee < 0 || fee > 1000000 ) @@ -165,12 +136,11 @@ public static Transaction createSpend (AddressToKeyMap am, List Date: Sun, 23 Jun 2013 22:31:41 +0200 Subject: [PATCH 072/299] a slave needs to answer getdata to originate transaction --- server/src/main/resources/context/server.xml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/server/src/main/resources/context/server.xml b/server/src/main/resources/context/server.xml index d314beab..435be220 100644 --- a/server/src/main/resources/context/server.xml +++ b/server/src/main/resources/context/server.xml @@ -33,14 +33,15 @@ + + + + + + + - - - - - - - + From 089596435894ddb9f2a73568cccd6aecd43e130b Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Sun, 23 Jun 2013 22:32:27 +0200 Subject: [PATCH 073/299] prefer aggregation of UTXO over fragmentation. estimate fee --- .../supernode/api/InMemoryUTXO.java | 16 +++++++++++- .../supernode/api/Transaction.java | 25 ++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java index c40d56b1..795f3d72 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java @@ -17,6 +17,8 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -83,9 +85,21 @@ public long getTotal () public List getSufficientSources (long amount, long fee, String color) { + List candidates = new ArrayList (); + candidates.addAll (utxo.values ()); + Collections.sort (candidates, new Comparator () + { + // prefer aggregation of UTXO + @Override + public int compare (TransactionOutput o1, TransactionOutput o2) + { + return (int) (o1.getValue () - o2.getValue ()); + } + }); + List result = new ArrayList (); long sum = 0; - for ( TransactionOutput o : utxo.values () ) + for ( TransactionOutput o : candidates ) { if ( color == null ) { diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java b/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java index a309abde..99ca59cc 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java @@ -47,6 +47,9 @@ public class Transaction implements Serializable, Cloneable private List inputs; private List outputs; + private static final long MINIMUM_FEE = 10000; + private static final long MAXIMUM_FEE = 1000000; + public static Transaction createCoinbase (Key receiver, long value, int blockHeight) { Transaction cb = new Transaction (); @@ -102,10 +105,30 @@ public long getValue () } } + public static long estimateFee (Transaction t) + { + WireFormat.Writer writer = new WireFormat.Writer (); + t.toWire (writer); + return Math.min (MAXIMUM_FEE, Math.max (MINIMUM_FEE, writer.toByteArray ().length / 1000 * MINIMUM_FEE)); + } + + public static Transaction createSpend (AddressToKeyMap am, List sources, List sinks) throws ValidationException + { + long fee = MINIMUM_FEE; + long prevfee = MINIMUM_FEE; + Transaction t = createSpend (am, sources, sinks, fee); + while ( (fee = estimateFee (t)) > prevfee ) + { + t = createSpend (am, sources, sinks, fee); + prevfee = fee; + } + return t; + } + public static Transaction createSpend (AddressToKeyMap am, List sources, List sinks, long fee) throws ValidationException { - if ( fee < 0 || fee > 1000000 ) + if ( fee < 0 || fee > MAXIMUM_FEE ) { throw new ValidationException ("You unlikely want to do that"); } From 6c53e28769c5ff45948c20bf8e1c0bcf9cfd0d5f Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Mon, 24 Jun 2013 13:20:52 +0200 Subject: [PATCH 074/299] Extracted common account functionality into BaseAccountManager --- .../supernode/api/AccountManager.java | 19 +- .../supernode/api/AddressToKeyMap.java | 8 - .../supernode/api/BaseAccountManager.java | 633 ++++++++++++++++++ .../bitsofproof/supernode/api/FileWallet.java | 76 +-- .../supernode/api/InMemoryAccountManager.java | 418 +----------- .../supernode/api/Transaction.java | 221 ------ .../com/bitsofproof/supernode/api/Wallet.java | 13 +- .../bitsofproof/supernode/test/APITest.java | 3 +- 8 files changed, 692 insertions(+), 699 deletions(-) delete mode 100644 api/src/main/java/com/bitsofproof/supernode/api/AddressToKeyMap.java create mode 100644 api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java index 27ba6ff1..f1f91894 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java @@ -16,32 +16,27 @@ package com.bitsofproof.supernode.api; import java.util.Collection; -import java.util.List; import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ValidationException; -public interface AccountManager extends AddressToKeyMap +public interface AccountManager extends TransactionListener { - public void setApi (BCSAPI api); - public String getName (); public long getCreated (); - public boolean updateWithTransaction (Transaction t); - - public List getTransactions (); - - public Collection getAddresses (); + public ExtendedKey getMaster (); - public ExtendedKey getMasterKey (); + public Key getKey (int i) throws ValidationException; public Key getNextKey () throws ValidationException; - public Transaction pay (byte[] receiver, long amount, long fee) throws ValidationException, BCSAPIException; + public Collection getAddresses (); + + public Transaction pay (byte[] receiver, long amount, long fee) throws ValidationException; - public Transaction split (long[] amounts, long fee) throws ValidationException, BCSAPIException; + public Transaction split (long[] amounts, long fee) throws ValidationException; public long getBalance (); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AddressToKeyMap.java b/api/src/main/java/com/bitsofproof/supernode/api/AddressToKeyMap.java deleted file mode 100644 index fa005582..00000000 --- a/api/src/main/java/com/bitsofproof/supernode/api/AddressToKeyMap.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.bitsofproof.supernode.api; - -import com.bitsofproof.supernode.common.Key; - -public interface AddressToKeyMap -{ - public Key getKeyForAddress (byte[] address); -} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java new file mode 100644 index 00000000..141e14e7 --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java @@ -0,0 +1,633 @@ +package com.bitsofproof.supernode.api; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.bouncycastle.util.Arrays; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.ByteVector; +import com.bitsofproof.supernode.common.Key; +import com.bitsofproof.supernode.common.ScriptFormat; +import com.bitsofproof.supernode.common.ScriptFormat.Opcode; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.WireFormat; + +public abstract class BaseAccountManager implements AccountManager +{ + private static final Logger log = LoggerFactory.getLogger (BaseAccountManager.class); + + private static final long MINIMUM_FEE = 10000; + private static final long MAXIMUM_FEE = 1000000; + + private final InMemoryUTXO confirmed = new InMemoryUTXO (); + private final InMemoryUTXO change = new InMemoryUTXO (); + private final InMemoryUTXO receiving = new InMemoryUTXO (); + private final InMemoryUTXO sending = new InMemoryUTXO (); + + private final Map keyIDForAddress = new HashMap (); + private final long created; + private final String name; + + private ExtendedKey master; + private int nextSequence; + + private final List accountListener = Collections.synchronizedList (new ArrayList ()); + + public abstract void storeTransaction (Transaction t); + + public abstract void removeTransaction (Transaction t); + + public BaseAccountManager (String name, long created) + { + this.name = name; + this.created = created; + } + + @Override + public String getName () + { + return name; + } + + @Override + public long getCreated () + { + return created; + } + + @Override + public ExtendedKey getMaster () + { + return master; + } + + public void setMaster (ExtendedKey master) + { + this.master = master; + } + + public int getNextSequence () + { + return nextSequence; + } + + public void setNextSequence (int nextSequence) throws ValidationException + { + while ( this.nextSequence < nextSequence ) + { + getNextKey (); + } + this.nextSequence = nextSequence; + } + + @Override + public Key getKey (int i) throws ValidationException + { + if ( i >= nextSequence ) + { + throw new ValidationException ("Use consecutive keys"); + } + return master.getKey (i); + } + + @Override + public Key getNextKey () throws ValidationException + { + Key key = master.getKey (nextSequence); + keyIDForAddress.put (new ByteVector (key.getAddress ()), nextSequence); + ++nextSequence; + return key; + } + + public Integer getKeyIDForAddress (byte[] address) throws ValidationException + { + return keyIDForAddress.get (new ByteVector (address)); + } + + public Key getKeyForAddress (byte[] address) throws ValidationException + { + Integer keyId = getKeyIDForAddress (address); + if ( keyId == null ) + { + return null; + } + return master.getKey (keyId); + } + + @Override + public Collection getAddresses () + { + List addresses = new ArrayList (); + for ( ByteVector v : keyIDForAddress.keySet () ) + { + addresses.add (v.toByteArray ()); + } + return addresses; + } + + protected static class TransactionSink + { + private final byte[] address; + private final long value; + + public TransactionSink (byte[] address, long value) + { + super (); + this.address = Arrays.clone (address); + this.value = value; + } + + public byte[] getAddress () + { + return Arrays.clone (address); + } + + public long getValue () + { + return value; + } + } + + public List getSources (long amount, long fee, String color) + { + synchronized ( confirmed ) + { + return confirmed.getSufficientSources (amount, fee, null); + } + } + + public static long estimateFee (Transaction t) + { + WireFormat.Writer writer = new WireFormat.Writer (); + t.toWire (writer); + return Math.min (MAXIMUM_FEE, Math.max (MINIMUM_FEE, writer.toByteArray ().length / 1000 * MINIMUM_FEE)); + } + + public Transaction createSpend (List sources, List sinks) throws ValidationException + { + long fee = MINIMUM_FEE; + long prevfee = MINIMUM_FEE; + Transaction t = createSpend (sources, sinks, fee); + while ( (fee = estimateFee (t)) > prevfee ) + { + t = createSpend (sources, sinks, fee); + prevfee = fee; + } + return t; + } + + public Transaction createSpend (List sources, List sinks, long fee) throws ValidationException + { + if ( fee < 0 || fee > MAXIMUM_FEE ) + { + throw new ValidationException ("You unlikely want to do that"); + } + Transaction transaction = new Transaction (); + transaction.setInputs (new ArrayList ()); + transaction.setOutputs (new ArrayList ()); + + long sumOut = 0; + for ( TransactionSink s : sinks ) + { + TransactionOutput o = new TransactionOutput (); + o.setValue (s.getValue ()); + sumOut += s.getValue (); + + ScriptFormat.Writer writer = new ScriptFormat.Writer (); + writer.writeToken (new ScriptFormat.Token (Opcode.OP_DUP)); + writer.writeToken (new ScriptFormat.Token (Opcode.OP_HASH160)); + if ( s.getAddress ().length != 20 ) + { + throw new ValidationException ("Sink is not an address"); + } + writer.writeData (s.getAddress ()); + writer.writeToken (new ScriptFormat.Token (Opcode.OP_EQUALVERIFY)); + writer.writeToken (new ScriptFormat.Token (Opcode.OP_CHECKSIG)); + o.setScript (writer.toByteArray ()); + + transaction.getOutputs ().add (o); + } + + long sumInput = 0; + for ( TransactionOutput o : sources ) + { + TransactionInput i = new TransactionInput (); + i.setSourceHash (o.getTxHash ()); + i.setIx (o.getIx ()); + sumInput += o.getValue (); + + transaction.getInputs ().add (i); + } + if ( sumInput != (sumOut + fee) ) + { + throw new ValidationException ("Sum of sinks (+fee) does not match sum of sources"); + } + + int j = 0; + for ( TransactionOutput o : sources ) + { + TransactionInput i = transaction.getInputs ().get (j); + ScriptFormat.Writer sw = new ScriptFormat.Writer (); + byte[] address = o.getOutputAddress (); + if ( address == null ) + { + throw new ValidationException ("Can only spend pay to address outputs"); + } + Key key = getKeyForAddress (address); + if ( key == null ) + { + throw new ValidationException ("Have no key to spend this output"); + } + byte[] sig = key.sign (hashTransaction (transaction, j, ScriptFormat.SIGHASH_ALL, o.getScript ())); + byte[] sigPlusType = new byte[sig.length + 1]; + System.arraycopy (sig, 0, sigPlusType, 0, sig.length); + sigPlusType[sigPlusType.length - 1] = (byte) (ScriptFormat.SIGHASH_ALL & 0xff); + sw.writeData (sigPlusType); + sw.writeData (key.getPublic ()); + i.setScript (sw.toByteArray ()); + ++j; + } + + transaction.computeHash (); + return transaction; + } + + private static byte[] hashTransaction (Transaction transaction, int inr, int hashType, byte[] script) throws ValidationException + { + Transaction copy = null; + try + { + copy = transaction.clone (); + } + catch ( CloneNotSupportedException e1 ) + { + return null; + } + + // implicit SIGHASH_ALL + int i = 0; + for ( TransactionInput in : copy.getInputs () ) + { + if ( i == inr ) + { + in.setScript (script); + } + else + { + in.setScript (new byte[0]); + } + ++i; + } + + if ( (hashType & 0x1f) == ScriptFormat.SIGHASH_NONE ) + { + copy.getOutputs ().clear (); + i = 0; + for ( TransactionInput in : copy.getInputs () ) + { + if ( i != inr ) + { + in.setSequence (0); + } + ++i; + } + } + else if ( (hashType & 0x1f) == ScriptFormat.SIGHASH_SINGLE ) + { + int onr = inr; + if ( onr >= copy.getOutputs ().size () ) + { + // this is a Satoshi client bug. + // This case should throw an error but it instead retuns 1 that is not checked and interpreted as below + return ByteUtils.fromHex ("0100000000000000000000000000000000000000000000000000000000000000"); + } + for ( i = copy.getOutputs ().size () - 1; i > onr; --i ) + { + copy.getOutputs ().remove (i); + } + for ( i = 0; i < onr; ++i ) + { + copy.getOutputs ().get (i).setScript (new byte[0]); + copy.getOutputs ().get (i).setValue (-1L); + } + i = 0; + for ( TransactionInput in : copy.getInputs () ) + { + if ( i != inr ) + { + in.setSequence (0); + } + ++i; + } + } + if ( (hashType & ScriptFormat.SIGHASH_ANYONECANPAY) != 0 ) + { + List oneIn = new ArrayList (); + oneIn.add (copy.getInputs ().get (inr)); + copy.setInputs (oneIn); + } + + WireFormat.Writer writer = new WireFormat.Writer (); + copy.toWire (writer); + + byte[] txwire = writer.toByteArray (); + byte[] hash = null; + try + { + MessageDigest a = MessageDigest.getInstance ("SHA-256"); + a.update (txwire); + a.update (new byte[] { (byte) (hashType & 0xff), 0, 0, 0 }); + hash = a.digest (a.digest ()); + } + catch ( NoSuchAlgorithmException e ) + { + } + return hash; + } + + @Override + public Transaction pay (byte[] receiver, long amount, long fee) throws ValidationException + { + synchronized ( confirmed ) + { + log.trace ("pay to " + ByteUtils.toHex (receiver) + " " + amount + " + " + fee); + List sources = confirmed.getSufficientSources (amount, fee, null); + if ( sources == null ) + { + throw new ValidationException ("Insufficient funds to pay " + (amount + fee)); + } + List sinks = new ArrayList (); + + long in = 0; + for ( TransactionOutput o : sources ) + { + log.trace ("using input " + o.getTxHash () + "[" + o.getIx () + "] " + o.getValue ()); + in += o.getValue (); + } + TransactionSink target = new TransactionSink (receiver, amount); + if ( (in - amount - fee) > 0 ) + { + TransactionSink change = new TransactionSink (getNextKey ().getAddress (), in - amount - fee); + log.trace ("change to " + ByteUtils.toHex (change.getAddress ()) + " " + change.getValue ()); + if ( new SecureRandom ().nextBoolean () ) + { + sinks.add (target); + sinks.add (change); + } + else + { + sinks.add (change); + sinks.add (target); + } + } + else + { + sinks.add (target); + } + return createSpend (sources, sinks, fee); + } + } + + @Override + public Transaction split (long[] amounts, long fee) throws ValidationException + { + synchronized ( confirmed ) + { + List sinks = new ArrayList (); + + long amount = 0; + for ( long a : amounts ) + { + amount += a; + } + List sources = confirmed.getSufficientSources (amount, fee, null); + if ( sources == null ) + { + throw new ValidationException ("Insufficient funds to pay " + (amount + fee)); + } + long in = 0; + for ( TransactionOutput o : sources ) + { + in += o.getValue (); + } + for ( long a : amounts ) + { + TransactionSink target = new TransactionSink (getNextKey ().getAddress (), a); + sinks.add (target); + } + if ( (in - amount - fee) > 0 ) + { + TransactionSink change = new TransactionSink (getNextKey ().getAddress (), in - amount - fee); + sinks.add (change); + } + Collections.shuffle (sinks); + return createSpend (sources, sinks, fee); + } + } + + public boolean updateWithTransaction (Transaction t) + { + synchronized ( confirmed ) + { + boolean modified = false; + if ( !t.isDoubleSpend () ) + { + TransactionOutput spend = null; + for ( TransactionInput i : t.getInputs () ) + { + spend = confirmed.get (i.getSourceHash (), i.getIx ()); + if ( spend != null ) + { + confirmed.remove (i.getSourceHash (), i.getIx ()); + log.trace ("Spend settled output " + i.getSourceHash () + " [" + i.getIx () + "] " + spend.getValue ()); + } + else + { + spend = change.get (i.getSourceHash (), i.getIx ()); + if ( spend != null ) + { + change.remove (i.getSourceHash (), i.getIx ()); + log.trace ("Spend change output " + i.getSourceHash () + " [" + i.getIx () + "] " + spend.getValue ()); + } + else + { + spend = receiving.get (i.getSourceHash (), i.getIx ()); + if ( spend != null ) + { + receiving.remove (i.getSourceHash (), i.getIx ()); + log.trace ("Spend receiving output " + i.getSourceHash () + " [" + i.getIx () + "] " + spend.getValue ()); + } + } + } + } + modified = spend != null; + for ( TransactionOutput o : t.getOutputs () ) + { + confirmed.remove (o.getTxHash (), o.getIx ()); + change.remove (o.getTxHash (), o.getIx ()); + receiving.remove (o.getTxHash (), o.getIx ()); + sending.remove (o.getTxHash (), o.getIx ()); + + if ( keyIDForAddress.containsKey (new ByteVector (o.getOutputAddress ())) ) + { + modified = true; + if ( t.getBlockHash () != null ) + { + confirmed.add (o); + log.trace ("Settled " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + o.getValue ()); + } + else + { + if ( spend != null ) + { + change.add (o); + log.trace ("Change " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + + o.getValue ()); + } + else + { + receiving.add (o); + log.trace ("Receiving " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + + o.getValue ()); + } + } + } + else + { + if ( t.getBlockHash () == null && spend != null ) + { + modified = true; + sending.add (o); + log.trace ("Sending " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + o.getValue ()); + } + } + } + if ( modified ) + { + storeTransaction (t); + } + } + else + { + for ( long ix = 0; ix < t.getOutputs ().size (); ++ix ) + { + TransactionOutput out = null; + out = confirmed.remove (t.getHash (), ix); + if ( out == null ) + { + out = change.remove (t.getHash (), ix); + } + if ( out == null ) + { + out = receiving.remove (t.getHash (), ix); + } + if ( out == null ) + { + out = sending.remove (t.getHash (), ix); + } + if ( out != null ) + { + log.trace ("Remove DS " + out.getTxHash () + " [" + out.getIx () + "] (" + ByteUtils.toHex (out.getOutputAddress ()) + ")" + + out.getValue ()); + } + modified |= out != null; + } + removeTransaction (t); + } + return modified; + } + } + + @Override + public long getBalance () + { + synchronized ( confirmed ) + { + return confirmed.getTotal () + change.getTotal () + receiving.getTotal (); + } + } + + @Override + public long getSettled () + { + synchronized ( confirmed ) + { + return confirmed.getTotal (); + } + } + + @Override + public long getSending () + { + synchronized ( confirmed ) + { + return sending.getTotal (); + } + } + + @Override + public long getReceiving () + { + synchronized ( confirmed ) + { + return receiving.getTotal (); + } + } + + @Override + public long getChange () + { + synchronized ( confirmed ) + { + return change.getTotal (); + } + } + + @Override + public void addAccountListener (AccountListener listener) + { + accountListener.add (listener); + } + + @Override + public void removeAccountListener (AccountListener listener) + { + accountListener.remove (listener); + } + + protected void notifyListener (Transaction t) + { + synchronized ( accountListener ) + { + for ( AccountListener l : accountListener ) + { + try + { + l.accountChanged (this, t); + } + catch ( Exception e ) + { + } + } + } + } + + @Override + public void process (Transaction t) + { + if ( updateWithTransaction (t) ) + { + notifyListener (t); + } + } +} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java index 047f6146..c1562db3 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java @@ -9,21 +9,18 @@ import java.util.HashMap; import java.util.Map; -import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ValidationException; import com.google.protobuf.ByteString; public class FileWallet implements Wallet { - private BCSAPI api; - private static final SecureRandom random = new SecureRandom (); private transient ExtendedKey master; private byte[] encryptedSeed; private byte[] signature; private String fileName; - private final Map accounts = new HashMap (); + private final Map accounts = new HashMap (); public FileWallet (String fileName) { @@ -35,12 +32,6 @@ public boolean exists () return new File (fileName).exists (); } - @Override - public void setApi (BCSAPI api) - { - this.api = api; - } - @Override public void init (String passphrase) { @@ -64,12 +55,21 @@ public void unlock (String passphrase) throws ValidationException { throw new ValidationException ("incorrect passphrase"); } + int i = 0; + for ( InMemoryAccountManager account : accounts.values () ) + { + account.setMaster (master.getChild (i++ | 0x80000000)); + } } @Override public void lock () { master = null; + for ( InMemoryAccountManager account : accounts.values () ) + { + account.setMaster (account.getMaster ().getReadOnly ()); + } } public void setFileName (String fileName) @@ -77,31 +77,36 @@ public void setFileName (String fileName) this.fileName = fileName; } - @Override - public void read (String fileName, int lookAhead) throws IOException, ValidationException, BCSAPIException + public static FileWallet read (String fileName) throws IOException, ValidationException { - this.fileName = fileName; - master = null; + FileWallet wallet = new FileWallet (fileName); File f = new File (fileName); InputStream in = new FileInputStream (f); try { BCSAPIMessage.Wallet walletMessage = BCSAPIMessage.Wallet.parseFrom (in); - encryptedSeed = walletMessage.getEncryptedSeed ().toByteArray (); - signature = walletMessage.getSignature ().toByteArray (); + wallet.encryptedSeed = walletMessage.getEncryptedSeed ().toByteArray (); + wallet.signature = walletMessage.getSignature ().toByteArray (); for ( BCSAPIMessage.Wallet.Account account : walletMessage.getAccountsList () ) { - InMemoryAccountManager am = - new InMemoryAccountManager (this, account.getName (), ExtendedKey.parse (account.getPublicKey ()), account.getCreated ()); - am.setApi (api); - accounts.put (account.getName (), am); - am.sync (lookAhead, account.getCreated ()); + InMemoryAccountManager am = new InMemoryAccountManager (account.getName (), account.getCreated ()); + wallet.accounts.put (account.getName (), am); + am.setMaster (ExtendedKey.parse (account.getPublicKey ())); } } finally { in.close (); } + return wallet; + } + + public void sync (BCSAPI api, int lookAhead) throws BCSAPIException, ValidationException + { + for ( InMemoryAccountManager account : accounts.values () ) + { + account.sync (api, lookAhead, account.getCreated ()); + } } @Override @@ -111,7 +116,7 @@ public synchronized AccountManager getAccountManager (String name) } @Override - public synchronized AccountManager createAccountManager (String name) throws ValidationException, IOException, BCSAPIException + public synchronized AccountManager createAccountManager (String name) throws ValidationException { if ( accounts.containsKey (name) ) { @@ -123,31 +128,14 @@ public synchronized AccountManager createAccountManager (String name) throws Val { throw new ValidationException ("The wallet is locked"); } - InMemoryAccountManager account = - new InMemoryAccountManager (this, name, master.getChild (accounts.size () | 0x80000000), System.currentTimeMillis () / 1000); - account.setApi (api); - api.registerTransactionListener (account); + InMemoryAccountManager account = new InMemoryAccountManager (name, System.currentTimeMillis () / 1000); + account.setMaster (master.getChild (accounts.size () | 0x80000000)); accounts.put (name, account); - persist (); return account; } } - @Override - public Key getKey (AccountManager am, int sequence) throws ValidationException - { - if ( master == null ) - { - return am.getMasterKey ().getKey (sequence); - } - else - { - return master.getChild (am.getMasterKey ().getSequence ()).getKey (sequence); - } - } - - @Override - public void persist () throws IOException, BCSAPIException + public void persist () throws IOException { FileOutputStream out = new FileOutputStream (fileName); try @@ -156,12 +144,12 @@ public void persist () throws IOException, BCSAPIException builder.setBcsapiversion (1); builder.setEncryptedSeed (ByteString.copyFrom (encryptedSeed)); builder.setSignature (ByteString.copyFrom (signature)); - for ( AccountManager am : accounts.values () ) + for ( InMemoryAccountManager am : accounts.values () ) { BCSAPIMessage.Wallet.Account.Builder ab = BCSAPIMessage.Wallet.Account.newBuilder (); ab.setName (am.getName ()); ab.setCreated (am.getCreated ()); - ab.setPublicKey (am.getMasterKey ().getReadOnly ().serialize (true)); + ab.setPublicKey (am.getMaster ().getReadOnly ().serialize (true)); builder.addAccounts (ab.build ()); } builder.build ().writeTo (out); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java index 0e7ed83e..4fa7b2f4 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java @@ -15,76 +15,33 @@ */ package com.bitsofproof.supernode.api; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.bitsofproof.supernode.api.Transaction.TransactionSink; -import com.bitsofproof.supernode.common.ByteVector; -import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ScriptFormat; import com.bitsofproof.supernode.common.ScriptFormat.Token; import com.bitsofproof.supernode.common.ValidationException; -class InMemoryAccountManager implements TransactionListener, AccountManager +class InMemoryAccountManager extends BaseAccountManager implements TransactionListener { private static final Logger log = LoggerFactory.getLogger (InMemoryAccountManager.class); - private BCSAPI api; - - private final InMemoryUTXO confirmed = new InMemoryUTXO (); - private final InMemoryUTXO change = new InMemoryUTXO (); - private final InMemoryUTXO receiving = new InMemoryUTXO (); - private final InMemoryUTXO sending = new InMemoryUTXO (); - - private final List accountListener = Collections.synchronizedList (new ArrayList ()); - private final Map relevantTransaction = new HashMap (); - - private final String name; - private final ExtendedKey extended; - private final Map keyIDForAddress = new HashMap (); - private int nextKey; - private final long created; - private final Wallet wallet; - - @Override - public void setApi (BCSAPI api) - { - this.api = api; - } - - @Override - public long getCreated () - { - return created; - } - - public InMemoryAccountManager (Wallet wallet, String name, ExtendedKey extended, long created) + public InMemoryAccountManager (String name, long created) { - this.name = name; - this.extended = extended; - this.created = created; - this.wallet = wallet; + super (name, created); } - public void sync (final int lookAhead, long after) throws BCSAPIException, ValidationException + public void sync (BCSAPI api, final int lookAhead, long after) throws BCSAPIException, ValidationException { - for ( int i = 0; i < nextKey + lookAhead; ++i ) + final AtomicInteger lastUsedKey = new AtomicInteger (getNextSequence ()); + for ( int i = 0; i < lookAhead; ++i ) { - Key key = extended.getKey (i); - keyIDForAddress.put (new ByteVector (key.getAddress ()), i); + getNextKey (); } - final AtomicInteger lastUsedKey = new AtomicInteger (-1); - api.scanTransactions (extended, lookAhead, after, new TransactionListener () + + api.scanTransactions (getMaster (), lookAhead, after, new TransactionListener () { @Override public void process (Transaction t) @@ -97,19 +54,18 @@ public void process (Transaction t) { if ( token.data != null ) { - Integer thisKey = keyIDForAddress.get (new ByteVector (token.data)); + Integer thisKey = getKeyIDForAddress (token.data); if ( thisKey != null ) { lastUsedKey.set (Math.max (thisKey, lastUsedKey.get ())); } else { - while ( thisKey == null && (keyIDForAddress.size () - lastUsedKey.get ()) < lookAhead ) + while ( thisKey == null && (getNextSequence () - lastUsedKey.get ()) < lookAhead ) { - Key key = extended.getKey (keyIDForAddress.size ()); - keyIDForAddress.put (new ByteVector (key.getAddress ()), keyIDForAddress.size ()); + getNextKey (); } - thisKey = keyIDForAddress.get (new ByteVector (token.data)); + thisKey = getKeyIDForAddress (token.data); if ( thisKey != null ) { lastUsedKey.set (Math.max (thisKey, lastUsedKey.get ())); @@ -121,361 +77,21 @@ public void process (Transaction t) catch ( ValidationException e ) { } - updateWithTransaction (t); } + updateWithTransaction (t); } }); - nextKey = Math.max (lastUsedKey.get (), nextKey); + setNextSequence (Math.min (lastUsedKey.get (), getNextSequence ())); api.registerTransactionListener (this); } @Override - public String getName () - { - return name; - } - - @Override - public Collection getAddresses () - { - List addresses = new ArrayList (); - for ( ByteVector v : keyIDForAddress.keySet () ) - { - addresses.add (v.toByteArray ()); - } - return addresses; - } - - @Override - public Key getKeyForAddress (byte[] address) - { - Integer id = keyIDForAddress.get (new ByteVector (address)); - Key key = null; - try - { - key = wallet.getKey (this, id); - } - catch ( ValidationException e ) - { - } - return key; - } - - @Override - public Key getNextKey () throws ValidationException - { - Key key = extended.getKey (nextKey); - keyIDForAddress.put (new ByteVector (key.getAddress ()), nextKey); - ++nextKey; - return key; - } - - @Override - public boolean updateWithTransaction (Transaction t) - { - synchronized ( confirmed ) - { - boolean modified = false; - if ( !t.isDoubleSpend () ) - { - TransactionOutput spend = null; - for ( TransactionInput i : t.getInputs () ) - { - spend = confirmed.get (i.getSourceHash (), i.getIx ()); - if ( spend != null ) - { - confirmed.remove (i.getSourceHash (), i.getIx ()); - log.trace ("Spend settled output " + i.getSourceHash () + " " + i.getIx () + " " + spend.getValue ()); - } - else - { - spend = change.get (i.getSourceHash (), i.getIx ()); - if ( spend != null ) - { - change.remove (i.getSourceHash (), i.getIx ()); - log.trace ("Spend change output " + i.getSourceHash () + " " + i.getIx () + " " + spend.getValue ()); - } - else - { - spend = receiving.get (i.getSourceHash (), i.getIx ()); - if ( spend != null ) - { - receiving.remove (i.getSourceHash (), i.getIx ()); - log.trace ("Spend receiving output " + i.getSourceHash () + " " + i.getIx () + " " + spend.getValue ()); - } - } - } - } - modified = spend != null; - long ix = 0; - for ( TransactionOutput o : t.getOutputs () ) - { - confirmed.remove (t.getHash (), ix); - change.remove (t.getHash (), ix); - receiving.remove (t.getHash (), ix); - sending.remove (t.getHash (), ix); - - if ( keyIDForAddress.containsKey (new ByteVector (o.getOutputAddress ())) ) - { - modified = true; - if ( t.getBlockHash () != null ) - { - confirmed.add (o); - log.trace ("Settled " + ix + " " + t.getHash () + " " + o.getValue ()); - } - else - { - if ( spend != null ) - { - change.add (o); - log.trace ("Change " + ix + " " + o.getValue ()); - } - else - { - receiving.add (o); - log.trace ("Receiving " + ix + " " + o.getValue ()); - } - } - } - else - { - if ( t.getBlockHash () == null && spend != null ) - { - modified = true; - sending.add (o); - log.trace ("Sending " + ix + " " + o.getValue ()); - } - } - ++ix; - } - if ( modified ) - { - relevantTransaction.put (t.getHash (), t); - } - } - else - { - for ( long ix = 0; ix < t.getOutputs ().size (); ++ix ) - { - TransactionOutput out = null; - out = confirmed.remove (t.getHash (), ix); - if ( out == null ) - { - out = change.remove (t.getHash (), ix); - } - if ( out == null ) - { - out = receiving.remove (t.getHash (), ix); - } - if ( out == null ) - { - out = sending.remove (t.getHash (), ix); - } - if ( out != null ) - { - log.trace ("Remove double spend from account " + name + " tx: " + t.getHash () + " ix " + ix + " " + out.getValue ()); - } - modified |= out != null; - } - relevantTransaction.remove (t.getHash ()); - } - return modified; - } - } - - @Override - public List getTransactions () + public void storeTransaction (Transaction t) { - ArrayList tl = new ArrayList (); - tl.addAll (relevantTransaction.values ()); - Collections.sort (tl, new Comparator () - { - @Override - public int compare (Transaction a, Transaction b) - { - for ( TransactionInput in : b.getInputs () ) - { - if ( in.getSourceHash ().equals (a.getHash ()) ) - { - return -1; - } - } - for ( TransactionInput in : a.getInputs () ) - { - if ( in.getSourceHash ().equals (b.getHash ()) ) - { - return 1; - } - } - return 0; - } - }); - return tl; - } - - @Override - public void process (Transaction t) - { - if ( updateWithTransaction (t) ) - { - notifyListener (t); - } - } - - @Override - public Transaction pay (byte[] receiver, long amount, long fee) throws ValidationException, BCSAPIException - { - synchronized ( confirmed ) - { - List sources = confirmed.getSufficientSources (amount, fee, null); - if ( sources == null ) - { - throw new ValidationException ("Insufficient funds to pay " + (amount + fee)); - } - List sinks = new ArrayList (); - - long in = 0; - for ( TransactionOutput o : sources ) - { - in += o.getValue (); - } - TransactionSink target = new TransactionSink (receiver, amount); - if ( (in - amount - fee) > 0 ) - { - TransactionSink change = new TransactionSink (getNextKey ().getAddress (), in - amount - fee); - if ( new SecureRandom ().nextBoolean () ) - { - sinks.add (target); - sinks.add (change); - } - else - { - sinks.add (change); - sinks.add (target); - } - } - else - { - sinks.add (target); - } - return Transaction.createSpend (this, sources, sinks, fee); - } - } - - @Override - public Transaction split (long[] amounts, long fee) throws ValidationException, BCSAPIException - { - synchronized ( confirmed ) - { - List sinks = new ArrayList (); - - long amount = 0; - for ( long a : amounts ) - { - amount += a; - } - List sources = confirmed.getSufficientSources (amount, fee, null); - if ( sources == null ) - { - throw new ValidationException ("Insufficient funds to pay " + (amount + fee)); - } - long in = 0; - for ( TransactionOutput o : sources ) - { - in += o.getValue (); - } - for ( long a : amounts ) - { - TransactionSink target = new TransactionSink (getNextKey ().getAddress (), a); - sinks.add (target); - } - if ( (in - amount - fee) > 0 ) - { - TransactionSink change = new TransactionSink (getNextKey ().getAddress (), in - amount - fee); - sinks.add (change); - } - Collections.shuffle (sinks); - return Transaction.createSpend (this, sources, sinks, fee); - } - } - - @Override - public long getBalance () - { - synchronized ( confirmed ) - { - return confirmed.getTotal () + change.getTotal () + receiving.getTotal (); - } - } - - @Override - public long getSettled () - { - synchronized ( confirmed ) - { - return confirmed.getTotal (); - } - } - - @Override - public long getSending () - { - synchronized ( confirmed ) - { - return sending.getTotal (); - } - } - - @Override - public long getReceiving () - { - synchronized ( confirmed ) - { - return receiving.getTotal (); - } - } - - @Override - public long getChange () - { - synchronized ( confirmed ) - { - return change.getTotal (); - } - } - - @Override - public void addAccountListener (AccountListener listener) - { - accountListener.add (listener); - } - - @Override - public void removeAccountListener (AccountListener listener) - { - accountListener.remove (listener); - } - - private void notifyListener (Transaction t) - { - synchronized ( accountListener ) - { - for ( AccountListener l : accountListener ) - { - try - { - l.accountChanged (this, t); - } - catch ( Exception e ) - { - } - } - } } @Override - public ExtendedKey getMasterKey () + public void removeTransaction (Transaction t) { - return extended; } } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java b/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java index 99ca59cc..91303978 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java @@ -16,19 +16,14 @@ package com.bitsofproof.supernode.api; import java.io.Serializable; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; -import org.bouncycastle.util.Arrays; - import com.bitsofproof.supernode.common.ByteUtils; import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ScriptFormat; import com.bitsofproof.supernode.common.ScriptFormat.Opcode; -import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.common.WireFormat; import com.google.protobuf.ByteString; @@ -47,9 +42,6 @@ public class Transaction implements Serializable, Cloneable private List inputs; private List outputs; - private static final long MINIMUM_FEE = 10000; - private static final long MAXIMUM_FEE = 1000000; - public static Transaction createCoinbase (Key receiver, long value, int blockHeight) { Transaction cb = new Transaction (); @@ -82,219 +74,6 @@ public static Transaction createCoinbase (Key receiver, long value, int blockHei return cb; } - public static class TransactionSink - { - private final byte[] address; - private final long value; - - public TransactionSink (byte[] address, long value) - { - super (); - this.address = Arrays.clone (address); - this.value = value; - } - - public byte[] getAddress () - { - return Arrays.clone (address); - } - - public long getValue () - { - return value; - } - } - - public static long estimateFee (Transaction t) - { - WireFormat.Writer writer = new WireFormat.Writer (); - t.toWire (writer); - return Math.min (MAXIMUM_FEE, Math.max (MINIMUM_FEE, writer.toByteArray ().length / 1000 * MINIMUM_FEE)); - } - - public static Transaction createSpend (AddressToKeyMap am, List sources, List sinks) throws ValidationException - { - long fee = MINIMUM_FEE; - long prevfee = MINIMUM_FEE; - Transaction t = createSpend (am, sources, sinks, fee); - while ( (fee = estimateFee (t)) > prevfee ) - { - t = createSpend (am, sources, sinks, fee); - prevfee = fee; - } - return t; - } - - public static Transaction createSpend (AddressToKeyMap am, List sources, List sinks, long fee) - throws ValidationException - { - if ( fee < 0 || fee > MAXIMUM_FEE ) - { - throw new ValidationException ("You unlikely want to do that"); - } - Transaction transaction = new Transaction (); - transaction.setInputs (new ArrayList ()); - transaction.setOutputs (new ArrayList ()); - - long sumOut = 0; - for ( TransactionSink s : sinks ) - { - TransactionOutput o = new TransactionOutput (); - o.setValue (s.getValue ()); - sumOut += s.getValue (); - - ScriptFormat.Writer writer = new ScriptFormat.Writer (); - writer.writeToken (new ScriptFormat.Token (Opcode.OP_DUP)); - writer.writeToken (new ScriptFormat.Token (Opcode.OP_HASH160)); - if ( s.getAddress ().length != 20 ) - { - throw new ValidationException ("Sink is not an address"); - } - writer.writeData (s.getAddress ()); - writer.writeToken (new ScriptFormat.Token (Opcode.OP_EQUALVERIFY)); - writer.writeToken (new ScriptFormat.Token (Opcode.OP_CHECKSIG)); - o.setScript (writer.toByteArray ()); - - transaction.getOutputs ().add (o); - } - - long sumInput = 0; - for ( TransactionOutput o : sources ) - { - TransactionInput i = new TransactionInput (); - i.setSourceHash (o.getTxHash ()); - i.setIx (o.getIx ()); - sumInput += o.getValue (); - - transaction.getInputs ().add (i); - } - if ( sumInput != (sumOut + fee) ) - { - throw new ValidationException ("Sum of sinks (+fee) does not match sum of sources"); - } - - int j = 0; - for ( TransactionOutput o : sources ) - { - TransactionInput i = transaction.getInputs ().get (j); - ScriptFormat.Writer sw = new ScriptFormat.Writer (); - byte[] address = o.getOutputAddress (); - if ( address == null ) - { - throw new ValidationException ("Can only spend pay to address outputs"); - } - Key key = am.getKeyForAddress (address); - if ( key == null ) - { - throw new ValidationException ("Have no key to spend this output"); - } - byte[] sig = key.sign (hashTransaction (transaction, j, ScriptFormat.SIGHASH_ALL, o.getScript ())); - byte[] sigPlusType = new byte[sig.length + 1]; - System.arraycopy (sig, 0, sigPlusType, 0, sig.length); - sigPlusType[sigPlusType.length - 1] = (byte) (ScriptFormat.SIGHASH_ALL & 0xff); - sw.writeData (sigPlusType); - sw.writeData (key.getPublic ()); - i.setScript (sw.toByteArray ()); - ++j; - } - - transaction.computeHash (); - return transaction; - } - - private static byte[] hashTransaction (Transaction transaction, int inr, int hashType, byte[] script) throws ValidationException - { - Transaction copy = null; - try - { - copy = transaction.clone (); - } - catch ( CloneNotSupportedException e1 ) - { - return null; - } - - // implicit SIGHASH_ALL - int i = 0; - for ( TransactionInput in : copy.getInputs () ) - { - if ( i == inr ) - { - in.setScript (script); - } - else - { - in.setScript (new byte[0]); - } - ++i; - } - - if ( (hashType & 0x1f) == ScriptFormat.SIGHASH_NONE ) - { - copy.getOutputs ().clear (); - i = 0; - for ( TransactionInput in : copy.getInputs () ) - { - if ( i != inr ) - { - in.setSequence (0); - } - ++i; - } - } - else if ( (hashType & 0x1f) == ScriptFormat.SIGHASH_SINGLE ) - { - int onr = inr; - if ( onr >= copy.getOutputs ().size () ) - { - // this is a Satoshi client bug. - // This case should throw an error but it instead retuns 1 that is not checked and interpreted as below - return ByteUtils.fromHex ("0100000000000000000000000000000000000000000000000000000000000000"); - } - for ( i = copy.getOutputs ().size () - 1; i > onr; --i ) - { - copy.getOutputs ().remove (i); - } - for ( i = 0; i < onr; ++i ) - { - copy.getOutputs ().get (i).setScript (new byte[0]); - copy.getOutputs ().get (i).setValue (-1L); - } - i = 0; - for ( TransactionInput in : copy.getInputs () ) - { - if ( i != inr ) - { - in.setSequence (0); - } - ++i; - } - } - if ( (hashType & ScriptFormat.SIGHASH_ANYONECANPAY) != 0 ) - { - List oneIn = new ArrayList (); - oneIn.add (copy.getInputs ().get (inr)); - copy.setInputs (oneIn); - } - - WireFormat.Writer writer = new WireFormat.Writer (); - copy.toWire (writer); - - byte[] txwire = writer.toByteArray (); - byte[] hash = null; - try - { - MessageDigest a = MessageDigest.getInstance ("SHA-256"); - a.update (txwire); - a.update (new byte[] { (byte) (hashType & 0xff), 0, 0, 0 }); - hash = a.digest (a.digest ()); - } - catch ( NoSuchAlgorithmException e ) - { - } - return hash; - } - public long getVersion () { return version; diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java b/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java index bac7b2f9..8589966f 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java @@ -1,14 +1,9 @@ package com.bitsofproof.supernode.api; -import java.io.IOException; - -import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ValidationException; public interface Wallet { - public void setApi (BCSAPI api); - public void init (String passphrase); public void unlock (String passphrase) throws ValidationException; @@ -17,11 +12,5 @@ public interface Wallet public AccountManager getAccountManager (String name); - public AccountManager createAccountManager (String name) throws ValidationException, IOException, BCSAPIException; - - public void read (String name, int lookAhead) throws IOException, ValidationException, BCSAPIException; - - public void persist () throws IOException, BCSAPIException; - - public Key getKey (AccountManager am, int sequence) throws ValidationException; + public AccountManager createAccountManager (String name) throws ValidationException; } \ No newline at end of file diff --git a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java index 33f4320d..85e04db1 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java @@ -177,11 +177,12 @@ public void test () throws BCSAPIException, ValidationException, IOException store.cache (chain, 0); wallet = new FileWallet ("test.wallet"); wallet.init ("passphrase"); - wallet.setApi (api); wallet.unlock ("passphrase"); alice = wallet.createAccountManager ("Alice"); bob = wallet.createAccountManager ("Bob"); + api.registerTransactionListener (alice); + api.registerTransactionListener (bob); alice.addAccountListener (aliceMonitor); bob.addAccountListener (bobMonitor); From 9ecb2b2f474878cefed38ab2eb13841a48f91828 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Mon, 24 Jun 2013 14:40:52 +0200 Subject: [PATCH 075/299] Rename InMemory to ExtendedKeyAccountManager add KeyListAccountManager --- .../supernode/api/AccountManager.java | 4 - .../supernode/api/BaseAccountManager.java | 84 +------- .../api/ExtendedKeyAccountManager.java | 184 ++++++++++++++++++ .../bitsofproof/supernode/api/FileWallet.java | 16 +- .../supernode/api/InMemoryAccountManager.java | 97 --------- .../supernode/api/KeyListAccountManager.java | 84 ++++++++ 6 files changed, 281 insertions(+), 188 deletions(-) create mode 100644 api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java delete mode 100644 api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java create mode 100644 api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java index f1f91894..3ce3edef 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java @@ -26,10 +26,6 @@ public interface AccountManager extends TransactionListener public long getCreated (); - public ExtendedKey getMaster (); - - public Key getKey (int i) throws ValidationException; - public Key getNextKey () throws ValidationException; public Collection getAddresses (); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java index 141e14e7..d9d05257 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java @@ -4,18 +4,14 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.bouncycastle.util.Arrays; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.bitsofproof.supernode.common.ByteUtils; -import com.bitsofproof.supernode.common.ByteVector; import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ScriptFormat; import com.bitsofproof.supernode.common.ScriptFormat.Opcode; @@ -34,19 +30,19 @@ public abstract class BaseAccountManager implements AccountManager private final InMemoryUTXO receiving = new InMemoryUTXO (); private final InMemoryUTXO sending = new InMemoryUTXO (); - private final Map keyIDForAddress = new HashMap (); private final long created; private final String name; - private ExtendedKey master; - private int nextSequence; - private final List accountListener = Collections.synchronizedList (new ArrayList ()); public abstract void storeTransaction (Transaction t); public abstract void removeTransaction (Transaction t); + public abstract Key getKeyForAddress (byte[] address); + + public abstract Key getNextKey () throws ValidationException; + public BaseAccountManager (String name, long created) { this.name = name; @@ -65,76 +61,6 @@ public long getCreated () return created; } - @Override - public ExtendedKey getMaster () - { - return master; - } - - public void setMaster (ExtendedKey master) - { - this.master = master; - } - - public int getNextSequence () - { - return nextSequence; - } - - public void setNextSequence (int nextSequence) throws ValidationException - { - while ( this.nextSequence < nextSequence ) - { - getNextKey (); - } - this.nextSequence = nextSequence; - } - - @Override - public Key getKey (int i) throws ValidationException - { - if ( i >= nextSequence ) - { - throw new ValidationException ("Use consecutive keys"); - } - return master.getKey (i); - } - - @Override - public Key getNextKey () throws ValidationException - { - Key key = master.getKey (nextSequence); - keyIDForAddress.put (new ByteVector (key.getAddress ()), nextSequence); - ++nextSequence; - return key; - } - - public Integer getKeyIDForAddress (byte[] address) throws ValidationException - { - return keyIDForAddress.get (new ByteVector (address)); - } - - public Key getKeyForAddress (byte[] address) throws ValidationException - { - Integer keyId = getKeyIDForAddress (address); - if ( keyId == null ) - { - return null; - } - return master.getKey (keyId); - } - - @Override - public Collection getAddresses () - { - List addresses = new ArrayList (); - for ( ByteVector v : keyIDForAddress.keySet () ) - { - addresses.add (v.toByteArray ()); - } - return addresses; - } - protected static class TransactionSink { private final byte[] address; @@ -478,7 +404,7 @@ public boolean updateWithTransaction (Transaction t) receiving.remove (o.getTxHash (), o.getIx ()); sending.remove (o.getTxHash (), o.getIx ()); - if ( keyIDForAddress.containsKey (new ByteVector (o.getOutputAddress ())) ) + if ( getKeyForAddress (o.getOutputAddress ()) != null ) { modified = true; if ( t.getBlockHash () != null ) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java new file mode 100644 index 00000000..50d35722 --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java @@ -0,0 +1,184 @@ +/* + * Copyright 2013 bits of proof zrt. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bitsofproof.supernode.api; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.bitsofproof.supernode.common.ByteVector; +import com.bitsofproof.supernode.common.Key; +import com.bitsofproof.supernode.common.ScriptFormat; +import com.bitsofproof.supernode.common.ScriptFormat.Token; +import com.bitsofproof.supernode.common.ValidationException; + +public class ExtendedKeyAccountManager extends BaseAccountManager implements TransactionListener +{ + private static final Logger log = LoggerFactory.getLogger (ExtendedKeyAccountManager.class); + + private final Map keyIDForAddress = new HashMap (); + private ExtendedKey master; + private int nextSequence; + + public ExtendedKey getMaster () + { + return master; + } + + public void setMaster (ExtendedKey master) + { + this.master = master; + } + + public int getNextSequence () + { + return nextSequence; + } + + public void setNextSequence (int nextSequence) throws ValidationException + { + while ( this.nextSequence < nextSequence ) + { + getNextKey (); + } + this.nextSequence = nextSequence; + } + + public Key getKey (int i) throws ValidationException + { + if ( i >= nextSequence ) + { + throw new ValidationException ("Use consecutive keys"); + } + return master.getKey (i); + } + + @Override + public Key getNextKey () throws ValidationException + { + Key key = master.getKey (nextSequence); + keyIDForAddress.put (new ByteVector (key.getAddress ()), nextSequence); + ++nextSequence; + return key; + } + + public Integer getKeyIDForAddress (byte[] address) + { + return keyIDForAddress.get (new ByteVector (address)); + } + + @Override + public Key getKeyForAddress (byte[] address) + { + Integer keyId = getKeyIDForAddress (address); + if ( keyId == null ) + { + return null; + } + try + { + return getKey (keyId); + } + catch ( ValidationException e ) + { + return null; + } + } + + @Override + public Collection getAddresses () + { + List addresses = new ArrayList (); + for ( ByteVector v : keyIDForAddress.keySet () ) + { + addresses.add (v.toByteArray ()); + } + return addresses; + } + + public ExtendedKeyAccountManager (String name, long created) + { + super (name, created); + } + + public void sync (BCSAPI api, final int lookAhead) throws BCSAPIException, ValidationException + { + final AtomicInteger lastUsedKey = new AtomicInteger (getNextSequence ()); + for ( int i = 0; i < lookAhead; ++i ) + { + getNextKey (); + } + log.trace ("Sync " + getName () + " nkeys: " + getNextSequence ()); + api.scanTransactions (getMaster (), lookAhead, getCreated (), new TransactionListener () + { + @Override + public void process (Transaction t) + { + for ( TransactionOutput o : t.getOutputs () ) + { + try + { + for ( Token token : ScriptFormat.parse (o.getScript ()) ) + { + if ( token.data != null ) + { + Integer thisKey = getKeyIDForAddress (token.data); + if ( thisKey != null ) + { + lastUsedKey.set (Math.max (thisKey, lastUsedKey.get ())); + } + else + { + while ( thisKey == null && (getNextSequence () - lastUsedKey.get ()) < lookAhead ) + { + getNextKey (); + } + thisKey = getKeyIDForAddress (token.data); + if ( thisKey != null ) + { + lastUsedKey.set (Math.max (thisKey, lastUsedKey.get ())); + } + } + } + } + } + catch ( ValidationException e ) + { + } + } + updateWithTransaction (t); + } + }); + setNextSequence (Math.min (lastUsedKey.get (), getNextSequence ())); + log.trace ("Sync " + getName () + " finished with nkeys: " + getNextSequence ()); + } + + @Override + public void storeTransaction (Transaction t) + { + } + + @Override + public void removeTransaction (Transaction t) + { + } +} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java index c1562db3..0674f649 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java @@ -20,7 +20,7 @@ public class FileWallet implements Wallet private byte[] signature; private String fileName; - private final Map accounts = new HashMap (); + private final Map accounts = new HashMap (); public FileWallet (String fileName) { @@ -56,7 +56,7 @@ public void unlock (String passphrase) throws ValidationException throw new ValidationException ("incorrect passphrase"); } int i = 0; - for ( InMemoryAccountManager account : accounts.values () ) + for ( ExtendedKeyAccountManager account : accounts.values () ) { account.setMaster (master.getChild (i++ | 0x80000000)); } @@ -66,7 +66,7 @@ public void unlock (String passphrase) throws ValidationException public void lock () { master = null; - for ( InMemoryAccountManager account : accounts.values () ) + for ( ExtendedKeyAccountManager account : accounts.values () ) { account.setMaster (account.getMaster ().getReadOnly ()); } @@ -89,7 +89,7 @@ public static FileWallet read (String fileName) throws IOException, ValidationEx wallet.signature = walletMessage.getSignature ().toByteArray (); for ( BCSAPIMessage.Wallet.Account account : walletMessage.getAccountsList () ) { - InMemoryAccountManager am = new InMemoryAccountManager (account.getName (), account.getCreated ()); + ExtendedKeyAccountManager am = new ExtendedKeyAccountManager (account.getName (), account.getCreated ()); wallet.accounts.put (account.getName (), am); am.setMaster (ExtendedKey.parse (account.getPublicKey ())); } @@ -103,9 +103,9 @@ public static FileWallet read (String fileName) throws IOException, ValidationEx public void sync (BCSAPI api, int lookAhead) throws BCSAPIException, ValidationException { - for ( InMemoryAccountManager account : accounts.values () ) + for ( ExtendedKeyAccountManager account : accounts.values () ) { - account.sync (api, lookAhead, account.getCreated ()); + account.sync (api, lookAhead); } } @@ -128,7 +128,7 @@ public synchronized AccountManager createAccountManager (String name) throws Val { throw new ValidationException ("The wallet is locked"); } - InMemoryAccountManager account = new InMemoryAccountManager (name, System.currentTimeMillis () / 1000); + ExtendedKeyAccountManager account = new ExtendedKeyAccountManager (name, System.currentTimeMillis () / 1000); account.setMaster (master.getChild (accounts.size () | 0x80000000)); accounts.put (name, account); return account; @@ -144,7 +144,7 @@ public void persist () throws IOException builder.setBcsapiversion (1); builder.setEncryptedSeed (ByteString.copyFrom (encryptedSeed)); builder.setSignature (ByteString.copyFrom (signature)); - for ( InMemoryAccountManager am : accounts.values () ) + for ( ExtendedKeyAccountManager am : accounts.values () ) { BCSAPIMessage.Wallet.Account.Builder ab = BCSAPIMessage.Wallet.Account.newBuilder (); ab.setName (am.getName ()); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java deleted file mode 100644 index 4fa7b2f4..00000000 --- a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryAccountManager.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2013 bits of proof zrt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.bitsofproof.supernode.api; - -import java.util.concurrent.atomic.AtomicInteger; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.bitsofproof.supernode.common.ScriptFormat; -import com.bitsofproof.supernode.common.ScriptFormat.Token; -import com.bitsofproof.supernode.common.ValidationException; - -class InMemoryAccountManager extends BaseAccountManager implements TransactionListener -{ - private static final Logger log = LoggerFactory.getLogger (InMemoryAccountManager.class); - - public InMemoryAccountManager (String name, long created) - { - super (name, created); - } - - public void sync (BCSAPI api, final int lookAhead, long after) throws BCSAPIException, ValidationException - { - final AtomicInteger lastUsedKey = new AtomicInteger (getNextSequence ()); - for ( int i = 0; i < lookAhead; ++i ) - { - getNextKey (); - } - - api.scanTransactions (getMaster (), lookAhead, after, new TransactionListener () - { - @Override - public void process (Transaction t) - { - for ( TransactionOutput o : t.getOutputs () ) - { - try - { - for ( Token token : ScriptFormat.parse (o.getScript ()) ) - { - if ( token.data != null ) - { - Integer thisKey = getKeyIDForAddress (token.data); - if ( thisKey != null ) - { - lastUsedKey.set (Math.max (thisKey, lastUsedKey.get ())); - } - else - { - while ( thisKey == null && (getNextSequence () - lastUsedKey.get ()) < lookAhead ) - { - getNextKey (); - } - thisKey = getKeyIDForAddress (token.data); - if ( thisKey != null ) - { - lastUsedKey.set (Math.max (thisKey, lastUsedKey.get ())); - } - } - } - } - } - catch ( ValidationException e ) - { - } - } - updateWithTransaction (t); - } - }); - setNextSequence (Math.min (lastUsedKey.get (), getNextSequence ())); - api.registerTransactionListener (this); - } - - @Override - public void storeTransaction (Transaction t) - { - } - - @Override - public void removeTransaction (Transaction t) - { - } -} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java new file mode 100644 index 00000000..59ce58de --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java @@ -0,0 +1,84 @@ +package com.bitsofproof.supernode.api; + +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; +import com.bitsofproof.supernode.common.ByteVector; +import com.bitsofproof.supernode.common.ECKeyPair; +import com.bitsofproof.supernode.common.Key; +import com.bitsofproof.supernode.common.ValidationException; + +public class KeyListAccountManager extends BaseAccountManager +{ + private static final Logger log = LoggerFactory.getLogger (KeyListAccountManager.class); + + private final Map keyByAddress = new HashMap (); + private final List keys = new ArrayList (); + private static final SecureRandom rnd = new SecureRandom (); + + public KeyListAccountManager (String name, long created) + { + super (name, created); + } + + @Override + public Collection getAddresses () + { + List a = new ArrayList (); + for ( ByteVector b : keyByAddress.keySet () ) + { + a.add (b.toByteArray ()); + } + return a; + } + + @Override + public Key getKeyForAddress (byte[] address) + { + return keyByAddress.get (new ByteVector (address)); + } + + @Override + public Key getNextKey () throws ValidationException + { + return keys.get (rnd.nextInt (keys.size ())); + } + + public void addKey (ECKeyPair key) + { + keys.add (key); + keyByAddress.put (new ByteVector (key.getAddress ()), key); + } + + public void sync (BCSAPI api, long after) throws BCSAPIException, ValidationException + { + log.trace ("Sync " + getName () + " nkeys: " + keys.size ()); + api.scanTransactions (getAddresses (), UpdateMode.all, after, new TransactionListener () + { + @Override + public void process (Transaction t) + { + updateWithTransaction (t); + } + }); + log.trace ("Sync " + getName () + " finished nkeys: " + keys.size ()); + } + + @Override + public void storeTransaction (Transaction t) + { + } + + @Override + public void removeTransaction (Transaction t) + { + } +} From 2e77b1d366019b473501219c40dfac5f8c9053da Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Mon, 24 Jun 2013 16:22:44 +0200 Subject: [PATCH 076/299] use created time of account manager --- .../com/bitsofproof/supernode/api/KeyListAccountManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java index 59ce58de..3b7295fe 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java @@ -58,10 +58,10 @@ public void addKey (ECKeyPair key) keyByAddress.put (new ByteVector (key.getAddress ()), key); } - public void sync (BCSAPI api, long after) throws BCSAPIException, ValidationException + public void sync (BCSAPI api) throws BCSAPIException, ValidationException { log.trace ("Sync " + getName () + " nkeys: " + keys.size ()); - api.scanTransactions (getAddresses (), UpdateMode.all, after, new TransactionListener () + api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () { @Override public void process (Transaction t) From 7ed6f84a09e184ac496cceaf2ed5c6a93b408342 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Mon, 24 Jun 2013 17:54:12 +0200 Subject: [PATCH 077/299] added wallet dump --- .../supernode/api/AccountManager.java | 2 +- .../supernode/api/BaseAccountManager.java | 54 ++++++++++++++++++- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java index 3ce3edef..bd303075 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java @@ -36,7 +36,7 @@ public interface AccountManager extends TransactionListener public long getBalance (); - public long getSettled (); + public long getConfirmed (); public long getSending (); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java index d9d05257..5f9bcf66 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java @@ -1,17 +1,22 @@ package com.bitsofproof.supernode.api; +import java.io.PrintStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.bouncycastle.util.Arrays; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.ByteVector; +import com.bitsofproof.supernode.common.ECKeyPair; import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ScriptFormat; import com.bitsofproof.supernode.common.ScriptFormat.Opcode; @@ -41,6 +46,7 @@ public abstract class BaseAccountManager implements AccountManager public abstract Key getKeyForAddress (byte[] address); + @Override public abstract Key getNextKey () throws ValidationException; public BaseAccountManager (String name, long created) @@ -484,7 +490,7 @@ public long getBalance () } @Override - public long getSettled () + public long getConfirmed () { synchronized ( confirmed ) { @@ -556,4 +562,50 @@ public void process (Transaction t) notifyListener (t); } } + + public void dumpOutputs (PrintStream print) + { + print.println ("Confirmed:"); + dumpUTXO (print, confirmed); + print.println ("Receiving:"); + dumpUTXO (print, receiving); + print.println ("Change:"); + dumpUTXO (print, change); + print.println ("Sending:"); + dumpUTXO (print, sending); + } + + public void dumpKeys (PrintStream print) + { + Set addresses = new HashSet (); + for ( TransactionOutput o : confirmed.getUTXO () ) + { + addresses.add (new ByteVector (o.getOutputAddress ())); + } + for ( TransactionOutput o : change.getUTXO () ) + { + addresses.add (new ByteVector (o.getOutputAddress ())); + } + for ( TransactionOutput o : receiving.getUTXO () ) + { + addresses.add (new ByteVector (o.getOutputAddress ())); + } + for ( ByteVector b : addresses ) + { + Key k = getKeyForAddress (b.toByteArray ()); + if ( k != null ) + { + print.println (ECKeyPair.serializeWIF (k) + " (" + ByteUtils.toHex (b.toByteArray ()) + ")"); + } + } + } + + private void dumpUTXO (PrintStream print, InMemoryUTXO set) + { + for ( TransactionOutput o : set.getUTXO () ) + { + print.println (o.getTxHash () + "[" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + o.getValue ()); + } + print.println (); + } } From 7c651a721b096c79ba69c5967a5b7b6bb7ecd2b1 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Mon, 24 Jun 2013 18:41:55 +0200 Subject: [PATCH 078/299] add getNumberofKeys --- .../bitsofproof/supernode/api/AccountManager.java | 2 ++ .../supernode/api/ExtendedKeyAccountManager.java | 13 +++++++------ .../supernode/api/KeyListAccountManager.java | 6 ++++++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java index bd303075..5b8e6650 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java @@ -26,6 +26,8 @@ public interface AccountManager extends TransactionListener public long getCreated (); + public int getNumberOfKeys (); + public Key getNextKey () throws ValidationException; public Collection getAddresses (); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java index 50d35722..c02e5d59 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java @@ -49,7 +49,8 @@ public void setMaster (ExtendedKey master) this.master = master; } - public int getNextSequence () + @Override + public int getNumberOfKeys () { return nextSequence; } @@ -122,12 +123,12 @@ public ExtendedKeyAccountManager (String name, long created) public void sync (BCSAPI api, final int lookAhead) throws BCSAPIException, ValidationException { - final AtomicInteger lastUsedKey = new AtomicInteger (getNextSequence ()); + final AtomicInteger lastUsedKey = new AtomicInteger (getNumberOfKeys ()); for ( int i = 0; i < lookAhead; ++i ) { getNextKey (); } - log.trace ("Sync " + getName () + " nkeys: " + getNextSequence ()); + log.trace ("Sync " + getName () + " nkeys: " + getNumberOfKeys ()); api.scanTransactions (getMaster (), lookAhead, getCreated (), new TransactionListener () { @Override @@ -148,7 +149,7 @@ public void process (Transaction t) } else { - while ( thisKey == null && (getNextSequence () - lastUsedKey.get ()) < lookAhead ) + while ( thisKey == null && (getNumberOfKeys () - lastUsedKey.get ()) < lookAhead ) { getNextKey (); } @@ -168,8 +169,8 @@ public void process (Transaction t) updateWithTransaction (t); } }); - setNextSequence (Math.min (lastUsedKey.get (), getNextSequence ())); - log.trace ("Sync " + getName () + " finished with nkeys: " + getNextSequence ()); + setNextSequence (Math.min (lastUsedKey.get (), getNumberOfKeys ())); + log.trace ("Sync " + getName () + " finished with nkeys: " + getNumberOfKeys ()); } @Override diff --git a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java index 3b7295fe..c43627d1 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java @@ -29,6 +29,12 @@ public KeyListAccountManager (String name, long created) super (name, created); } + @Override + public int getNumberOfKeys () + { + return keys.size (); + } + @Override public Collection getAddresses () { From f345f38687e79d47a6eb378c9d595f087b00ab19 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Mon, 24 Jun 2013 20:32:34 +0200 Subject: [PATCH 079/299] set block hash if sending tx from trunk update --- .../src/main/java/com/bitsofproof/supernode/core/TxHandler.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index a829f3ba..7fa57d3c 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -361,6 +361,7 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo { for ( Tx tx : blk.getTransactions () ) { + tx.setBlockHash (blk.getHash ()); if ( unconfirmed.containsKey (tx.getHash ()) ) { unconfirmed.remove (tx.getHash ()); From 228425a88fad3126095e0c5e73ea6394cd87715f Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Mon, 24 Jun 2013 21:23:42 +0200 Subject: [PATCH 080/299] Add address list account manager (useful for BCCAPI) --- .../supernode/api/AccountManager.java | 11 +++ .../api/AddressListAccountManager.java | 81 +++++++++++++++++++ .../supernode/api/BaseAccountManager.java | 75 +++++++++++++++-- .../api/ExtendedKeyAccountManager.java | 10 --- .../supernode/api/InMemoryUTXO.java | 2 +- .../supernode/api/KeyListAccountManager.java | 10 --- 6 files changed, 161 insertions(+), 28 deletions(-) create mode 100644 api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java index 5b8e6650..8c78bde4 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java @@ -16,6 +16,7 @@ package com.bitsofproof.supernode.api; import java.util.Collection; +import java.util.List; import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ValidationException; @@ -46,6 +47,16 @@ public interface AccountManager extends TransactionListener public long getChange (); + public Collection getConfirmedOutputs (); + + public Collection getSendingOutputs (); + + public Collection getReceivingOutputs (); + + public Collection getChangeOutputs (); + + public List getTransactions (); + public void addAccountListener (AccountListener listener); public void removeAccountListener (AccountListener listener); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java new file mode 100644 index 00000000..436412a5 --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java @@ -0,0 +1,81 @@ +package com.bitsofproof.supernode.api; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; +import com.bitsofproof.supernode.common.ByteVector; +import com.bitsofproof.supernode.common.Key; +import com.bitsofproof.supernode.common.ValidationException; + +public class AddressListAccountManager extends BaseAccountManager +{ + private static final Logger log = LoggerFactory.getLogger (AddressListAccountManager.class); + + private final Set addresses = new HashSet (); + + public AddressListAccountManager (String name, long created) + { + super (name, created); + } + + @Override + public int getNumberOfKeys () + { + return addresses.size (); + } + + @Override + public Collection getAddresses () + { + List al = new ArrayList (); + for ( ByteVector b : addresses ) + { + al.add (b.toByteArray ()); + } + return al; + } + + @Override + public Key getKeyForAddress (byte[] address) + { + return null; + } + + @Override + public boolean isOwnAddress (byte[] address) + { + return addresses.contains (address); + } + + @Override + public Key getNextKey () throws ValidationException + { + return null; + } + + public void addAddress (byte[] address) + { + addresses.add (new ByteVector (address)); + } + + public void sync (BCSAPI api) throws BCSAPIException, ValidationException + { + log.trace ("Sync " + getName () + " naddr: " + addresses.size ()); + api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () + { + @Override + public void process (Transaction t) + { + updateWithTransaction (t); + } + }); + log.trace ("Sync " + getName () + " finished naddr: " + addresses.size ()); + } +} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java index 5f9bcf66..64aae485 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java @@ -5,9 +5,13 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import org.bouncycastle.util.Arrays; @@ -39,10 +43,7 @@ public abstract class BaseAccountManager implements AccountManager private final String name; private final List accountListener = Collections.synchronizedList (new ArrayList ()); - - public abstract void storeTransaction (Transaction t); - - public abstract void removeTransaction (Transaction t); + private final Map transactions = new HashMap (); public abstract Key getKeyForAddress (byte[] address); @@ -55,6 +56,11 @@ public BaseAccountManager (String name, long created) this.created = created; } + public boolean isOwnAddress (byte[] address) + { + return getKeyForAddress (address) != null; + } + @Override public String getName () { @@ -410,7 +416,7 @@ public boolean updateWithTransaction (Transaction t) receiving.remove (o.getTxHash (), o.getIx ()); sending.remove (o.getTxHash (), o.getIx ()); - if ( getKeyForAddress (o.getOutputAddress ()) != null ) + if ( isOwnAddress (o.getOutputAddress ()) ) { modified = true; if ( t.getBlockHash () != null ) @@ -446,7 +452,7 @@ public boolean updateWithTransaction (Transaction t) } if ( modified ) { - storeTransaction (t); + transactions.put (t.getHash (), t); } } else @@ -474,7 +480,7 @@ public boolean updateWithTransaction (Transaction t) } modified |= out != null; } - removeTransaction (t); + transactions.remove (t.getHash ()); } return modified; } @@ -525,6 +531,30 @@ public long getChange () } } + @Override + public Collection getConfirmedOutputs () + { + return confirmed.getUTXO (); + } + + @Override + public Collection getSendingOutputs () + { + return sending.getUTXO (); + } + + @Override + public Collection getReceivingOutputs () + { + return receiving.getUTXO (); + } + + @Override + public Collection getChangeOutputs () + { + return change.getUTXO (); + } + @Override public void addAccountListener (AccountListener listener) { @@ -563,6 +593,37 @@ public void process (Transaction t) } } + @Override + public List getTransactions () + { + List tl = new ArrayList (); + tl.addAll (transactions.values ()); + Collections.sort (tl, new Comparator () + { + @Override + public int compare (Transaction a, Transaction b) + { + for ( TransactionInput i : b.getInputs () ) + { + if ( i.getSourceHash ().equals (a.getHash ()) ) + { + return -1; + } + } + for ( TransactionInput i : a.getInputs () ) + { + if ( i.getSourceHash ().equals (b.getHash ()) ) + { + return 1; + } + } + return 0; + } + }); + + return tl; + } + public void dumpOutputs (PrintStream print) { print.println ("Confirmed:"); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java index c02e5d59..9fa87f0c 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java @@ -172,14 +172,4 @@ public void process (Transaction t) setNextSequence (Math.min (lastUsedKey.get (), getNumberOfKeys ())); log.trace ("Sync " + getName () + " finished with nkeys: " + getNumberOfKeys ()); } - - @Override - public void storeTransaction (Transaction t) - { - } - - @Override - public void removeTransaction (Transaction t) - { - } } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java index 795f3d72..0e789ec6 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java @@ -60,7 +60,7 @@ public void add (TransactionOutput out) public Collection getUTXO () { - return utxo.values (); + return Collections.unmodifiableCollection (utxo.values ()); } public TransactionOutput get (String tx, long ix) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java index c43627d1..e31eac3c 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java @@ -77,14 +77,4 @@ public void process (Transaction t) }); log.trace ("Sync " + getName () + " finished nkeys: " + keys.size ()); } - - @Override - public void storeTransaction (Transaction t) - { - } - - @Override - public void removeTransaction (Transaction t) - { - } } From 9568f099974858dcd951f9eb14a6646407f0c74a Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Mon, 24 Jun 2013 21:49:17 +0200 Subject: [PATCH 081/299] next key is last used + 1 --- .../bitsofproof/supernode/api/ExtendedKeyAccountManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java index 9fa87f0c..0e95a93b 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java @@ -123,7 +123,7 @@ public ExtendedKeyAccountManager (String name, long created) public void sync (BCSAPI api, final int lookAhead) throws BCSAPIException, ValidationException { - final AtomicInteger lastUsedKey = new AtomicInteger (getNumberOfKeys ()); + final AtomicInteger lastUsedKey = new AtomicInteger (-1); for ( int i = 0; i < lookAhead; ++i ) { getNextKey (); @@ -169,7 +169,7 @@ public void process (Transaction t) updateWithTransaction (t); } }); - setNextSequence (Math.min (lastUsedKey.get (), getNumberOfKeys ())); + setNextSequence (Math.min (lastUsedKey.get () + 1, getNumberOfKeys ())); log.trace ("Sync " + getName () + " finished with nkeys: " + getNumberOfKeys ()); } } From 7b36d8aa6ac5ed92dd0a2687824cb375167c49e3 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Mon, 24 Jun 2013 22:18:08 +0200 Subject: [PATCH 082/299] consecutive key check disables get key for addresses already known. deleted. --- .../bitsofproof/supernode/api/ExtendedKeyAccountManager.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java index 0e95a93b..9eba0e7d 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java @@ -66,10 +66,6 @@ public void setNextSequence (int nextSequence) throws ValidationException public Key getKey (int i) throws ValidationException { - if ( i >= nextSequence ) - { - throw new ValidationException ("Use consecutive keys"); - } return master.getKey (i); } From a2ae9f465fe70b8b43c5e6288872fa3af86fdd65 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 25 Jun 2013 10:30:39 +0200 Subject: [PATCH 083/299] send height in block messages --- .../supernode/core/ImplementBCSAPI.java | 53 ++++++++----------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index ff494939..b193868d 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -779,11 +779,10 @@ public Transaction doInTransaction (TransactionStatus status) public Block getBlock (final String hash) { log.trace ("get block " + hash); - final WireFormat.Writer writer = new WireFormat.Writer (); - new TransactionTemplate (transactionManager).execute (new TransactionCallbackWithoutResult () + Block block = new TransactionTemplate (transactionManager).execute (new TransactionCallback () { @Override - protected void doInTransactionWithoutResult (TransactionStatus status) + public Block doInTransaction (TransactionStatus status) { status.setRollbackOnly (); String h = hash; @@ -797,32 +796,26 @@ protected void doInTransactionWithoutResult (TransactionStatus status) b = store.getBlock (h); if ( b != null ) { - b.toWire (writer); + return toBCSAPIBlock (b); } } catch ( ValidationException e ) { } + return null; } }); - byte[] blockdump = writer.toByteArray (); - if ( blockdump != null && blockdump.length > 0 ) - { - log.trace ("get block returned " + hash); - return Block.fromWire (new WireFormat.Reader (writer.toByteArray ())); - } - log.trace ("get block failed "); - return null; + log.trace ("get block returned " + block != null ? hash : "null"); + return block; } public Block getBlockHeader (final String hash) { log.trace ("get block header " + hash); - final WireFormat.Writer writer = new WireFormat.Writer (); - new TransactionTemplate (transactionManager).execute (new TransactionCallbackWithoutResult () + Block block = new TransactionTemplate (transactionManager).execute (new TransactionCallback () { @Override - protected void doInTransactionWithoutResult (TransactionStatus status) + public Block doInTransaction (TransactionStatus status) { status.setRollbackOnly (); String h = hash; @@ -836,22 +829,17 @@ protected void doInTransactionWithoutResult (TransactionStatus status) b = store.getBlockHeader (h); if ( b != null ) { - b.toWire (writer); + return toBCSAPIBlock (b); } } catch ( ValidationException e ) { } + return null; } }); - byte[] blockdump = writer.toByteArray (); - if ( blockdump != null && blockdump.length > 0 ) - { - log.trace ("get block header returned " + hash); - return Block.fromWire (new WireFormat.Reader (writer.toByteArray ())); - } - log.trace ("get block header failed "); - return null; + log.trace ("get block header returned " + block != null ? hash : "null"); + return block; } private Color getColor (final String hash) @@ -964,6 +952,15 @@ private Transaction toBCSAPITransaction (Tx tx, boolean doubleSpend) return transaction; } + private Block toBCSAPIBlock (Blk b) + { + WireFormat.Writer writer = new WireFormat.Writer (); + b.toWire (writer); + Block block = Block.fromWire (new WireFormat.Reader (writer.toByteArray ())); + block.setHeight (b.getHeight ()); + return block; + } + @Override public void trunkUpdate (final List removed, final List extended) { @@ -973,15 +970,11 @@ public void trunkUpdate (final List removed, final List extended) List a = new ArrayList (); for ( Blk blk : removed ) { - WireFormat.Writer writer = new WireFormat.Writer (); - blk.toWire (writer); - r.add (Block.fromWire (new WireFormat.Reader (writer.toByteArray ()))); + r.add (toBCSAPIBlock (blk)); } for ( Blk blk : extended ) { - WireFormat.Writer writer = new WireFormat.Writer (); - blk.toWire (writer); - a.add (Block.fromWire (new WireFormat.Reader (writer.toByteArray ()))); + r.add (toBCSAPIBlock (blk)); } TrunkUpdateMessage tu = new TrunkUpdateMessage (a, r); BytesMessage m = session.createBytesMessage (); From b89e6500731a87cb538c98214563d151592c7043 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 25 Jun 2013 10:51:18 +0200 Subject: [PATCH 084/299] missed trunk add --- .../java/com/bitsofproof/supernode/core/ImplementBCSAPI.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index b193868d..10a78f96 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -974,7 +974,7 @@ public void trunkUpdate (final List removed, final List extended) } for ( Blk blk : extended ) { - r.add (toBCSAPIBlock (blk)); + a.add (toBCSAPIBlock (blk)); } TrunkUpdateMessage tu = new TrunkUpdateMessage (a, r); BytesMessage m = session.createBytesMessage (); From ad592b3d5537f19fbab71c70fd286d9df5a10905 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 25 Jun 2013 11:29:47 +0200 Subject: [PATCH 085/299] 1.1.1 --- api/pom.xml | 2 +- pom.xml | 2 +- server/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 8d7e5454..f0013261 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1 + 1.1.1 ../ diff --git a/pom.xml b/pom.xml index 0a91dc36..19931cce 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.1 + 1.1.1 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/pom.xml b/server/pom.xml index 88792611..501af946 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1 + 1.1.1 ../ From 692c02fd8476a6abc29ae3b72fe6ac1ab1142221 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 25 Jun 2013 15:20:10 +0200 Subject: [PATCH 086/299] fixing address list account manager address comparison --- api/pom.xml | 2 +- .../bitsofproof/supernode/api/AddressListAccountManager.java | 2 +- pom.xml | 2 +- server/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index f0013261..eaf8694d 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.1 + 1.1.2 ../ diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java index 436412a5..ae388232 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java @@ -51,7 +51,7 @@ public Key getKeyForAddress (byte[] address) @Override public boolean isOwnAddress (byte[] address) { - return addresses.contains (address); + return addresses.contains (new ByteVector (address)); } @Override diff --git a/pom.xml b/pom.xml index 19931cce..2c1de0ea 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.1.1 + 1.1.2 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/pom.xml b/server/pom.xml index 501af946..c909467c 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.1 + 1.1.2 ../ From 9ebe4bf60525428d27c61ebf079952c02440604b Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 25 Jun 2013 15:22:08 +0200 Subject: [PATCH 087/299] ignore logs --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 86532d2b..5330893e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ target .settings .factorypath data +/logs From 5c99c4d461d531032295f24d19f1e01989d60d1a Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 25 Jun 2013 16:00:59 +0200 Subject: [PATCH 088/299] refer current version in startup script --- server/bop-service.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/bop-service.sh b/server/bop-service.sh index 6ae9ee78..0cd8b9d0 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.2.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.2.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" From d707cf01a486005565c958da7c90c7a658eabbe0 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 25 Jun 2013 21:49:33 +0200 Subject: [PATCH 089/299] ByteMessage is a stream! synchronized session object. --- .../supernode/api/JMSServerConnector.java | 162 +++++++++++------- 1 file changed, 101 insertions(+), 61 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java index efbda9a9..57a2e2e6 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java @@ -82,9 +82,14 @@ public void setTimeout (long timeout) this.timeout = timeout; } + private interface ByteArrayMessageListener + { + public void onMessage (byte[] array); + } + private class MessageDispatcher { - private final Map wrapperMap = new HashMap (); + private final Map wrapperMap = new HashMap (); private final MessageConsumer consumer; private TemporaryQueue temporaryQueue; @@ -99,14 +104,35 @@ public MessageDispatcher (MessageConsumer consumer) @Override public void onMessage (Message message) { - List listenerList = new ArrayList (); + List listenerList = new ArrayList (); synchronized ( wrapperMap ) { listenerList.addAll (wrapperMap.values ()); } - for ( MessageListener listener : listenerList ) + BytesMessage m = (BytesMessage) message; + byte[] body; + try + { + if ( m.getBodyLength () > 0 ) + { + body = new byte[(int) m.getBodyLength ()]; + m.readBytes (body); + for ( ByteArrayMessageListener listener : listenerList ) + { + listener.onMessage (body); + } + } + else + { + for ( ByteArrayMessageListener listener : listenerList ) + { + listener.onMessage (null); + } + } + } + catch ( JMSException e ) { - listener.onMessage (message); + log.error ("JMS Error ", e); } } }); @@ -117,7 +143,7 @@ public void onMessage (Message message) } } - public void addListener (Object inner, MessageListener listener) + public void addListener (Object inner, ByteArrayMessageListener listener) { synchronized ( wrapperMap ) { @@ -168,15 +194,23 @@ public void setConnectionFactory (ConnectionFactory connectionFactory) this.connectionFactory = connectionFactory; } - private void addTopicListener (String topic, Object inner, MessageListener listener) throws JMSException + private Destination createTopic (String topic) throws JMSException + { + synchronized ( session ) + { + return session.createTopic (topic); + } + } + + private void addTopicListener (String topic, Object inner, ByteArrayMessageListener listener) throws JMSException { synchronized ( messageDispatcher ) { MessageDispatcher dispatcher = messageDispatcher.get (topic); if ( dispatcher == null ) { - Destination destination = session.createTopic (topic); - MessageConsumer consumer = session.createConsumer (destination); + Destination destination = createTopic (topic); + MessageConsumer consumer = createConsumer (destination); messageDispatcher.put (topic, dispatcher = new MessageDispatcher (consumer)); } dispatcher.addListener (inner, listener); @@ -233,6 +267,14 @@ public void init () } } + private BytesMessage createBytesMessage () throws JMSException + { + synchronized ( session ) + { + return session.createBytesMessage (); + } + } + public void destroy () { try @@ -251,7 +293,7 @@ public long ping (long nonce) throws BCSAPIException try { log.trace ("ping " + nonce); - BytesMessage m = session.createBytesMessage (); + BytesMessage m = createBytesMessage (); BCSAPIMessage.Ping.Builder builder = BCSAPIMessage.Ping.newBuilder (); builder.setBcsapiversion (1); builder.setNonce (nonce); @@ -283,24 +325,21 @@ public void addAlertListener (final AlertListener alertListener) throws BCSAPIEx { try { - addTopicListener ("alert", alertListener, new MessageListener () + addTopicListener ("alert", alertListener, new ByteArrayMessageListener () { @Override - public void onMessage (Message arg0) + public void onMessage (byte[] body) { - BytesMessage o = (BytesMessage) arg0; + BCSAPIMessage.Alert alert; try { - byte[] body = new byte[(int) o.getBodyLength ()]; - o.readBytes (body); - BCSAPIMessage.Alert alert = BCSAPIMessage.Alert.parseFrom (body); + alert = BCSAPIMessage.Alert.parseFrom (body); alertListener.alert (alert.getAlert (), alert.getSeverity ()); } - catch ( Exception e ) + catch ( InvalidProtocolBufferException e ) { - log.error ("Alert message error", e); + log.error ("Message format error", e); } - } }); } @@ -326,12 +365,28 @@ public boolean isProduction () throws BCSAPIException return production = getBlockHeader ("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f") != null; } + private TemporaryQueue createTemporaryQueue () throws JMSException + { + synchronized ( session ) + { + return session.createTemporaryQueue (); + } + } + + private MessageConsumer createConsumer (Destination destination) throws JMSException + { + synchronized ( session ) + { + return session.createConsumer (destination); + } + } + @Override public void scanTransactions (Collection match, UpdateMode mode, long after, final TransactionListener listener) throws BCSAPIException { try { - BytesMessage m = session.createBytesMessage (); + BytesMessage m = createBytesMessage (); BCSAPIMessage.ExactMatchRequest.Builder builder = BCSAPIMessage.ExactMatchRequest.newBuilder (); builder.setBcsapiversion (1); @@ -345,8 +400,8 @@ public void scanTransactions (Collection match, UpdateMode mode, long af builder.setAfter (after); } m.writeBytes (builder.build ().toByteArray ()); - final TemporaryQueue answerQueue = session.createTemporaryQueue (); - final MessageConsumer consumer = session.createConsumer (answerQueue); + final TemporaryQueue answerQueue = createTemporaryQueue (); + final MessageConsumer consumer = createConsumer (answerQueue); m.setJMSReplyTo (answerQueue); final Semaphore ready = new Semaphore (0); consumer.setMessageListener (new MessageListener () @@ -405,7 +460,7 @@ public void scanTransactions (BloomFilter filter, final TransactionListener list { try { - BytesMessage m = session.createBytesMessage (); + BytesMessage m = createBytesMessage (); BCSAPIMessage.FilterRequest.Builder builder = BCSAPIMessage.FilterRequest.newBuilder (); builder.setBcsapiversion (1); @@ -416,8 +471,8 @@ public void scanTransactions (BloomFilter filter, final TransactionListener list m.writeBytes (builder.build ().toByteArray ()); - final TemporaryQueue answerQueue = session.createTemporaryQueue (); - final MessageConsumer consumer = session.createConsumer (answerQueue); + final TemporaryQueue answerQueue = createTemporaryQueue (); + final MessageConsumer consumer = createConsumer (answerQueue); m.setJMSReplyTo (answerQueue); final Semaphore ready = new Semaphore (0); consumer.setMessageListener (new MessageListener () @@ -480,7 +535,7 @@ public void scanTransactions (ExtendedKey master, int lookAhead, long after, fin } try { - BytesMessage m = session.createBytesMessage (); + BytesMessage m = createBytesMessage (); BCSAPIMessage.AccountRequest.Builder builder = BCSAPIMessage.AccountRequest.newBuilder (); builder.setBcsapiversion (1); @@ -489,8 +544,8 @@ public void scanTransactions (ExtendedKey master, int lookAhead, long after, fin builder.setAfter (after); m.writeBytes (builder.build ().toByteArray ()); - final TemporaryQueue answerQueue = session.createTemporaryQueue (); - final MessageConsumer consumer = session.createConsumer (answerQueue); + final TemporaryQueue answerQueue = createTemporaryQueue (); + final MessageConsumer consumer = createConsumer (answerQueue); m.setJMSReplyTo (answerQueue); final Semaphore ready = new Semaphore (0); consumer.setMessageListener (new MessageListener () @@ -550,7 +605,7 @@ public void registerFilteredListener (final BloomFilter filter, final Transactio { try { - BytesMessage m = session.createBytesMessage (); + BytesMessage m = createBytesMessage (); BCSAPIMessage.FilterRequest.Builder builder = BCSAPIMessage.FilterRequest.newBuilder (); builder.setBcsapiversion (1); builder.setFilter (ByteString.copyFrom (filter.getFilter ())); @@ -564,24 +619,20 @@ public void registerFilteredListener (final BloomFilter filter, final Transactio MessageDispatcher dispatcher = messageDispatcher.get (filter.toString ()); if ( dispatcher == null ) { - TemporaryQueue answerQueue = session.createTemporaryQueue (); - MessageConsumer consumer = session.createConsumer (answerQueue); + TemporaryQueue answerQueue = createTemporaryQueue (); + MessageConsumer consumer = createConsumer (answerQueue); dispatcher = new MessageDispatcher (consumer); dispatcher.setTemporaryQueue (answerQueue); messageDispatcher.put (filter.toString (), dispatcher); - dispatcher.addListener (listener, new MessageListener () + dispatcher.addListener (listener, new ByteArrayMessageListener () { @Override - public void onMessage (Message message) + public void onMessage (byte[] body) { - BytesMessage m = (BytesMessage) message; - byte[] body; try { - if ( m.getBodyLength () > 0 ) + if ( body != null ) { - body = new byte[(int) m.getBodyLength ()]; - m.readBytes (body); Transaction t = Transaction.fromProtobuf (BCSAPIMessage.Transaction.parseFrom (body)); t.computeHash (); listener.process (t); @@ -591,10 +642,6 @@ public void onMessage (Message message) listener.process (null); } } - catch ( JMSException e ) - { - log.error ("Malformed message received for filter", e); - } catch ( InvalidProtocolBufferException e ) { log.error ("Malformed message received for filter", e); @@ -644,16 +691,13 @@ public void registerTransactionListener (final TransactionListener listener) thr { try { - addTopicListener ("transaction", listener, new MessageListener () + addTopicListener ("transaction", listener, new ByteArrayMessageListener () { @Override - public void onMessage (Message arg0) + public void onMessage (byte[] body) { - BytesMessage o = (BytesMessage) arg0; try { - byte[] body = new byte[(int) o.getBodyLength ()]; - o.readBytes (body); Transaction t = Transaction.fromProtobuf (BCSAPIMessage.Transaction.parseFrom (body)); t.computeHash (); listener.process (t); @@ -662,7 +706,6 @@ public void onMessage (Message arg0) { log.debug ("Transaction message error", e); } - } }); } @@ -683,16 +726,13 @@ public void registerTrunkListener (final TrunkListener listener) throws BCSAPIEx { try { - addTopicListener ("trunk", listener, new MessageListener () + addTopicListener ("trunk", listener, new ByteArrayMessageListener () { @Override - public void onMessage (Message message) + public void onMessage (byte[] body) { try { - BytesMessage m = (BytesMessage) message; - byte[] body = new byte[(int) m.getBodyLength ()]; - m.readBytes (body); TrunkUpdateMessage tu = TrunkUpdateMessage.fromProtobuf (BCSAPIMessage.TrunkUpdate.parseFrom (body)); if ( tu.getRemoved () != null ) { @@ -736,7 +776,7 @@ private byte[] synchronousRequest (MessageProducer producer, Message m) throws B { try { - answerQueue = session.createTemporaryQueue (); + answerQueue = createTemporaryQueue (); } catch ( JMSException e ) { @@ -766,7 +806,7 @@ private byte[] synchronousRequest (MessageProducer producer, Message m, Queue an try { m.setJMSReplyTo (answerQueue); - consumer = session.createConsumer (answerQueue); + consumer = createConsumer (answerQueue); consumer.setMessageListener (new MessageListener () { @Override @@ -846,7 +886,7 @@ public Transaction getTransaction (String hash) throws BCSAPIException BytesMessage m; try { - m = session.createBytesMessage (); + m = createBytesMessage (); BCSAPIMessage.Hash.Builder builder = BCSAPIMessage.Hash.newBuilder (); builder.setBcsapiversion (1); builder.addHash (ByteString.copyFrom (new Hash (hash).toByteArray ())); @@ -877,7 +917,7 @@ public Block getBlock (String hash) throws BCSAPIException try { log.trace ("get block " + hash); - BytesMessage m = session.createBytesMessage (); + BytesMessage m = createBytesMessage (); BCSAPIMessage.Hash.Builder builder = BCSAPIMessage.Hash.newBuilder (); builder.setBcsapiversion (1); builder.addHash (ByteString.copyFrom (new Hash (hash).toByteArray ())); @@ -907,7 +947,7 @@ public Block getBlockHeader (String hash) throws BCSAPIException try { log.trace ("get block header" + hash); - BytesMessage m = session.createBytesMessage (); + BytesMessage m = createBytesMessage (); BCSAPIMessage.Hash.Builder builder = BCSAPIMessage.Hash.newBuilder (); builder.setBcsapiversion (1); builder.addHash (ByteString.copyFrom (new Hash (hash).toByteArray ())); @@ -938,7 +978,7 @@ public void sendTransaction (Transaction transaction) throws BCSAPIException { transaction.computeHash (); log.trace ("send transaction " + transaction.getHash ()); - BytesMessage m = session.createBytesMessage (); + BytesMessage m = createBytesMessage (); m.writeBytes (transaction.toProtobuf ().toByteArray ()); byte[] reply = synchronousRequest (transactionProducer, m); if ( reply != null ) @@ -967,7 +1007,7 @@ public void sendBlock (Block block) throws BCSAPIException { block.computeHash (); log.trace ("send block " + block.getHash ()); - BytesMessage m = session.createBytesMessage (); + BytesMessage m = createBytesMessage (); m.writeBytes (block.toProtobuf ().toByteArray ()); byte[] reply = synchronousRequest (blockProducer, m); if ( reply != null ) @@ -994,7 +1034,7 @@ public void issueColor (Color color) throws BCSAPIException try { log.trace ("issue color " + color.getTerms ()); - BytesMessage m = session.createBytesMessage (); + BytesMessage m = createBytesMessage (); m.writeBytes (color.toProtobuf ().toByteArray ()); byte[] reply = synchronousRequest (colorProducer, m); if ( reply != null ) @@ -1022,7 +1062,7 @@ public Color getColor (String digest) throws BCSAPIException BytesMessage m; try { - m = session.createBytesMessage (); + m = createBytesMessage (); BCSAPIMessage.Hash.Builder builder = BCSAPIMessage.Hash.newBuilder (); builder.setBcsapiversion (1); builder.addHash (ByteString.copyFrom (new Hash (digest).toByteArray ())); From 86ed71a65c7c472452b4ba0023d712cb89dc6c2d Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 25 Jun 2013 22:54:56 +0200 Subject: [PATCH 090/299] revert session sync --- .../supernode/api/JMSServerConnector.java | 80 ++++++------------- 1 file changed, 24 insertions(+), 56 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java index 57a2e2e6..d23484e5 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java @@ -194,14 +194,6 @@ public void setConnectionFactory (ConnectionFactory connectionFactory) this.connectionFactory = connectionFactory; } - private Destination createTopic (String topic) throws JMSException - { - synchronized ( session ) - { - return session.createTopic (topic); - } - } - private void addTopicListener (String topic, Object inner, ByteArrayMessageListener listener) throws JMSException { synchronized ( messageDispatcher ) @@ -209,8 +201,8 @@ private void addTopicListener (String topic, Object inner, ByteArrayMessageListe MessageDispatcher dispatcher = messageDispatcher.get (topic); if ( dispatcher == null ) { - Destination destination = createTopic (topic); - MessageConsumer consumer = createConsumer (destination); + Destination destination = session.createTopic (topic); + MessageConsumer consumer = session.createConsumer (destination); messageDispatcher.put (topic, dispatcher = new MessageDispatcher (consumer)); } dispatcher.addListener (inner, listener); @@ -267,14 +259,6 @@ public void init () } } - private BytesMessage createBytesMessage () throws JMSException - { - synchronized ( session ) - { - return session.createBytesMessage (); - } - } - public void destroy () { try @@ -293,7 +277,7 @@ public long ping (long nonce) throws BCSAPIException try { log.trace ("ping " + nonce); - BytesMessage m = createBytesMessage (); + BytesMessage m = session.createBytesMessage (); BCSAPIMessage.Ping.Builder builder = BCSAPIMessage.Ping.newBuilder (); builder.setBcsapiversion (1); builder.setNonce (nonce); @@ -365,28 +349,12 @@ public boolean isProduction () throws BCSAPIException return production = getBlockHeader ("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f") != null; } - private TemporaryQueue createTemporaryQueue () throws JMSException - { - synchronized ( session ) - { - return session.createTemporaryQueue (); - } - } - - private MessageConsumer createConsumer (Destination destination) throws JMSException - { - synchronized ( session ) - { - return session.createConsumer (destination); - } - } - @Override public void scanTransactions (Collection match, UpdateMode mode, long after, final TransactionListener listener) throws BCSAPIException { try { - BytesMessage m = createBytesMessage (); + BytesMessage m = session.createBytesMessage (); BCSAPIMessage.ExactMatchRequest.Builder builder = BCSAPIMessage.ExactMatchRequest.newBuilder (); builder.setBcsapiversion (1); @@ -400,8 +368,8 @@ public void scanTransactions (Collection match, UpdateMode mode, long af builder.setAfter (after); } m.writeBytes (builder.build ().toByteArray ()); - final TemporaryQueue answerQueue = createTemporaryQueue (); - final MessageConsumer consumer = createConsumer (answerQueue); + final TemporaryQueue answerQueue = session.createTemporaryQueue (); + final MessageConsumer consumer = session.createConsumer (answerQueue); m.setJMSReplyTo (answerQueue); final Semaphore ready = new Semaphore (0); consumer.setMessageListener (new MessageListener () @@ -460,7 +428,7 @@ public void scanTransactions (BloomFilter filter, final TransactionListener list { try { - BytesMessage m = createBytesMessage (); + BytesMessage m = session.createBytesMessage (); BCSAPIMessage.FilterRequest.Builder builder = BCSAPIMessage.FilterRequest.newBuilder (); builder.setBcsapiversion (1); @@ -471,8 +439,8 @@ public void scanTransactions (BloomFilter filter, final TransactionListener list m.writeBytes (builder.build ().toByteArray ()); - final TemporaryQueue answerQueue = createTemporaryQueue (); - final MessageConsumer consumer = createConsumer (answerQueue); + final TemporaryQueue answerQueue = session.createTemporaryQueue (); + final MessageConsumer consumer = session.createConsumer (answerQueue); m.setJMSReplyTo (answerQueue); final Semaphore ready = new Semaphore (0); consumer.setMessageListener (new MessageListener () @@ -535,7 +503,7 @@ public void scanTransactions (ExtendedKey master, int lookAhead, long after, fin } try { - BytesMessage m = createBytesMessage (); + BytesMessage m = session.createBytesMessage (); BCSAPIMessage.AccountRequest.Builder builder = BCSAPIMessage.AccountRequest.newBuilder (); builder.setBcsapiversion (1); @@ -544,8 +512,8 @@ public void scanTransactions (ExtendedKey master, int lookAhead, long after, fin builder.setAfter (after); m.writeBytes (builder.build ().toByteArray ()); - final TemporaryQueue answerQueue = createTemporaryQueue (); - final MessageConsumer consumer = createConsumer (answerQueue); + final TemporaryQueue answerQueue = session.createTemporaryQueue (); + final MessageConsumer consumer = session.createConsumer (answerQueue); m.setJMSReplyTo (answerQueue); final Semaphore ready = new Semaphore (0); consumer.setMessageListener (new MessageListener () @@ -605,7 +573,7 @@ public void registerFilteredListener (final BloomFilter filter, final Transactio { try { - BytesMessage m = createBytesMessage (); + BytesMessage m = session.createBytesMessage (); BCSAPIMessage.FilterRequest.Builder builder = BCSAPIMessage.FilterRequest.newBuilder (); builder.setBcsapiversion (1); builder.setFilter (ByteString.copyFrom (filter.getFilter ())); @@ -619,8 +587,8 @@ public void registerFilteredListener (final BloomFilter filter, final Transactio MessageDispatcher dispatcher = messageDispatcher.get (filter.toString ()); if ( dispatcher == null ) { - TemporaryQueue answerQueue = createTemporaryQueue (); - MessageConsumer consumer = createConsumer (answerQueue); + TemporaryQueue answerQueue = session.createTemporaryQueue (); + MessageConsumer consumer = session.createConsumer (answerQueue); dispatcher = new MessageDispatcher (consumer); dispatcher.setTemporaryQueue (answerQueue); messageDispatcher.put (filter.toString (), dispatcher); @@ -776,7 +744,7 @@ private byte[] synchronousRequest (MessageProducer producer, Message m) throws B { try { - answerQueue = createTemporaryQueue (); + answerQueue = session.createTemporaryQueue (); } catch ( JMSException e ) { @@ -806,7 +774,7 @@ private byte[] synchronousRequest (MessageProducer producer, Message m, Queue an try { m.setJMSReplyTo (answerQueue); - consumer = createConsumer (answerQueue); + consumer = session.createConsumer (answerQueue); consumer.setMessageListener (new MessageListener () { @Override @@ -886,7 +854,7 @@ public Transaction getTransaction (String hash) throws BCSAPIException BytesMessage m; try { - m = createBytesMessage (); + m = session.createBytesMessage (); BCSAPIMessage.Hash.Builder builder = BCSAPIMessage.Hash.newBuilder (); builder.setBcsapiversion (1); builder.addHash (ByteString.copyFrom (new Hash (hash).toByteArray ())); @@ -917,7 +885,7 @@ public Block getBlock (String hash) throws BCSAPIException try { log.trace ("get block " + hash); - BytesMessage m = createBytesMessage (); + BytesMessage m = session.createBytesMessage (); BCSAPIMessage.Hash.Builder builder = BCSAPIMessage.Hash.newBuilder (); builder.setBcsapiversion (1); builder.addHash (ByteString.copyFrom (new Hash (hash).toByteArray ())); @@ -947,7 +915,7 @@ public Block getBlockHeader (String hash) throws BCSAPIException try { log.trace ("get block header" + hash); - BytesMessage m = createBytesMessage (); + BytesMessage m = session.createBytesMessage (); BCSAPIMessage.Hash.Builder builder = BCSAPIMessage.Hash.newBuilder (); builder.setBcsapiversion (1); builder.addHash (ByteString.copyFrom (new Hash (hash).toByteArray ())); @@ -978,7 +946,7 @@ public void sendTransaction (Transaction transaction) throws BCSAPIException { transaction.computeHash (); log.trace ("send transaction " + transaction.getHash ()); - BytesMessage m = createBytesMessage (); + BytesMessage m = session.createBytesMessage (); m.writeBytes (transaction.toProtobuf ().toByteArray ()); byte[] reply = synchronousRequest (transactionProducer, m); if ( reply != null ) @@ -1007,7 +975,7 @@ public void sendBlock (Block block) throws BCSAPIException { block.computeHash (); log.trace ("send block " + block.getHash ()); - BytesMessage m = createBytesMessage (); + BytesMessage m = session.createBytesMessage (); m.writeBytes (block.toProtobuf ().toByteArray ()); byte[] reply = synchronousRequest (blockProducer, m); if ( reply != null ) @@ -1034,7 +1002,7 @@ public void issueColor (Color color) throws BCSAPIException try { log.trace ("issue color " + color.getTerms ()); - BytesMessage m = createBytesMessage (); + BytesMessage m = session.createBytesMessage (); m.writeBytes (color.toProtobuf ().toByteArray ()); byte[] reply = synchronousRequest (colorProducer, m); if ( reply != null ) @@ -1062,7 +1030,7 @@ public Color getColor (String digest) throws BCSAPIException BytesMessage m; try { - m = createBytesMessage (); + m = session.createBytesMessage (); BCSAPIMessage.Hash.Builder builder = BCSAPIMessage.Hash.newBuilder (); builder.setBcsapiversion (1); builder.addHash (ByteString.copyFrom (new Hash (digest).toByteArray ())); From 474117466b7392cd2b6a15cad26abe3f3e6e8a12 Mon Sep 17 00:00:00 2001 From: bitsofproof Date: Tue, 25 Jun 2013 22:58:32 +0200 Subject: [PATCH 091/299] 1.1.3 --- api/pom.xml | 2 +- pom.xml | 2 +- server/bop-service.sh | 4 ++-- server/pom.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index eaf8694d..4d5d9345 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.2 + 1.1.3 ../ diff --git a/pom.xml b/pom.xml index 2c1de0ea..e684279f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.1.2 + 1.1.3 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/bop-service.sh b/server/bop-service.sh index 0cd8b9d0..381f6c2d 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.2.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.3.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.2.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.3.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" diff --git a/server/pom.xml b/server/pom.xml index c909467c..d6044758 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.2 + 1.1.3 ../ From e1d262c60ba5bc038142ffe539f65f9c4a753cfb Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 26 Jun 2013 17:10:19 +0200 Subject: [PATCH 092/299] updated with products --- README | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/README b/README index 7693941e..0465cb98 100644 --- a/README +++ b/README @@ -1,37 +1,52 @@ INTRODUCTION -Welcome to the Bits of Proof implementation of the Bitcoin protocol. +Welcome to the BOP Community Bitcoin Server. -This is a modern, modular, enterprise implementation of the Bitcoin protocol targeted to developer of Bitcoin applications and system integrators. +This software enables your application or website to send or receive Bitcoin payments or to data mine the network's transaction history. -The server process handles peer-to-peer communication, protocol validation and block chain store. The data store can be configured to use a relational database or LevelDB. +You may run it stand-alone utilizing its own validation engine or as a slave process behind the Satosi implementation. In either case it builds a fully indexed block chain of transactions stored in LevelDB or in a relational database. -The validation engine passes most important unit tests of the Satoshi implementation and the block chain integration test cases of BitcoinJ that are also used in pull testing of the Satoshi implementation. +The server process handles peer-to-peer communication with the network and serves clients connected to it through a message bus. Wallet(s) are implemented by the client library and transactions are also signed at the client side. It is safe to operate a Server in a remote, untrusted environment as it does not store or receive private keys. -Clients or extensions communicate with the server process through an asynchrounous message bus. The bus is provided by a separate broker process. The messages are in protobuf format hence interoperable with modules in other languages e.g. python. +Bits of Proof offers professionally hosted server instances for commercial use, that share and build on this code base: -STATUS +BOP Enterprise Bitcoin Server - for very high volume multiple and hierarchical wallet support. +BOP Mobile Bitcoin Server - to back mobile Wallets or any Mobile application sending or receiving Bitcoin. +BOP Merchant Bitcoin Server - issue payment requests, get notification on payments. Accept Bitcoin on your own. +BOP ERP Bitcoin Server - initiate and monitor Bitcoin transactions with your ERP system. +BOP Audit Bitcoin Server - data mine the Bitcoin transaction history and get real-time feed of network events. -The implementation passed lots of testing and runs since January 2013 in parallel with the network. It is community reviewed, see: https://bitcointalk.org/index.php?topic=122013.0 +Please review our product sheets at http://bitsofproof.com and contact sales@bitsofproof.com for further info. -BUILD THE SERVER -See build instructions at: http://bitsofproof.com:8082/display/BPD/Build+and+run+the+Server +BUILD THE COMMUNITY SERVER +Make sure you have Maven3, JDK 1.6 or 7 and protobuf compiler 2.4.1 installed. -CLIENTS -Applications can use the Bitcoin network by embedding a very low footprint client library. The Bitcoin private keys, that are able to command Bitcoins, are generated and stored at the client side and never transmitted to the server. See the primary client API documentation here: http://bitsofproof.com:8082/display/BPD/Build+an+Application+using+BOP+Servers + git clone https://github.com/bitsofproof/supernode -Further layers of API are available to support Mobile and Web applications. See more details here: http://bitsofproof.com:8082/display/BPD/Bits+of+Proof+Documentation+Home + cd supernode + mvn package -SUPPORT AND HOSTING +RUN + java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.0.jar testnet3 memdb -Bits of Proof offers commercial support and hosts BOP Enterprise Bitcoin Server instances that run this open source code. BOP servers offer the fastest time to market for your Bitcoin application development. Visit http://bitsofproof.com for more details. +The final two parameters of the above example command line identify configuration contexts stored under server/src/main/resources/context. You have to choose one of the networks by specifying either testnet3 or production or slave, and a database layer, that could be (examples): + + memdb - in memory database for tests + leveldb - LevelDB + derby - For embedded relational database Derby + progresql - To connect to a ProgreSQL server -LICENCE +Review the context file of the SQL databases before attempting to use them, since connection parameters will likely not apply to your installation. In addition you might add the BCSAPI context to let the server listen to a message broker. The complete command line for a production environment is likely: + +java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.0.jar production leveldb BCSAPI +To use the API of your local server you need to run a message broker process providing the infrastructure. Since the message bus offers authentication and a wide selection of transports, your installation will likely be unique and need to be reflected in server/src/main/resources/context/BCSAPI-profile.xml. You find example configurations for the message broker Apollo and Active MQ there. + +LICENCE Apache License, Version 2.0. See LICENSE file. DONATIONS -In case you do not require professional services of Bits of Proof, but would like to honor its contribution to the community, please donate to: +In case you do not require professional services of Bits of Proof, but would like to honor its contribution to the Bitcoin community, please donate to: 1EuamejAs2Lcz1ZPNrEhLsFTLnEY29BYKU From 612f3e927efcd54331eb6ed65f12635c8384e66f Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 26 Jun 2013 17:14:31 +0200 Subject: [PATCH 093/299] try .md format --- README => README.md | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) rename README => README.md (81%) diff --git a/README b/README.md similarity index 81% rename from README rename to README.md index 0465cb98..537fa9af 100644 --- a/README +++ b/README.md @@ -1,6 +1,5 @@ -INTRODUCTION - -Welcome to the BOP Community Bitcoin Server. +BOP Community Bitcoin Server +============================ This software enables your application or website to send or receive Bitcoin payments or to data mine the network's transaction history. @@ -10,15 +9,16 @@ The server process handles peer-to-peer communication with the network and serve Bits of Proof offers professionally hosted server instances for commercial use, that share and build on this code base: -BOP Enterprise Bitcoin Server - for very high volume multiple and hierarchical wallet support. -BOP Mobile Bitcoin Server - to back mobile Wallets or any Mobile application sending or receiving Bitcoin. -BOP Merchant Bitcoin Server - issue payment requests, get notification on payments. Accept Bitcoin on your own. -BOP ERP Bitcoin Server - initiate and monitor Bitcoin transactions with your ERP system. -BOP Audit Bitcoin Server - data mine the Bitcoin transaction history and get real-time feed of network events. +* BOP Enterprise Bitcoin Server - for very high volume multiple and hierarchical wallet support. +* BOP Mobile Bitcoin Server - to back mobile Wallets or any Mobile application sending or receiving Bitcoin. +* BOP Merchant Bitcoin Server - issue payment requests, get notification on payments. Accept Bitcoin on your own. +* BOP ERP Bitcoin Server - initiate and monitor Bitcoin transactions with your ERP system. +* BOP Audit Bitcoin Server - data mine the Bitcoin transaction history and get real-time feed of network events. Please review our product sheets at http://bitsofproof.com and contact sales@bitsofproof.com for further info. -BUILD THE COMMUNITY SERVER +Build +----- Make sure you have Maven3, JDK 1.6 or 7 and protobuf compiler 2.4.1 installed. git clone https://github.com/bitsofproof/supernode @@ -26,7 +26,8 @@ Make sure you have Maven3, JDK 1.6 or 7 and protobuf compiler 2.4.1 installed. cd supernode mvn package -RUN +Run +--- java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.0.jar testnet3 memdb The final two parameters of the above example command line identify configuration contexts stored under server/src/main/resources/context. You have to choose one of the networks by specifying either testnet3 or production or slave, and a database layer, that could be (examples): @@ -42,10 +43,12 @@ java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.0.jar product To use the API of your local server you need to run a message broker process providing the infrastructure. Since the message bus offers authentication and a wide selection of transports, your installation will likely be unique and need to be reflected in server/src/main/resources/context/BCSAPI-profile.xml. You find example configurations for the message broker Apollo and Active MQ there. -LICENCE +License +------- Apache License, Version 2.0. See LICENSE file. -DONATIONS +Donations +--------- In case you do not require professional services of Bits of Proof, but would like to honor its contribution to the Bitcoin community, please donate to: 1EuamejAs2Lcz1ZPNrEhLsFTLnEY29BYKU From 755c2c7d1b10fd9453de6578360fcbbf0e5664bc Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 26 Jun 2013 17:15:47 +0200 Subject: [PATCH 094/299] formatting --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 537fa9af..918d9167 100644 --- a/README.md +++ b/README.md @@ -28,18 +28,19 @@ Make sure you have Maven3, JDK 1.6 or 7 and protobuf compiler 2.4.1 installed. Run --- - java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.0.jar testnet3 memdb + +`java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.0.jar testnet3 memdb The final two parameters of the above example command line identify configuration contexts stored under server/src/main/resources/context. You have to choose one of the networks by specifying either testnet3 or production or slave, and a database layer, that could be (examples): - memdb - in memory database for tests - leveldb - LevelDB - derby - For embedded relational database Derby - progresql - To connect to a ProgreSQL server + * memdb - in memory database for tests + * leveldb - LevelDB + * derby - For embedded relational database Derby + * progresql - To connect to a ProgreSQL server Review the context file of the SQL databases before attempting to use them, since connection parameters will likely not apply to your installation. In addition you might add the BCSAPI context to let the server listen to a message broker. The complete command line for a production environment is likely: -java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.0.jar production leveldb BCSAPI +`java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.0.jar production leveldb BCSAPI To use the API of your local server you need to run a message broker process providing the infrastructure. Since the message bus offers authentication and a wide selection of transports, your installation will likely be unique and need to be reflected in server/src/main/resources/context/BCSAPI-profile.xml. You find example configurations for the message broker Apollo and Active MQ there. From 1a46d1487015319c40a9cbbedbfd752002e3a0ca Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 26 Jun 2013 17:21:44 +0200 Subject: [PATCH 095/299] typos --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 918d9167..14f33836 100644 --- a/README.md +++ b/README.md @@ -3,15 +3,15 @@ BOP Community Bitcoin Server This software enables your application or website to send or receive Bitcoin payments or to data mine the network's transaction history. -You may run it stand-alone utilizing its own validation engine or as a slave process behind the Satosi implementation. In either case it builds a fully indexed block chain of transactions stored in LevelDB or in a relational database. +You may run it stand-alone utilizing its own validation engine or as a slave process behind the Satosi implementation so definitely accepts exactly what Satoshi does. In either case it builds a fully indexed block chain of transactions stored in LevelDB or in a relational database. The server process handles peer-to-peer communication with the network and serves clients connected to it through a message bus. Wallet(s) are implemented by the client library and transactions are also signed at the client side. It is safe to operate a Server in a remote, untrusted environment as it does not store or receive private keys. Bits of Proof offers professionally hosted server instances for commercial use, that share and build on this code base: * BOP Enterprise Bitcoin Server - for very high volume multiple and hierarchical wallet support. -* BOP Mobile Bitcoin Server - to back mobile Wallets or any Mobile application sending or receiving Bitcoin. -* BOP Merchant Bitcoin Server - issue payment requests, get notification on payments. Accept Bitcoin on your own. +* BOP Mobile Bitcoin Server - to back mobile wallets or any mobile application sending or receiving Bitcoin. +* BOP Merchant Bitcoin Server - pay or issue payment requests, get notification on payments. Deal with your own Bitcoin, save commissions. * BOP ERP Bitcoin Server - initiate and monitor Bitcoin transactions with your ERP system. * BOP Audit Bitcoin Server - data mine the Bitcoin transaction history and get real-time feed of network events. @@ -19,17 +19,18 @@ Please review our product sheets at http://bitsofproof.com and contact sales@bit Build ----- -Make sure you have Maven3, JDK 1.6 or 7 and protobuf compiler 2.4.1 installed. +Make sure you have Maven3, JDK 1.6 or 7 and Google protobuf compiler 2.4.1 installed. git clone https://github.com/bitsofproof/supernode cd supernode + mvn package Run --- -`java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.0.jar testnet3 memdb +java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.0.jar testnet3 memdb The final two parameters of the above example command line identify configuration contexts stored under server/src/main/resources/context. You have to choose one of the networks by specifying either testnet3 or production or slave, and a database layer, that could be (examples): @@ -40,7 +41,7 @@ The final two parameters of the above example command line identify configuratio Review the context file of the SQL databases before attempting to use them, since connection parameters will likely not apply to your installation. In addition you might add the BCSAPI context to let the server listen to a message broker. The complete command line for a production environment is likely: -`java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.0.jar production leveldb BCSAPI +java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.0.jar production leveldb BCSAPI To use the API of your local server you need to run a message broker process providing the infrastructure. Since the message bus offers authentication and a wide selection of transports, your installation will likely be unique and need to be reflected in server/src/main/resources/context/BCSAPI-profile.xml. You find example configurations for the message broker Apollo and Active MQ there. From 2903af1d4cd12aeee41cc7c5f34503613bd56806 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 26 Jun 2013 17:22:29 +0200 Subject: [PATCH 096/299] typos --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 14f33836..e6a4df53 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Make sure you have Maven3, JDK 1.6 or 7 and Google protobuf compiler 2.4.1 insta Run --- -java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.0.jar testnet3 memdb +java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.1.3.jar testnet3 memdb The final two parameters of the above example command line identify configuration contexts stored under server/src/main/resources/context. You have to choose one of the networks by specifying either testnet3 or production or slave, and a database layer, that could be (examples): @@ -41,7 +41,7 @@ The final two parameters of the above example command line identify configuratio Review the context file of the SQL databases before attempting to use them, since connection parameters will likely not apply to your installation. In addition you might add the BCSAPI context to let the server listen to a message broker. The complete command line for a production environment is likely: -java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.0.jar production leveldb BCSAPI +java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.1.3.jar production leveldb BCSAPI To use the API of your local server you need to run a message broker process providing the infrastructure. Since the message bus offers authentication and a wide selection of transports, your installation will likely be unique and need to be reflected in server/src/main/resources/context/BCSAPI-profile.xml. You find example configurations for the message broker Apollo and Active MQ there. From 6f3164e16b8e27c8cb678a2d4eec917f5077fbb5 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 26 Jun 2013 17:27:49 +0200 Subject: [PATCH 097/299] furter typos --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e6a4df53..45940048 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ BOP Community Bitcoin Server ============================ -This software enables your application or website to send or receive Bitcoin payments or to data mine the network's transaction history. +This software enables your application to send/receive Bitcoin payments or to data mine the network's transaction history, with ease. -You may run it stand-alone utilizing its own validation engine or as a slave process behind the Satosi implementation so definitely accepts exactly what Satoshi does. In either case it builds a fully indexed block chain of transactions stored in LevelDB or in a relational database. +You may run it stand-alone utilizing its own validation engine or as a slave behind the Satoshi client so it accepts exactly what the 'reference client' does. In either case it builds a fully indexed block chain of transactions stored in LevelDB or in a relational database. The server process handles peer-to-peer communication with the network and serves clients connected to it through a message bus. Wallet(s) are implemented by the client library and transactions are also signed at the client side. It is safe to operate a Server in a remote, untrusted environment as it does not store or receive private keys. From 77f370c64f722ad26d62484cde9f22eb71dc1d63 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 26 Jun 2013 17:31:08 +0200 Subject: [PATCH 098/299] tuning --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 45940048..821c5de8 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ This software enables your application to send/receive Bitcoin payments or to da You may run it stand-alone utilizing its own validation engine or as a slave behind the Satoshi client so it accepts exactly what the 'reference client' does. In either case it builds a fully indexed block chain of transactions stored in LevelDB or in a relational database. -The server process handles peer-to-peer communication with the network and serves clients connected to it through a message bus. Wallet(s) are implemented by the client library and transactions are also signed at the client side. It is safe to operate a Server in a remote, untrusted environment as it does not store or receive private keys. +The server process handles peer-to-peer communication with the network and serves clients connected to it through a message bus. Wallet(s) are implemented by the client library and transactions are also signed at the client side, therefore it is safe to operate the Server in a remote environment as it does not store or receive private keys. Bits of Proof offers professionally hosted server instances for commercial use, that share and build on this code base: From 75c8db4d6186c6e8303408b655e8c3e27a59c006 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 26 Jun 2013 17:32:17 +0200 Subject: [PATCH 099/299] tuning --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 821c5de8..fe39f7db 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ The final two parameters of the above example command line identify configuratio Review the context file of the SQL databases before attempting to use them, since connection parameters will likely not apply to your installation. In addition you might add the BCSAPI context to let the server listen to a message broker. The complete command line for a production environment is likely: -java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.1.3.jar production leveldb BCSAPI +java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.1.3.jar production leveldb BCSAPI apollo To use the API of your local server you need to run a message broker process providing the infrastructure. Since the message bus offers authentication and a wide selection of transports, your installation will likely be unique and need to be reflected in server/src/main/resources/context/BCSAPI-profile.xml. You find example configurations for the message broker Apollo and Active MQ there. From f63335351fd37a229a02e4741a2c990daf0aca3b Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 26 Jun 2013 17:34:22 +0200 Subject: [PATCH 100/299] tuning --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index fe39f7db..192571d9 100644 --- a/README.md +++ b/README.md @@ -39,12 +39,10 @@ The final two parameters of the above example command line identify configuratio * derby - For embedded relational database Derby * progresql - To connect to a ProgreSQL server -Review the context file of the SQL databases before attempting to use them, since connection parameters will likely not apply to your installation. In addition you might add the BCSAPI context to let the server listen to a message broker. The complete command line for a production environment is likely: +Review the context file of the SQL databases before attempting to use them, since connection parameters will likely not apply to your installation. To use the API of your local server you need to run a message broker process providing the infrastructure. Since the message bus offers authentication and a wide selection of transports, your installation will likely be unique and need to be reflected in server/src/main/resources/context/BCSAPI-profile.xml. You find example configurations for the message broker Apollo and Active MQ there. The complete command line for a production environment might be: java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.1.3.jar production leveldb BCSAPI apollo -To use the API of your local server you need to run a message broker process providing the infrastructure. Since the message bus offers authentication and a wide selection of transports, your installation will likely be unique and need to be reflected in server/src/main/resources/context/BCSAPI-profile.xml. You find example configurations for the message broker Apollo and Active MQ there. - License ------- Apache License, Version 2.0. See LICENSE file. From ad6595a342ab2bf5935f6045b42651ac5c734801 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 26 Jun 2013 17:35:05 +0200 Subject: [PATCH 101/299] why is a line missing? --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 192571d9..e5c1f24d 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Review the context file of the SQL databases before attempting to use them, sinc java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.1.3.jar production leveldb BCSAPI apollo + License ------- Apache License, Version 2.0. See LICENSE file. From 489e04b01077b7302770e83651dae32cd612f617 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 26 Jun 2013 17:39:47 +0200 Subject: [PATCH 102/299] tuning --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e5c1f24d..18557ee8 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,11 @@ The server process handles peer-to-peer communication with the network and serve Bits of Proof offers professionally hosted server instances for commercial use, that share and build on this code base: -* BOP Enterprise Bitcoin Server - for very high volume multiple and hierarchical wallet support. -* BOP Mobile Bitcoin Server - to back mobile wallets or any mobile application sending or receiving Bitcoin. +* BOP Enterprise Bitcoin Server - for very high volume multiple and hierarchical wallet support. Don't try to work around the 'reference client's geeky RPC calls. Use our enterprise class solution. +* BOP Mobile Bitcoin Server - to back mobile wallets or any mobile application sending or receiving Bitcoin. The highest performance mobile Bitcoin service with the smallest memory and traffic footprint on the device. * BOP Merchant Bitcoin Server - pay or issue payment requests, get notification on payments. Deal with your own Bitcoin, save commissions. -* BOP ERP Bitcoin Server - initiate and monitor Bitcoin transactions with your ERP system. -* BOP Audit Bitcoin Server - data mine the Bitcoin transaction history and get real-time feed of network events. +* BOP ERP Bitcoin Server - initiate and monitor Bitcoin transactions with your ERP system. The next generation of service - stay tuned. +* BOP Audit Bitcoin Server - data mine the Bitcoin transaction history and get real-time feed of network events. Best for forensic research, no hassle to get high quality data. Please review our product sheets at http://bitsofproof.com and contact sales@bitsofproof.com for further info. From 5b34edaca75445bb53c1163b64201d70e8d9ddf6 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 26 Jun 2013 17:45:14 +0200 Subject: [PATCH 103/299] tuning --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 18557ee8..9e367fcb 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ The server process handles peer-to-peer communication with the network and serve Bits of Proof offers professionally hosted server instances for commercial use, that share and build on this code base: -* BOP Enterprise Bitcoin Server - for very high volume multiple and hierarchical wallet support. Don't try to work around the 'reference client's geeky RPC calls. Use our enterprise class solution. +* BOP Enterprise Bitcoin Server - for very high volume multiple and hierarchical wallet support. Don't despair with the 'reference client's geeky RPC calls design and poor performance. Use our intuitive, business friendly, high performance API and spend rather to develop your own business case. * BOP Mobile Bitcoin Server - to back mobile wallets or any mobile application sending or receiving Bitcoin. The highest performance mobile Bitcoin service with the smallest memory and traffic footprint on the device. * BOP Merchant Bitcoin Server - pay or issue payment requests, get notification on payments. Deal with your own Bitcoin, save commissions. * BOP ERP Bitcoin Server - initiate and monitor Bitcoin transactions with your ERP system. The next generation of service - stay tuned. From 65bc38e5cb99c14be162201d83b802f3e1effbd3 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 26 Jun 2013 17:47:14 +0200 Subject: [PATCH 104/299] tuning --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9e367fcb..38c8f9e8 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,10 @@ The server process handles peer-to-peer communication with the network and serve Bits of Proof offers professionally hosted server instances for commercial use, that share and build on this code base: * BOP Enterprise Bitcoin Server - for very high volume multiple and hierarchical wallet support. Don't despair with the 'reference client's geeky RPC calls design and poor performance. Use our intuitive, business friendly, high performance API and spend rather to develop your own business case. -* BOP Mobile Bitcoin Server - to back mobile wallets or any mobile application sending or receiving Bitcoin. The highest performance mobile Bitcoin service with the smallest memory and traffic footprint on the device. +* BOP Mobile Bitcoin Server - back mobile wallets or any mobile application sending or receiving Bitcoin. We give you the highest performance mobile Bitcoin service with the smallest memory and traffic footprint on the device. * BOP Merchant Bitcoin Server - pay or issue payment requests, get notification on payments. Deal with your own Bitcoin, save commissions. -* BOP ERP Bitcoin Server - initiate and monitor Bitcoin transactions with your ERP system. The next generation of service - stay tuned. * BOP Audit Bitcoin Server - data mine the Bitcoin transaction history and get real-time feed of network events. Best for forensic research, no hassle to get high quality data. +* BOP ERP Bitcoin Server - initiate and monitor Bitcoin transactions with your ERP system. The next generation of service - stay tuned. Please review our product sheets at http://bitsofproof.com and contact sales@bitsofproof.com for further info. From 61185d9747c22cf42c0c383691052857ea361d24 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 26 Jun 2013 17:47:52 +0200 Subject: [PATCH 105/299] tuning --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 38c8f9e8..b6896285 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,13 @@ The server process handles peer-to-peer communication with the network and serve Bits of Proof offers professionally hosted server instances for commercial use, that share and build on this code base: * BOP Enterprise Bitcoin Server - for very high volume multiple and hierarchical wallet support. Don't despair with the 'reference client's geeky RPC calls design and poor performance. Use our intuitive, business friendly, high performance API and spend rather to develop your own business case. + * BOP Mobile Bitcoin Server - back mobile wallets or any mobile application sending or receiving Bitcoin. We give you the highest performance mobile Bitcoin service with the smallest memory and traffic footprint on the device. + * BOP Merchant Bitcoin Server - pay or issue payment requests, get notification on payments. Deal with your own Bitcoin, save commissions. + * BOP Audit Bitcoin Server - data mine the Bitcoin transaction history and get real-time feed of network events. Best for forensic research, no hassle to get high quality data. + * BOP ERP Bitcoin Server - initiate and monitor Bitcoin transactions with your ERP system. The next generation of service - stay tuned. Please review our product sheets at http://bitsofproof.com and contact sales@bitsofproof.com for further info. From 455d20aa590c35f2e9d35e0b389fbeded3240229 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 26 Jun 2013 18:22:14 +0200 Subject: [PATCH 106/299] tuning --- NOTICE | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NOTICE b/NOTICE index b7d9c8cd..a5a3ce72 100644 --- a/NOTICE +++ b/NOTICE @@ -1,2 +1,2 @@ -bitsofproof Bitcoin protocol implementation -Copyright 2013 bits of proof zrt. +BOP Community Bitcoin Server +Copyright 2013 bits of proof zrt. http://bitsofproof.com From be038a9b2fef692a18c9dff9f87c235434a7a694 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 26 Jun 2013 19:41:48 +0200 Subject: [PATCH 107/299] refer to bitcoinspinner --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b6896285..2fb6b36a 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Bits of Proof offers professionally hosted server instances for commercial use, * BOP Enterprise Bitcoin Server - for very high volume multiple and hierarchical wallet support. Don't despair with the 'reference client's geeky RPC calls design and poor performance. Use our intuitive, business friendly, high performance API and spend rather to develop your own business case. -* BOP Mobile Bitcoin Server - back mobile wallets or any mobile application sending or receiving Bitcoin. We give you the highest performance mobile Bitcoin service with the smallest memory and traffic footprint on the device. +* BOP Mobile Bitcoin Server - back mobile wallets or any mobile application sending or receiving Bitcoin. We give you the highest performance mobile Bitcoin service with the smallest memory and traffic footprint on the device. See the example port of the popular Android Bitcoin wallet BitcoinSpinner at https://github.com/bitsofproof/bop-bitcoinspinner * BOP Merchant Bitcoin Server - pay or issue payment requests, get notification on payments. Deal with your own Bitcoin, save commissions. @@ -58,4 +58,4 @@ In case you do not require professional services of Bits of Proof, but would lik 1EuamejAs2Lcz1ZPNrEhLsFTLnEY29BYKU -Donations will finance social events of Bits of Proof developer. \ No newline at end of file +Donations will finance social events of Bits of Proof developer. From 165c162c26c557ab829c2008ebfcea8e2ebfb976 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 26 Jun 2013 20:01:39 +0200 Subject: [PATCH 108/299] refer other projects --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2fb6b36a..23245932 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,10 @@ Bits of Proof offers professionally hosted server instances for commercial use, * BOP ERP Bitcoin Server - initiate and monitor Bitcoin transactions with your ERP system. The next generation of service - stay tuned. -Please review our product sheets at http://bitsofproof.com and contact sales@bitsofproof.com for further info. +_Use our ready to go hosted BOP Server instance! Get an evaluation access, attractive pricing based on resources used or transactions processed from sales@bitsofproof.com_ -Build ------ +Build the Community Server +-------------------------- Make sure you have Maven3, JDK 1.6 or 7 and Google protobuf compiler 2.4.1 installed. git clone https://github.com/bitsofproof/supernode From 9ac0a4f208f3d0ac71a0090a17b5e4f8399ecab3 Mon Sep 17 00:00:00 2001 From: senecaso Date: Sat, 29 Jun 2013 15:07:26 +0900 Subject: [PATCH 109/299] -removing generated source code from the repository -fixing up maven build to properly build protobuf files and put them in target/generated-sources -updating version of protoc used, since the committed files were depending on classes which only exist in protobuf 2.5+, not 2.4.1 -ignoring IntelliJ project files --- .gitignore | 2 + api/pom.xml | 38 +- .../supernode/api/BCSAPIMessage.java | 10132 ---------------- .../{protobuf => proto}/BCSAPIMessage.proto | 0 pom.xml | 13 +- server/pom.xml | 38 +- .../supernode/model/LevelDBStore.java | 6838 ----------- .../{protobuf => proto}/LevelDBStore.proto | 0 8 files changed, 45 insertions(+), 17016 deletions(-) delete mode 100644 api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java rename api/src/main/{protobuf => proto}/BCSAPIMessage.proto (100%) delete mode 100644 server/src/main/java/com/bitsofproof/supernode/model/LevelDBStore.java rename server/src/main/{protobuf => proto}/LevelDBStore.proto (100%) diff --git a/.gitignore b/.gitignore index 5330893e..728a28b0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ target .classpath .project .settings +*.iml +*.idea .factorypath data /logs diff --git a/api/pom.xml b/api/pom.xml index 4d5d9345..4b319cba 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,33 +9,27 @@ com.bitsofproof supernode - 1.1.3 + 1.1.4-SNAPSHOT ../ - - maven-antrun-plugin - - - compile-protoc - generate-sources - - - - - - - - - - - run - - - - + + com.google.protobuf.tools + maven-protoc-plugin + + /usr/local/bin/protoc + + + + + compile + testCompile + + + + diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java deleted file mode 100644 index 7716124d..00000000 --- a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPIMessage.java +++ /dev/null @@ -1,10132 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: main/protobuf/BCSAPIMessage.proto - -package com.bitsofproof.supernode.api; - -public final class BCSAPIMessage { - private BCSAPIMessage() {} - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistry registry) { - } - public interface PingOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required uint32 bcsapiversion = 1; - boolean hasBcsapiversion(); - int getBcsapiversion(); - - // required uint64 nonce = 2; - boolean hasNonce(); - long getNonce(); - } - public static final class Ping extends - com.google.protobuf.GeneratedMessage - implements PingOrBuilder { - // Use Ping.newBuilder() to construct. - private Ping(Builder builder) { - super(builder); - } - private Ping(boolean noInit) {} - - private static final Ping defaultInstance; - public static Ping getDefaultInstance() { - return defaultInstance; - } - - public Ping getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Ping_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Ping_fieldAccessorTable; - } - - private int bitField0_; - // required uint32 bcsapiversion = 1; - public static final int BCSAPIVERSION_FIELD_NUMBER = 1; - private int bcsapiversion_; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - - // required uint64 nonce = 2; - public static final int NONCE_FIELD_NUMBER = 2; - private long nonce_; - public boolean hasNonce() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public long getNonce() { - return nonce_; - } - - private void initFields() { - bcsapiversion_ = 0; - nonce_ = 0L; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasBcsapiversion()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasNonce()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, bcsapiversion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt64(2, nonce_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, bcsapiversion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(2, nonce_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.api.BCSAPIMessage.Ping parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Ping parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Ping parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Ping parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Ping parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Ping parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Ping parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Ping parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Ping parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Ping parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.api.BCSAPIMessage.Ping prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.api.BCSAPIMessage.PingOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Ping_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Ping_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.api.BCSAPIMessage.Ping.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - bcsapiversion_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - nonce_ = 0L; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Ping.getDescriptor(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Ping getDefaultInstanceForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Ping.getDefaultInstance(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Ping build() { - com.bitsofproof.supernode.api.BCSAPIMessage.Ping result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.api.BCSAPIMessage.Ping buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.api.BCSAPIMessage.Ping result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Ping buildPartial() { - com.bitsofproof.supernode.api.BCSAPIMessage.Ping result = new com.bitsofproof.supernode.api.BCSAPIMessage.Ping(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.bcsapiversion_ = bcsapiversion_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.nonce_ = nonce_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.api.BCSAPIMessage.Ping) { - return mergeFrom((com.bitsofproof.supernode.api.BCSAPIMessage.Ping)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.Ping other) { - if (other == com.bitsofproof.supernode.api.BCSAPIMessage.Ping.getDefaultInstance()) return this; - if (other.hasBcsapiversion()) { - setBcsapiversion(other.getBcsapiversion()); - } - if (other.hasNonce()) { - setNonce(other.getNonce()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasBcsapiversion()) { - - return false; - } - if (!hasNonce()) { - - return false; - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - bcsapiversion_ = input.readUInt32(); - break; - } - case 16: { - bitField0_ |= 0x00000002; - nonce_ = input.readUInt64(); - break; - } - } - } - } - - private int bitField0_; - - // required uint32 bcsapiversion = 1; - private int bcsapiversion_ ; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - public Builder setBcsapiversion(int value) { - bitField0_ |= 0x00000001; - bcsapiversion_ = value; - onChanged(); - return this; - } - public Builder clearBcsapiversion() { - bitField0_ = (bitField0_ & ~0x00000001); - bcsapiversion_ = 0; - onChanged(); - return this; - } - - // required uint64 nonce = 2; - private long nonce_ ; - public boolean hasNonce() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public long getNonce() { - return nonce_; - } - public Builder setNonce(long value) { - bitField0_ |= 0x00000002; - nonce_ = value; - onChanged(); - return this; - } - public Builder clearNonce() { - bitField0_ = (bitField0_ & ~0x00000002); - nonce_ = 0L; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.Ping) - } - - static { - defaultInstance = new Ping(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.api.Ping) - } - - public interface AlertOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required uint32 bcsapiversion = 1; - boolean hasBcsapiversion(); - int getBcsapiversion(); - - // required string alert = 2; - boolean hasAlert(); - String getAlert(); - - // required uint32 severity = 3; - boolean hasSeverity(); - int getSeverity(); - } - public static final class Alert extends - com.google.protobuf.GeneratedMessage - implements AlertOrBuilder { - // Use Alert.newBuilder() to construct. - private Alert(Builder builder) { - super(builder); - } - private Alert(boolean noInit) {} - - private static final Alert defaultInstance; - public static Alert getDefaultInstance() { - return defaultInstance; - } - - public Alert getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Alert_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Alert_fieldAccessorTable; - } - - private int bitField0_; - // required uint32 bcsapiversion = 1; - public static final int BCSAPIVERSION_FIELD_NUMBER = 1; - private int bcsapiversion_; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - - // required string alert = 2; - public static final int ALERT_FIELD_NUMBER = 2; - private java.lang.Object alert_; - public boolean hasAlert() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public String getAlert() { - java.lang.Object ref = alert_; - if (ref instanceof String) { - return (String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - String s = bs.toStringUtf8(); - if (com.google.protobuf.Internal.isValidUtf8(bs)) { - alert_ = s; - } - return s; - } - } - private com.google.protobuf.ByteString getAlertBytes() { - java.lang.Object ref = alert_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8((String) ref); - alert_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // required uint32 severity = 3; - public static final int SEVERITY_FIELD_NUMBER = 3; - private int severity_; - public boolean hasSeverity() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public int getSeverity() { - return severity_; - } - - private void initFields() { - bcsapiversion_ = 0; - alert_ = ""; - severity_ = 0; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasBcsapiversion()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasAlert()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasSeverity()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, bcsapiversion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getAlertBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeUInt32(3, severity_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, bcsapiversion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getAlertBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(3, severity_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.api.BCSAPIMessage.Alert parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Alert parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Alert parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Alert parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Alert parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Alert parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Alert parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Alert parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Alert parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Alert parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.api.BCSAPIMessage.Alert prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.api.BCSAPIMessage.AlertOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Alert_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Alert_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.api.BCSAPIMessage.Alert.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - bcsapiversion_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - alert_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - severity_ = 0; - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Alert.getDescriptor(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Alert getDefaultInstanceForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Alert.getDefaultInstance(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Alert build() { - com.bitsofproof.supernode.api.BCSAPIMessage.Alert result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.api.BCSAPIMessage.Alert buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.api.BCSAPIMessage.Alert result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Alert buildPartial() { - com.bitsofproof.supernode.api.BCSAPIMessage.Alert result = new com.bitsofproof.supernode.api.BCSAPIMessage.Alert(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.bcsapiversion_ = bcsapiversion_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.alert_ = alert_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.severity_ = severity_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.api.BCSAPIMessage.Alert) { - return mergeFrom((com.bitsofproof.supernode.api.BCSAPIMessage.Alert)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.Alert other) { - if (other == com.bitsofproof.supernode.api.BCSAPIMessage.Alert.getDefaultInstance()) return this; - if (other.hasBcsapiversion()) { - setBcsapiversion(other.getBcsapiversion()); - } - if (other.hasAlert()) { - setAlert(other.getAlert()); - } - if (other.hasSeverity()) { - setSeverity(other.getSeverity()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasBcsapiversion()) { - - return false; - } - if (!hasAlert()) { - - return false; - } - if (!hasSeverity()) { - - return false; - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - bcsapiversion_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - alert_ = input.readBytes(); - break; - } - case 24: { - bitField0_ |= 0x00000004; - severity_ = input.readUInt32(); - break; - } - } - } - } - - private int bitField0_; - - // required uint32 bcsapiversion = 1; - private int bcsapiversion_ ; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - public Builder setBcsapiversion(int value) { - bitField0_ |= 0x00000001; - bcsapiversion_ = value; - onChanged(); - return this; - } - public Builder clearBcsapiversion() { - bitField0_ = (bitField0_ & ~0x00000001); - bcsapiversion_ = 0; - onChanged(); - return this; - } - - // required string alert = 2; - private java.lang.Object alert_ = ""; - public boolean hasAlert() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public String getAlert() { - java.lang.Object ref = alert_; - if (!(ref instanceof String)) { - String s = ((com.google.protobuf.ByteString) ref).toStringUtf8(); - alert_ = s; - return s; - } else { - return (String) ref; - } - } - public Builder setAlert(String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - alert_ = value; - onChanged(); - return this; - } - public Builder clearAlert() { - bitField0_ = (bitField0_ & ~0x00000002); - alert_ = getDefaultInstance().getAlert(); - onChanged(); - return this; - } - void setAlert(com.google.protobuf.ByteString value) { - bitField0_ |= 0x00000002; - alert_ = value; - onChanged(); - } - - // required uint32 severity = 3; - private int severity_ ; - public boolean hasSeverity() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public int getSeverity() { - return severity_; - } - public Builder setSeverity(int value) { - bitField0_ |= 0x00000004; - severity_ = value; - onChanged(); - return this; - } - public Builder clearSeverity() { - bitField0_ = (bitField0_ & ~0x00000004); - severity_ = 0; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.Alert) - } - - static { - defaultInstance = new Alert(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.api.Alert) - } - - public interface TransactionInputOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required bytes source = 1; - boolean hasSource(); - com.google.protobuf.ByteString getSource(); - - // required uint32 sourceix = 2; - boolean hasSourceix(); - int getSourceix(); - - // required uint32 sequence = 3; - boolean hasSequence(); - int getSequence(); - - // required bytes script = 4; - boolean hasScript(); - com.google.protobuf.ByteString getScript(); - } - public static final class TransactionInput extends - com.google.protobuf.GeneratedMessage - implements TransactionInputOrBuilder { - // Use TransactionInput.newBuilder() to construct. - private TransactionInput(Builder builder) { - super(builder); - } - private TransactionInput(boolean noInit) {} - - private static final TransactionInput defaultInstance; - public static TransactionInput getDefaultInstance() { - return defaultInstance; - } - - public TransactionInput getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_TransactionInput_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_TransactionInput_fieldAccessorTable; - } - - private int bitField0_; - // required bytes source = 1; - public static final int SOURCE_FIELD_NUMBER = 1; - private com.google.protobuf.ByteString source_; - public boolean hasSource() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public com.google.protobuf.ByteString getSource() { - return source_; - } - - // required uint32 sourceix = 2; - public static final int SOURCEIX_FIELD_NUMBER = 2; - private int sourceix_; - public boolean hasSourceix() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public int getSourceix() { - return sourceix_; - } - - // required uint32 sequence = 3; - public static final int SEQUENCE_FIELD_NUMBER = 3; - private int sequence_; - public boolean hasSequence() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public int getSequence() { - return sequence_; - } - - // required bytes script = 4; - public static final int SCRIPT_FIELD_NUMBER = 4; - private com.google.protobuf.ByteString script_; - public boolean hasScript() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public com.google.protobuf.ByteString getScript() { - return script_; - } - - private void initFields() { - source_ = com.google.protobuf.ByteString.EMPTY; - sourceix_ = 0; - sequence_ = 0; - script_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasSource()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasSourceix()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasSequence()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasScript()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, source_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt32(2, sourceix_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeUInt32(3, sequence_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(4, script_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, source_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(2, sourceix_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(3, sequence_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(4, script_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInputOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_TransactionInput_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_TransactionInput_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - source_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - sourceix_ = 0; - bitField0_ = (bitField0_ & ~0x00000002); - sequence_ = 0; - bitField0_ = (bitField0_ & ~0x00000004); - script_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000008); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput.getDescriptor(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput getDefaultInstanceForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput.getDefaultInstance(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput build() { - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput buildPartial() { - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput result = new com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.source_ = source_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.sourceix_ = sourceix_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.sequence_ = sequence_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.script_ = script_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput) { - return mergeFrom((com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput other) { - if (other == com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput.getDefaultInstance()) return this; - if (other.hasSource()) { - setSource(other.getSource()); - } - if (other.hasSourceix()) { - setSourceix(other.getSourceix()); - } - if (other.hasSequence()) { - setSequence(other.getSequence()); - } - if (other.hasScript()) { - setScript(other.getScript()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasSource()) { - - return false; - } - if (!hasSourceix()) { - - return false; - } - if (!hasSequence()) { - - return false; - } - if (!hasScript()) { - - return false; - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - source_ = input.readBytes(); - break; - } - case 16: { - bitField0_ |= 0x00000002; - sourceix_ = input.readUInt32(); - break; - } - case 24: { - bitField0_ |= 0x00000004; - sequence_ = input.readUInt32(); - break; - } - case 34: { - bitField0_ |= 0x00000008; - script_ = input.readBytes(); - break; - } - } - } - } - - private int bitField0_; - - // required bytes source = 1; - private com.google.protobuf.ByteString source_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasSource() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public com.google.protobuf.ByteString getSource() { - return source_; - } - public Builder setSource(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - source_ = value; - onChanged(); - return this; - } - public Builder clearSource() { - bitField0_ = (bitField0_ & ~0x00000001); - source_ = getDefaultInstance().getSource(); - onChanged(); - return this; - } - - // required uint32 sourceix = 2; - private int sourceix_ ; - public boolean hasSourceix() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public int getSourceix() { - return sourceix_; - } - public Builder setSourceix(int value) { - bitField0_ |= 0x00000002; - sourceix_ = value; - onChanged(); - return this; - } - public Builder clearSourceix() { - bitField0_ = (bitField0_ & ~0x00000002); - sourceix_ = 0; - onChanged(); - return this; - } - - // required uint32 sequence = 3; - private int sequence_ ; - public boolean hasSequence() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public int getSequence() { - return sequence_; - } - public Builder setSequence(int value) { - bitField0_ |= 0x00000004; - sequence_ = value; - onChanged(); - return this; - } - public Builder clearSequence() { - bitField0_ = (bitField0_ & ~0x00000004); - sequence_ = 0; - onChanged(); - return this; - } - - // required bytes script = 4; - private com.google.protobuf.ByteString script_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasScript() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public com.google.protobuf.ByteString getScript() { - return script_; - } - public Builder setScript(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - script_ = value; - onChanged(); - return this; - } - public Builder clearScript() { - bitField0_ = (bitField0_ & ~0x00000008); - script_ = getDefaultInstance().getScript(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.TransactionInput) - } - - static { - defaultInstance = new TransactionInput(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.api.TransactionInput) - } - - public interface TransactionOutputOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required uint64 value = 1; - boolean hasValue(); - long getValue(); - - // required bytes script = 2; - boolean hasScript(); - com.google.protobuf.ByteString getScript(); - - // optional bytes color = 3; - boolean hasColor(); - com.google.protobuf.ByteString getColor(); - } - public static final class TransactionOutput extends - com.google.protobuf.GeneratedMessage - implements TransactionOutputOrBuilder { - // Use TransactionOutput.newBuilder() to construct. - private TransactionOutput(Builder builder) { - super(builder); - } - private TransactionOutput(boolean noInit) {} - - private static final TransactionOutput defaultInstance; - public static TransactionOutput getDefaultInstance() { - return defaultInstance; - } - - public TransactionOutput getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_TransactionOutput_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_TransactionOutput_fieldAccessorTable; - } - - private int bitField0_; - // required uint64 value = 1; - public static final int VALUE_FIELD_NUMBER = 1; - private long value_; - public boolean hasValue() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public long getValue() { - return value_; - } - - // required bytes script = 2; - public static final int SCRIPT_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString script_; - public boolean hasScript() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public com.google.protobuf.ByteString getScript() { - return script_; - } - - // optional bytes color = 3; - public static final int COLOR_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString color_; - public boolean hasColor() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public com.google.protobuf.ByteString getColor() { - return color_; - } - - private void initFields() { - value_ = 0L; - script_ = com.google.protobuf.ByteString.EMPTY; - color_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasValue()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasScript()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt64(1, value_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, script_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, color_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(1, value_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, script_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, color_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutputOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_TransactionOutput_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_TransactionOutput_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - value_ = 0L; - bitField0_ = (bitField0_ & ~0x00000001); - script_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - color_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput.getDescriptor(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput getDefaultInstanceForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput.getDefaultInstance(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput build() { - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput buildPartial() { - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput result = new com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.value_ = value_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.script_ = script_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.color_ = color_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput) { - return mergeFrom((com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput other) { - if (other == com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput.getDefaultInstance()) return this; - if (other.hasValue()) { - setValue(other.getValue()); - } - if (other.hasScript()) { - setScript(other.getScript()); - } - if (other.hasColor()) { - setColor(other.getColor()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasValue()) { - - return false; - } - if (!hasScript()) { - - return false; - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - value_ = input.readUInt64(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - script_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - color_ = input.readBytes(); - break; - } - } - } - } - - private int bitField0_; - - // required uint64 value = 1; - private long value_ ; - public boolean hasValue() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public long getValue() { - return value_; - } - public Builder setValue(long value) { - bitField0_ |= 0x00000001; - value_ = value; - onChanged(); - return this; - } - public Builder clearValue() { - bitField0_ = (bitField0_ & ~0x00000001); - value_ = 0L; - onChanged(); - return this; - } - - // required bytes script = 2; - private com.google.protobuf.ByteString script_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasScript() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public com.google.protobuf.ByteString getScript() { - return script_; - } - public Builder setScript(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - script_ = value; - onChanged(); - return this; - } - public Builder clearScript() { - bitField0_ = (bitField0_ & ~0x00000002); - script_ = getDefaultInstance().getScript(); - onChanged(); - return this; - } - - // optional bytes color = 3; - private com.google.protobuf.ByteString color_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasColor() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public com.google.protobuf.ByteString getColor() { - return color_; - } - public Builder setColor(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - color_ = value; - onChanged(); - return this; - } - public Builder clearColor() { - bitField0_ = (bitField0_ & ~0x00000004); - color_ = getDefaultInstance().getColor(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.TransactionOutput) - } - - static { - defaultInstance = new TransactionOutput(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.api.TransactionOutput) - } - - public interface TransactionOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required uint32 bcsapiversion = 1; - boolean hasBcsapiversion(); - int getBcsapiversion(); - - // required uint32 version = 3; - boolean hasVersion(); - int getVersion(); - - // required uint32 locktime = 4; - boolean hasLocktime(); - int getLocktime(); - - // repeated .com.bitsofproof.supernode.api.TransactionInput inputs = 5; - java.util.List - getInputsList(); - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput getInputs(int index); - int getInputsCount(); - java.util.List - getInputsOrBuilderList(); - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInputOrBuilder getInputsOrBuilder( - int index); - - // repeated .com.bitsofproof.supernode.api.TransactionOutput outputs = 6; - java.util.List - getOutputsList(); - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput getOutputs(int index); - int getOutputsCount(); - java.util.List - getOutputsOrBuilderList(); - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutputOrBuilder getOutputsOrBuilder( - int index); - - // optional bytes block = 7; - boolean hasBlock(); - com.google.protobuf.ByteString getBlock(); - - // optional bool doubleSpend = 8; - boolean hasDoubleSpend(); - boolean getDoubleSpend(); - - // optional uint32 height = 9; - boolean hasHeight(); - int getHeight(); - } - public static final class Transaction extends - com.google.protobuf.GeneratedMessage - implements TransactionOrBuilder { - // Use Transaction.newBuilder() to construct. - private Transaction(Builder builder) { - super(builder); - } - private Transaction(boolean noInit) {} - - private static final Transaction defaultInstance; - public static Transaction getDefaultInstance() { - return defaultInstance; - } - - public Transaction getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Transaction_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Transaction_fieldAccessorTable; - } - - private int bitField0_; - // required uint32 bcsapiversion = 1; - public static final int BCSAPIVERSION_FIELD_NUMBER = 1; - private int bcsapiversion_; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - - // required uint32 version = 3; - public static final int VERSION_FIELD_NUMBER = 3; - private int version_; - public boolean hasVersion() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public int getVersion() { - return version_; - } - - // required uint32 locktime = 4; - public static final int LOCKTIME_FIELD_NUMBER = 4; - private int locktime_; - public boolean hasLocktime() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public int getLocktime() { - return locktime_; - } - - // repeated .com.bitsofproof.supernode.api.TransactionInput inputs = 5; - public static final int INPUTS_FIELD_NUMBER = 5; - private java.util.List inputs_; - public java.util.List getInputsList() { - return inputs_; - } - public java.util.List - getInputsOrBuilderList() { - return inputs_; - } - public int getInputsCount() { - return inputs_.size(); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput getInputs(int index) { - return inputs_.get(index); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInputOrBuilder getInputsOrBuilder( - int index) { - return inputs_.get(index); - } - - // repeated .com.bitsofproof.supernode.api.TransactionOutput outputs = 6; - public static final int OUTPUTS_FIELD_NUMBER = 6; - private java.util.List outputs_; - public java.util.List getOutputsList() { - return outputs_; - } - public java.util.List - getOutputsOrBuilderList() { - return outputs_; - } - public int getOutputsCount() { - return outputs_.size(); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput getOutputs(int index) { - return outputs_.get(index); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutputOrBuilder getOutputsOrBuilder( - int index) { - return outputs_.get(index); - } - - // optional bytes block = 7; - public static final int BLOCK_FIELD_NUMBER = 7; - private com.google.protobuf.ByteString block_; - public boolean hasBlock() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public com.google.protobuf.ByteString getBlock() { - return block_; - } - - // optional bool doubleSpend = 8; - public static final int DOUBLESPEND_FIELD_NUMBER = 8; - private boolean doubleSpend_; - public boolean hasDoubleSpend() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - public boolean getDoubleSpend() { - return doubleSpend_; - } - - // optional uint32 height = 9; - public static final int HEIGHT_FIELD_NUMBER = 9; - private int height_; - public boolean hasHeight() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - public int getHeight() { - return height_; - } - - private void initFields() { - bcsapiversion_ = 0; - version_ = 0; - locktime_ = 0; - inputs_ = java.util.Collections.emptyList(); - outputs_ = java.util.Collections.emptyList(); - block_ = com.google.protobuf.ByteString.EMPTY; - doubleSpend_ = false; - height_ = 0; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasBcsapiversion()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasVersion()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasLocktime()) { - memoizedIsInitialized = 0; - return false; - } - for (int i = 0; i < getInputsCount(); i++) { - if (!getInputs(i).isInitialized()) { - memoizedIsInitialized = 0; - return false; - } - } - for (int i = 0; i < getOutputsCount(); i++) { - if (!getOutputs(i).isInitialized()) { - memoizedIsInitialized = 0; - return false; - } - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, bcsapiversion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt32(3, version_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeUInt32(4, locktime_); - } - for (int i = 0; i < inputs_.size(); i++) { - output.writeMessage(5, inputs_.get(i)); - } - for (int i = 0; i < outputs_.size(); i++) { - output.writeMessage(6, outputs_.get(i)); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(7, block_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeBool(8, doubleSpend_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeUInt32(9, height_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, bcsapiversion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(3, version_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(4, locktime_); - } - for (int i = 0; i < inputs_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(5, inputs_.get(i)); - } - for (int i = 0; i < outputs_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(6, outputs_.get(i)); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(7, block_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeBoolSize(8, doubleSpend_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(9, height_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.api.BCSAPIMessage.Transaction parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Transaction parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Transaction parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Transaction parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Transaction parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Transaction parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Transaction parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Transaction parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Transaction parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Transaction parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.api.BCSAPIMessage.Transaction prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Transaction_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Transaction_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getInputsFieldBuilder(); - getOutputsFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - bcsapiversion_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - version_ = 0; - bitField0_ = (bitField0_ & ~0x00000002); - locktime_ = 0; - bitField0_ = (bitField0_ & ~0x00000004); - if (inputsBuilder_ == null) { - inputs_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); - } else { - inputsBuilder_.clear(); - } - if (outputsBuilder_ == null) { - outputs_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000010); - } else { - outputsBuilder_.clear(); - } - block_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000020); - doubleSpend_ = false; - bitField0_ = (bitField0_ & ~0x00000040); - height_ = 0; - bitField0_ = (bitField0_ & ~0x00000080); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.getDescriptor(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction getDefaultInstanceForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.getDefaultInstance(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction build() { - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.api.BCSAPIMessage.Transaction buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction buildPartial() { - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction result = new com.bitsofproof.supernode.api.BCSAPIMessage.Transaction(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.bcsapiversion_ = bcsapiversion_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.version_ = version_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.locktime_ = locktime_; - if (inputsBuilder_ == null) { - if (((bitField0_ & 0x00000008) == 0x00000008)) { - inputs_ = java.util.Collections.unmodifiableList(inputs_); - bitField0_ = (bitField0_ & ~0x00000008); - } - result.inputs_ = inputs_; - } else { - result.inputs_ = inputsBuilder_.build(); - } - if (outputsBuilder_ == null) { - if (((bitField0_ & 0x00000010) == 0x00000010)) { - outputs_ = java.util.Collections.unmodifiableList(outputs_); - bitField0_ = (bitField0_ & ~0x00000010); - } - result.outputs_ = outputs_; - } else { - result.outputs_ = outputsBuilder_.build(); - } - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000008; - } - result.block_ = block_; - if (((from_bitField0_ & 0x00000040) == 0x00000040)) { - to_bitField0_ |= 0x00000010; - } - result.doubleSpend_ = doubleSpend_; - if (((from_bitField0_ & 0x00000080) == 0x00000080)) { - to_bitField0_ |= 0x00000020; - } - result.height_ = height_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.api.BCSAPIMessage.Transaction) { - return mergeFrom((com.bitsofproof.supernode.api.BCSAPIMessage.Transaction)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.Transaction other) { - if (other == com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.getDefaultInstance()) return this; - if (other.hasBcsapiversion()) { - setBcsapiversion(other.getBcsapiversion()); - } - if (other.hasVersion()) { - setVersion(other.getVersion()); - } - if (other.hasLocktime()) { - setLocktime(other.getLocktime()); - } - if (inputsBuilder_ == null) { - if (!other.inputs_.isEmpty()) { - if (inputs_.isEmpty()) { - inputs_ = other.inputs_; - bitField0_ = (bitField0_ & ~0x00000008); - } else { - ensureInputsIsMutable(); - inputs_.addAll(other.inputs_); - } - onChanged(); - } - } else { - if (!other.inputs_.isEmpty()) { - if (inputsBuilder_.isEmpty()) { - inputsBuilder_.dispose(); - inputsBuilder_ = null; - inputs_ = other.inputs_; - bitField0_ = (bitField0_ & ~0x00000008); - inputsBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getInputsFieldBuilder() : null; - } else { - inputsBuilder_.addAllMessages(other.inputs_); - } - } - } - if (outputsBuilder_ == null) { - if (!other.outputs_.isEmpty()) { - if (outputs_.isEmpty()) { - outputs_ = other.outputs_; - bitField0_ = (bitField0_ & ~0x00000010); - } else { - ensureOutputsIsMutable(); - outputs_.addAll(other.outputs_); - } - onChanged(); - } - } else { - if (!other.outputs_.isEmpty()) { - if (outputsBuilder_.isEmpty()) { - outputsBuilder_.dispose(); - outputsBuilder_ = null; - outputs_ = other.outputs_; - bitField0_ = (bitField0_ & ~0x00000010); - outputsBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getOutputsFieldBuilder() : null; - } else { - outputsBuilder_.addAllMessages(other.outputs_); - } - } - } - if (other.hasBlock()) { - setBlock(other.getBlock()); - } - if (other.hasDoubleSpend()) { - setDoubleSpend(other.getDoubleSpend()); - } - if (other.hasHeight()) { - setHeight(other.getHeight()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasBcsapiversion()) { - - return false; - } - if (!hasVersion()) { - - return false; - } - if (!hasLocktime()) { - - return false; - } - for (int i = 0; i < getInputsCount(); i++) { - if (!getInputs(i).isInitialized()) { - - return false; - } - } - for (int i = 0; i < getOutputsCount(); i++) { - if (!getOutputs(i).isInitialized()) { - - return false; - } - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - bcsapiversion_ = input.readUInt32(); - break; - } - case 24: { - bitField0_ |= 0x00000002; - version_ = input.readUInt32(); - break; - } - case 32: { - bitField0_ |= 0x00000004; - locktime_ = input.readUInt32(); - break; - } - case 42: { - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput.Builder subBuilder = com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput.newBuilder(); - input.readMessage(subBuilder, extensionRegistry); - addInputs(subBuilder.buildPartial()); - break; - } - case 50: { - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput.Builder subBuilder = com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput.newBuilder(); - input.readMessage(subBuilder, extensionRegistry); - addOutputs(subBuilder.buildPartial()); - break; - } - case 58: { - bitField0_ |= 0x00000020; - block_ = input.readBytes(); - break; - } - case 64: { - bitField0_ |= 0x00000040; - doubleSpend_ = input.readBool(); - break; - } - case 72: { - bitField0_ |= 0x00000080; - height_ = input.readUInt32(); - break; - } - } - } - } - - private int bitField0_; - - // required uint32 bcsapiversion = 1; - private int bcsapiversion_ ; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - public Builder setBcsapiversion(int value) { - bitField0_ |= 0x00000001; - bcsapiversion_ = value; - onChanged(); - return this; - } - public Builder clearBcsapiversion() { - bitField0_ = (bitField0_ & ~0x00000001); - bcsapiversion_ = 0; - onChanged(); - return this; - } - - // required uint32 version = 3; - private int version_ ; - public boolean hasVersion() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public int getVersion() { - return version_; - } - public Builder setVersion(int value) { - bitField0_ |= 0x00000002; - version_ = value; - onChanged(); - return this; - } - public Builder clearVersion() { - bitField0_ = (bitField0_ & ~0x00000002); - version_ = 0; - onChanged(); - return this; - } - - // required uint32 locktime = 4; - private int locktime_ ; - public boolean hasLocktime() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public int getLocktime() { - return locktime_; - } - public Builder setLocktime(int value) { - bitField0_ |= 0x00000004; - locktime_ = value; - onChanged(); - return this; - } - public Builder clearLocktime() { - bitField0_ = (bitField0_ & ~0x00000004); - locktime_ = 0; - onChanged(); - return this; - } - - // repeated .com.bitsofproof.supernode.api.TransactionInput inputs = 5; - private java.util.List inputs_ = - java.util.Collections.emptyList(); - private void ensureInputsIsMutable() { - if (!((bitField0_ & 0x00000008) == 0x00000008)) { - inputs_ = new java.util.ArrayList(inputs_); - bitField0_ |= 0x00000008; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInputOrBuilder> inputsBuilder_; - - public java.util.List getInputsList() { - if (inputsBuilder_ == null) { - return java.util.Collections.unmodifiableList(inputs_); - } else { - return inputsBuilder_.getMessageList(); - } - } - public int getInputsCount() { - if (inputsBuilder_ == null) { - return inputs_.size(); - } else { - return inputsBuilder_.getCount(); - } - } - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput getInputs(int index) { - if (inputsBuilder_ == null) { - return inputs_.get(index); - } else { - return inputsBuilder_.getMessage(index); - } - } - public Builder setInputs( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput value) { - if (inputsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureInputsIsMutable(); - inputs_.set(index, value); - onChanged(); - } else { - inputsBuilder_.setMessage(index, value); - } - return this; - } - public Builder setInputs( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput.Builder builderForValue) { - if (inputsBuilder_ == null) { - ensureInputsIsMutable(); - inputs_.set(index, builderForValue.build()); - onChanged(); - } else { - inputsBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - public Builder addInputs(com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput value) { - if (inputsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureInputsIsMutable(); - inputs_.add(value); - onChanged(); - } else { - inputsBuilder_.addMessage(value); - } - return this; - } - public Builder addInputs( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput value) { - if (inputsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureInputsIsMutable(); - inputs_.add(index, value); - onChanged(); - } else { - inputsBuilder_.addMessage(index, value); - } - return this; - } - public Builder addInputs( - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput.Builder builderForValue) { - if (inputsBuilder_ == null) { - ensureInputsIsMutable(); - inputs_.add(builderForValue.build()); - onChanged(); - } else { - inputsBuilder_.addMessage(builderForValue.build()); - } - return this; - } - public Builder addInputs( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput.Builder builderForValue) { - if (inputsBuilder_ == null) { - ensureInputsIsMutable(); - inputs_.add(index, builderForValue.build()); - onChanged(); - } else { - inputsBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - public Builder addAllInputs( - java.lang.Iterable values) { - if (inputsBuilder_ == null) { - ensureInputsIsMutable(); - super.addAll(values, inputs_); - onChanged(); - } else { - inputsBuilder_.addAllMessages(values); - } - return this; - } - public Builder clearInputs() { - if (inputsBuilder_ == null) { - inputs_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); - onChanged(); - } else { - inputsBuilder_.clear(); - } - return this; - } - public Builder removeInputs(int index) { - if (inputsBuilder_ == null) { - ensureInputsIsMutable(); - inputs_.remove(index); - onChanged(); - } else { - inputsBuilder_.remove(index); - } - return this; - } - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput.Builder getInputsBuilder( - int index) { - return getInputsFieldBuilder().getBuilder(index); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInputOrBuilder getInputsOrBuilder( - int index) { - if (inputsBuilder_ == null) { - return inputs_.get(index); } else { - return inputsBuilder_.getMessageOrBuilder(index); - } - } - public java.util.List - getInputsOrBuilderList() { - if (inputsBuilder_ != null) { - return inputsBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(inputs_); - } - } - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput.Builder addInputsBuilder() { - return getInputsFieldBuilder().addBuilder( - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput.getDefaultInstance()); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput.Builder addInputsBuilder( - int index) { - return getInputsFieldBuilder().addBuilder( - index, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput.getDefaultInstance()); - } - public java.util.List - getInputsBuilderList() { - return getInputsFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInputOrBuilder> - getInputsFieldBuilder() { - if (inputsBuilder_ == null) { - inputsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInputOrBuilder>( - inputs_, - ((bitField0_ & 0x00000008) == 0x00000008), - getParentForChildren(), - isClean()); - inputs_ = null; - } - return inputsBuilder_; - } - - // repeated .com.bitsofproof.supernode.api.TransactionOutput outputs = 6; - private java.util.List outputs_ = - java.util.Collections.emptyList(); - private void ensureOutputsIsMutable() { - if (!((bitField0_ & 0x00000010) == 0x00000010)) { - outputs_ = new java.util.ArrayList(outputs_); - bitField0_ |= 0x00000010; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutputOrBuilder> outputsBuilder_; - - public java.util.List getOutputsList() { - if (outputsBuilder_ == null) { - return java.util.Collections.unmodifiableList(outputs_); - } else { - return outputsBuilder_.getMessageList(); - } - } - public int getOutputsCount() { - if (outputsBuilder_ == null) { - return outputs_.size(); - } else { - return outputsBuilder_.getCount(); - } - } - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput getOutputs(int index) { - if (outputsBuilder_ == null) { - return outputs_.get(index); - } else { - return outputsBuilder_.getMessage(index); - } - } - public Builder setOutputs( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput value) { - if (outputsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureOutputsIsMutable(); - outputs_.set(index, value); - onChanged(); - } else { - outputsBuilder_.setMessage(index, value); - } - return this; - } - public Builder setOutputs( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput.Builder builderForValue) { - if (outputsBuilder_ == null) { - ensureOutputsIsMutable(); - outputs_.set(index, builderForValue.build()); - onChanged(); - } else { - outputsBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - public Builder addOutputs(com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput value) { - if (outputsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureOutputsIsMutable(); - outputs_.add(value); - onChanged(); - } else { - outputsBuilder_.addMessage(value); - } - return this; - } - public Builder addOutputs( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput value) { - if (outputsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureOutputsIsMutable(); - outputs_.add(index, value); - onChanged(); - } else { - outputsBuilder_.addMessage(index, value); - } - return this; - } - public Builder addOutputs( - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput.Builder builderForValue) { - if (outputsBuilder_ == null) { - ensureOutputsIsMutable(); - outputs_.add(builderForValue.build()); - onChanged(); - } else { - outputsBuilder_.addMessage(builderForValue.build()); - } - return this; - } - public Builder addOutputs( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput.Builder builderForValue) { - if (outputsBuilder_ == null) { - ensureOutputsIsMutable(); - outputs_.add(index, builderForValue.build()); - onChanged(); - } else { - outputsBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - public Builder addAllOutputs( - java.lang.Iterable values) { - if (outputsBuilder_ == null) { - ensureOutputsIsMutable(); - super.addAll(values, outputs_); - onChanged(); - } else { - outputsBuilder_.addAllMessages(values); - } - return this; - } - public Builder clearOutputs() { - if (outputsBuilder_ == null) { - outputs_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000010); - onChanged(); - } else { - outputsBuilder_.clear(); - } - return this; - } - public Builder removeOutputs(int index) { - if (outputsBuilder_ == null) { - ensureOutputsIsMutable(); - outputs_.remove(index); - onChanged(); - } else { - outputsBuilder_.remove(index); - } - return this; - } - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput.Builder getOutputsBuilder( - int index) { - return getOutputsFieldBuilder().getBuilder(index); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutputOrBuilder getOutputsOrBuilder( - int index) { - if (outputsBuilder_ == null) { - return outputs_.get(index); } else { - return outputsBuilder_.getMessageOrBuilder(index); - } - } - public java.util.List - getOutputsOrBuilderList() { - if (outputsBuilder_ != null) { - return outputsBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(outputs_); - } - } - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput.Builder addOutputsBuilder() { - return getOutputsFieldBuilder().addBuilder( - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput.getDefaultInstance()); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput.Builder addOutputsBuilder( - int index) { - return getOutputsFieldBuilder().addBuilder( - index, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput.getDefaultInstance()); - } - public java.util.List - getOutputsBuilderList() { - return getOutputsFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutputOrBuilder> - getOutputsFieldBuilder() { - if (outputsBuilder_ == null) { - outputsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutputOrBuilder>( - outputs_, - ((bitField0_ & 0x00000010) == 0x00000010), - getParentForChildren(), - isClean()); - outputs_ = null; - } - return outputsBuilder_; - } - - // optional bytes block = 7; - private com.google.protobuf.ByteString block_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasBlock() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - public com.google.protobuf.ByteString getBlock() { - return block_; - } - public Builder setBlock(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000020; - block_ = value; - onChanged(); - return this; - } - public Builder clearBlock() { - bitField0_ = (bitField0_ & ~0x00000020); - block_ = getDefaultInstance().getBlock(); - onChanged(); - return this; - } - - // optional bool doubleSpend = 8; - private boolean doubleSpend_ ; - public boolean hasDoubleSpend() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - public boolean getDoubleSpend() { - return doubleSpend_; - } - public Builder setDoubleSpend(boolean value) { - bitField0_ |= 0x00000040; - doubleSpend_ = value; - onChanged(); - return this; - } - public Builder clearDoubleSpend() { - bitField0_ = (bitField0_ & ~0x00000040); - doubleSpend_ = false; - onChanged(); - return this; - } - - // optional uint32 height = 9; - private int height_ ; - public boolean hasHeight() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - public int getHeight() { - return height_; - } - public Builder setHeight(int value) { - bitField0_ |= 0x00000080; - height_ = value; - onChanged(); - return this; - } - public Builder clearHeight() { - bitField0_ = (bitField0_ & ~0x00000080); - height_ = 0; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.Transaction) - } - - static { - defaultInstance = new Transaction(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.api.Transaction) - } - - public interface BlockOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required uint32 bcsapiversion = 1; - boolean hasBcsapiversion(); - int getBcsapiversion(); - - // required uint32 version = 2; - boolean hasVersion(); - int getVersion(); - - // required bytes previousBlock = 3; - boolean hasPreviousBlock(); - com.google.protobuf.ByteString getPreviousBlock(); - - // required bytes merkleRoot = 4; - boolean hasMerkleRoot(); - com.google.protobuf.ByteString getMerkleRoot(); - - // required uint32 timestamp = 5; - boolean hasTimestamp(); - int getTimestamp(); - - // required uint32 difficulty = 6; - boolean hasDifficulty(); - int getDifficulty(); - - // required uint32 nonce = 7; - boolean hasNonce(); - int getNonce(); - - // repeated .com.bitsofproof.supernode.api.Transaction transactions = 8; - java.util.List - getTransactionsList(); - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction getTransactions(int index); - int getTransactionsCount(); - java.util.List - getTransactionsOrBuilderList(); - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder getTransactionsOrBuilder( - int index); - - // optional uint32 height = 9; - boolean hasHeight(); - int getHeight(); - } - public static final class Block extends - com.google.protobuf.GeneratedMessage - implements BlockOrBuilder { - // Use Block.newBuilder() to construct. - private Block(Builder builder) { - super(builder); - } - private Block(boolean noInit) {} - - private static final Block defaultInstance; - public static Block getDefaultInstance() { - return defaultInstance; - } - - public Block getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Block_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Block_fieldAccessorTable; - } - - private int bitField0_; - // required uint32 bcsapiversion = 1; - public static final int BCSAPIVERSION_FIELD_NUMBER = 1; - private int bcsapiversion_; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - - // required uint32 version = 2; - public static final int VERSION_FIELD_NUMBER = 2; - private int version_; - public boolean hasVersion() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public int getVersion() { - return version_; - } - - // required bytes previousBlock = 3; - public static final int PREVIOUSBLOCK_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString previousBlock_; - public boolean hasPreviousBlock() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public com.google.protobuf.ByteString getPreviousBlock() { - return previousBlock_; - } - - // required bytes merkleRoot = 4; - public static final int MERKLEROOT_FIELD_NUMBER = 4; - private com.google.protobuf.ByteString merkleRoot_; - public boolean hasMerkleRoot() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public com.google.protobuf.ByteString getMerkleRoot() { - return merkleRoot_; - } - - // required uint32 timestamp = 5; - public static final int TIMESTAMP_FIELD_NUMBER = 5; - private int timestamp_; - public boolean hasTimestamp() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - public int getTimestamp() { - return timestamp_; - } - - // required uint32 difficulty = 6; - public static final int DIFFICULTY_FIELD_NUMBER = 6; - private int difficulty_; - public boolean hasDifficulty() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - public int getDifficulty() { - return difficulty_; - } - - // required uint32 nonce = 7; - public static final int NONCE_FIELD_NUMBER = 7; - private int nonce_; - public boolean hasNonce() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - public int getNonce() { - return nonce_; - } - - // repeated .com.bitsofproof.supernode.api.Transaction transactions = 8; - public static final int TRANSACTIONS_FIELD_NUMBER = 8; - private java.util.List transactions_; - public java.util.List getTransactionsList() { - return transactions_; - } - public java.util.List - getTransactionsOrBuilderList() { - return transactions_; - } - public int getTransactionsCount() { - return transactions_.size(); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction getTransactions(int index) { - return transactions_.get(index); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder getTransactionsOrBuilder( - int index) { - return transactions_.get(index); - } - - // optional uint32 height = 9; - public static final int HEIGHT_FIELD_NUMBER = 9; - private int height_; - public boolean hasHeight() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - public int getHeight() { - return height_; - } - - private void initFields() { - bcsapiversion_ = 0; - version_ = 0; - previousBlock_ = com.google.protobuf.ByteString.EMPTY; - merkleRoot_ = com.google.protobuf.ByteString.EMPTY; - timestamp_ = 0; - difficulty_ = 0; - nonce_ = 0; - transactions_ = java.util.Collections.emptyList(); - height_ = 0; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasBcsapiversion()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasVersion()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasPreviousBlock()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasMerkleRoot()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasTimestamp()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasDifficulty()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasNonce()) { - memoizedIsInitialized = 0; - return false; - } - for (int i = 0; i < getTransactionsCount(); i++) { - if (!getTransactions(i).isInitialized()) { - memoizedIsInitialized = 0; - return false; - } - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, bcsapiversion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt32(2, version_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, previousBlock_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(4, merkleRoot_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeUInt32(5, timestamp_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeUInt32(6, difficulty_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - output.writeUInt32(7, nonce_); - } - for (int i = 0; i < transactions_.size(); i++) { - output.writeMessage(8, transactions_.get(i)); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - output.writeUInt32(9, height_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, bcsapiversion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(2, version_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, previousBlock_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(4, merkleRoot_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(5, timestamp_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(6, difficulty_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(7, nonce_); - } - for (int i = 0; i < transactions_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(8, transactions_.get(i)); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(9, height_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.api.BCSAPIMessage.Block parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Block parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Block parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Block parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Block parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Block parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Block parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Block parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Block parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Block parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.api.BCSAPIMessage.Block prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.api.BCSAPIMessage.BlockOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Block_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Block_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.api.BCSAPIMessage.Block.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getTransactionsFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - bcsapiversion_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - version_ = 0; - bitField0_ = (bitField0_ & ~0x00000002); - previousBlock_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - merkleRoot_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000008); - timestamp_ = 0; - bitField0_ = (bitField0_ & ~0x00000010); - difficulty_ = 0; - bitField0_ = (bitField0_ & ~0x00000020); - nonce_ = 0; - bitField0_ = (bitField0_ & ~0x00000040); - if (transactionsBuilder_ == null) { - transactions_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000080); - } else { - transactionsBuilder_.clear(); - } - height_ = 0; - bitField0_ = (bitField0_ & ~0x00000100); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Block.getDescriptor(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Block getDefaultInstanceForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Block.getDefaultInstance(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Block build() { - com.bitsofproof.supernode.api.BCSAPIMessage.Block result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.api.BCSAPIMessage.Block buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.api.BCSAPIMessage.Block result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Block buildPartial() { - com.bitsofproof.supernode.api.BCSAPIMessage.Block result = new com.bitsofproof.supernode.api.BCSAPIMessage.Block(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.bcsapiversion_ = bcsapiversion_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.version_ = version_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.previousBlock_ = previousBlock_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.merkleRoot_ = merkleRoot_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - result.timestamp_ = timestamp_; - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000020; - } - result.difficulty_ = difficulty_; - if (((from_bitField0_ & 0x00000040) == 0x00000040)) { - to_bitField0_ |= 0x00000040; - } - result.nonce_ = nonce_; - if (transactionsBuilder_ == null) { - if (((bitField0_ & 0x00000080) == 0x00000080)) { - transactions_ = java.util.Collections.unmodifiableList(transactions_); - bitField0_ = (bitField0_ & ~0x00000080); - } - result.transactions_ = transactions_; - } else { - result.transactions_ = transactionsBuilder_.build(); - } - if (((from_bitField0_ & 0x00000100) == 0x00000100)) { - to_bitField0_ |= 0x00000080; - } - result.height_ = height_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.api.BCSAPIMessage.Block) { - return mergeFrom((com.bitsofproof.supernode.api.BCSAPIMessage.Block)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.Block other) { - if (other == com.bitsofproof.supernode.api.BCSAPIMessage.Block.getDefaultInstance()) return this; - if (other.hasBcsapiversion()) { - setBcsapiversion(other.getBcsapiversion()); - } - if (other.hasVersion()) { - setVersion(other.getVersion()); - } - if (other.hasPreviousBlock()) { - setPreviousBlock(other.getPreviousBlock()); - } - if (other.hasMerkleRoot()) { - setMerkleRoot(other.getMerkleRoot()); - } - if (other.hasTimestamp()) { - setTimestamp(other.getTimestamp()); - } - if (other.hasDifficulty()) { - setDifficulty(other.getDifficulty()); - } - if (other.hasNonce()) { - setNonce(other.getNonce()); - } - if (transactionsBuilder_ == null) { - if (!other.transactions_.isEmpty()) { - if (transactions_.isEmpty()) { - transactions_ = other.transactions_; - bitField0_ = (bitField0_ & ~0x00000080); - } else { - ensureTransactionsIsMutable(); - transactions_.addAll(other.transactions_); - } - onChanged(); - } - } else { - if (!other.transactions_.isEmpty()) { - if (transactionsBuilder_.isEmpty()) { - transactionsBuilder_.dispose(); - transactionsBuilder_ = null; - transactions_ = other.transactions_; - bitField0_ = (bitField0_ & ~0x00000080); - transactionsBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getTransactionsFieldBuilder() : null; - } else { - transactionsBuilder_.addAllMessages(other.transactions_); - } - } - } - if (other.hasHeight()) { - setHeight(other.getHeight()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasBcsapiversion()) { - - return false; - } - if (!hasVersion()) { - - return false; - } - if (!hasPreviousBlock()) { - - return false; - } - if (!hasMerkleRoot()) { - - return false; - } - if (!hasTimestamp()) { - - return false; - } - if (!hasDifficulty()) { - - return false; - } - if (!hasNonce()) { - - return false; - } - for (int i = 0; i < getTransactionsCount(); i++) { - if (!getTransactions(i).isInitialized()) { - - return false; - } - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - bcsapiversion_ = input.readUInt32(); - break; - } - case 16: { - bitField0_ |= 0x00000002; - version_ = input.readUInt32(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - previousBlock_ = input.readBytes(); - break; - } - case 34: { - bitField0_ |= 0x00000008; - merkleRoot_ = input.readBytes(); - break; - } - case 40: { - bitField0_ |= 0x00000010; - timestamp_ = input.readUInt32(); - break; - } - case 48: { - bitField0_ |= 0x00000020; - difficulty_ = input.readUInt32(); - break; - } - case 56: { - bitField0_ |= 0x00000040; - nonce_ = input.readUInt32(); - break; - } - case 66: { - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder subBuilder = com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.newBuilder(); - input.readMessage(subBuilder, extensionRegistry); - addTransactions(subBuilder.buildPartial()); - break; - } - case 72: { - bitField0_ |= 0x00000100; - height_ = input.readUInt32(); - break; - } - } - } - } - - private int bitField0_; - - // required uint32 bcsapiversion = 1; - private int bcsapiversion_ ; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - public Builder setBcsapiversion(int value) { - bitField0_ |= 0x00000001; - bcsapiversion_ = value; - onChanged(); - return this; - } - public Builder clearBcsapiversion() { - bitField0_ = (bitField0_ & ~0x00000001); - bcsapiversion_ = 0; - onChanged(); - return this; - } - - // required uint32 version = 2; - private int version_ ; - public boolean hasVersion() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public int getVersion() { - return version_; - } - public Builder setVersion(int value) { - bitField0_ |= 0x00000002; - version_ = value; - onChanged(); - return this; - } - public Builder clearVersion() { - bitField0_ = (bitField0_ & ~0x00000002); - version_ = 0; - onChanged(); - return this; - } - - // required bytes previousBlock = 3; - private com.google.protobuf.ByteString previousBlock_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasPreviousBlock() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public com.google.protobuf.ByteString getPreviousBlock() { - return previousBlock_; - } - public Builder setPreviousBlock(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - previousBlock_ = value; - onChanged(); - return this; - } - public Builder clearPreviousBlock() { - bitField0_ = (bitField0_ & ~0x00000004); - previousBlock_ = getDefaultInstance().getPreviousBlock(); - onChanged(); - return this; - } - - // required bytes merkleRoot = 4; - private com.google.protobuf.ByteString merkleRoot_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasMerkleRoot() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public com.google.protobuf.ByteString getMerkleRoot() { - return merkleRoot_; - } - public Builder setMerkleRoot(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - merkleRoot_ = value; - onChanged(); - return this; - } - public Builder clearMerkleRoot() { - bitField0_ = (bitField0_ & ~0x00000008); - merkleRoot_ = getDefaultInstance().getMerkleRoot(); - onChanged(); - return this; - } - - // required uint32 timestamp = 5; - private int timestamp_ ; - public boolean hasTimestamp() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - public int getTimestamp() { - return timestamp_; - } - public Builder setTimestamp(int value) { - bitField0_ |= 0x00000010; - timestamp_ = value; - onChanged(); - return this; - } - public Builder clearTimestamp() { - bitField0_ = (bitField0_ & ~0x00000010); - timestamp_ = 0; - onChanged(); - return this; - } - - // required uint32 difficulty = 6; - private int difficulty_ ; - public boolean hasDifficulty() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - public int getDifficulty() { - return difficulty_; - } - public Builder setDifficulty(int value) { - bitField0_ |= 0x00000020; - difficulty_ = value; - onChanged(); - return this; - } - public Builder clearDifficulty() { - bitField0_ = (bitField0_ & ~0x00000020); - difficulty_ = 0; - onChanged(); - return this; - } - - // required uint32 nonce = 7; - private int nonce_ ; - public boolean hasNonce() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - public int getNonce() { - return nonce_; - } - public Builder setNonce(int value) { - bitField0_ |= 0x00000040; - nonce_ = value; - onChanged(); - return this; - } - public Builder clearNonce() { - bitField0_ = (bitField0_ & ~0x00000040); - nonce_ = 0; - onChanged(); - return this; - } - - // repeated .com.bitsofproof.supernode.api.Transaction transactions = 8; - private java.util.List transactions_ = - java.util.Collections.emptyList(); - private void ensureTransactionsIsMutable() { - if (!((bitField0_ & 0x00000080) == 0x00000080)) { - transactions_ = new java.util.ArrayList(transactions_); - bitField0_ |= 0x00000080; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder> transactionsBuilder_; - - public java.util.List getTransactionsList() { - if (transactionsBuilder_ == null) { - return java.util.Collections.unmodifiableList(transactions_); - } else { - return transactionsBuilder_.getMessageList(); - } - } - public int getTransactionsCount() { - if (transactionsBuilder_ == null) { - return transactions_.size(); - } else { - return transactionsBuilder_.getCount(); - } - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction getTransactions(int index) { - if (transactionsBuilder_ == null) { - return transactions_.get(index); - } else { - return transactionsBuilder_.getMessage(index); - } - } - public Builder setTransactions( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction value) { - if (transactionsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureTransactionsIsMutable(); - transactions_.set(index, value); - onChanged(); - } else { - transactionsBuilder_.setMessage(index, value); - } - return this; - } - public Builder setTransactions( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder builderForValue) { - if (transactionsBuilder_ == null) { - ensureTransactionsIsMutable(); - transactions_.set(index, builderForValue.build()); - onChanged(); - } else { - transactionsBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - public Builder addTransactions(com.bitsofproof.supernode.api.BCSAPIMessage.Transaction value) { - if (transactionsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureTransactionsIsMutable(); - transactions_.add(value); - onChanged(); - } else { - transactionsBuilder_.addMessage(value); - } - return this; - } - public Builder addTransactions( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction value) { - if (transactionsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureTransactionsIsMutable(); - transactions_.add(index, value); - onChanged(); - } else { - transactionsBuilder_.addMessage(index, value); - } - return this; - } - public Builder addTransactions( - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder builderForValue) { - if (transactionsBuilder_ == null) { - ensureTransactionsIsMutable(); - transactions_.add(builderForValue.build()); - onChanged(); - } else { - transactionsBuilder_.addMessage(builderForValue.build()); - } - return this; - } - public Builder addTransactions( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder builderForValue) { - if (transactionsBuilder_ == null) { - ensureTransactionsIsMutable(); - transactions_.add(index, builderForValue.build()); - onChanged(); - } else { - transactionsBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - public Builder addAllTransactions( - java.lang.Iterable values) { - if (transactionsBuilder_ == null) { - ensureTransactionsIsMutable(); - super.addAll(values, transactions_); - onChanged(); - } else { - transactionsBuilder_.addAllMessages(values); - } - return this; - } - public Builder clearTransactions() { - if (transactionsBuilder_ == null) { - transactions_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000080); - onChanged(); - } else { - transactionsBuilder_.clear(); - } - return this; - } - public Builder removeTransactions(int index) { - if (transactionsBuilder_ == null) { - ensureTransactionsIsMutable(); - transactions_.remove(index); - onChanged(); - } else { - transactionsBuilder_.remove(index); - } - return this; - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder getTransactionsBuilder( - int index) { - return getTransactionsFieldBuilder().getBuilder(index); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder getTransactionsOrBuilder( - int index) { - if (transactionsBuilder_ == null) { - return transactions_.get(index); } else { - return transactionsBuilder_.getMessageOrBuilder(index); - } - } - public java.util.List - getTransactionsOrBuilderList() { - if (transactionsBuilder_ != null) { - return transactionsBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(transactions_); - } - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder addTransactionsBuilder() { - return getTransactionsFieldBuilder().addBuilder( - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.getDefaultInstance()); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder addTransactionsBuilder( - int index) { - return getTransactionsFieldBuilder().addBuilder( - index, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.getDefaultInstance()); - } - public java.util.List - getTransactionsBuilderList() { - return getTransactionsFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder> - getTransactionsFieldBuilder() { - if (transactionsBuilder_ == null) { - transactionsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction, com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOrBuilder>( - transactions_, - ((bitField0_ & 0x00000080) == 0x00000080), - getParentForChildren(), - isClean()); - transactions_ = null; - } - return transactionsBuilder_; - } - - // optional uint32 height = 9; - private int height_ ; - public boolean hasHeight() { - return ((bitField0_ & 0x00000100) == 0x00000100); - } - public int getHeight() { - return height_; - } - public Builder setHeight(int value) { - bitField0_ |= 0x00000100; - height_ = value; - onChanged(); - return this; - } - public Builder clearHeight() { - bitField0_ = (bitField0_ & ~0x00000100); - height_ = 0; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.Block) - } - - static { - defaultInstance = new Block(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.api.Block) - } - - public interface TrunkUpdateOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required uint32 bcsapiversion = 1; - boolean hasBcsapiversion(); - int getBcsapiversion(); - - // repeated .com.bitsofproof.supernode.api.Block added = 2; - java.util.List - getAddedList(); - com.bitsofproof.supernode.api.BCSAPIMessage.Block getAdded(int index); - int getAddedCount(); - java.util.List - getAddedOrBuilderList(); - com.bitsofproof.supernode.api.BCSAPIMessage.BlockOrBuilder getAddedOrBuilder( - int index); - - // repeated .com.bitsofproof.supernode.api.Block removed = 3; - java.util.List - getRemovedList(); - com.bitsofproof.supernode.api.BCSAPIMessage.Block getRemoved(int index); - int getRemovedCount(); - java.util.List - getRemovedOrBuilderList(); - com.bitsofproof.supernode.api.BCSAPIMessage.BlockOrBuilder getRemovedOrBuilder( - int index); - } - public static final class TrunkUpdate extends - com.google.protobuf.GeneratedMessage - implements TrunkUpdateOrBuilder { - // Use TrunkUpdate.newBuilder() to construct. - private TrunkUpdate(Builder builder) { - super(builder); - } - private TrunkUpdate(boolean noInit) {} - - private static final TrunkUpdate defaultInstance; - public static TrunkUpdate getDefaultInstance() { - return defaultInstance; - } - - public TrunkUpdate getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_TrunkUpdate_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_TrunkUpdate_fieldAccessorTable; - } - - private int bitField0_; - // required uint32 bcsapiversion = 1; - public static final int BCSAPIVERSION_FIELD_NUMBER = 1; - private int bcsapiversion_; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - - // repeated .com.bitsofproof.supernode.api.Block added = 2; - public static final int ADDED_FIELD_NUMBER = 2; - private java.util.List added_; - public java.util.List getAddedList() { - return added_; - } - public java.util.List - getAddedOrBuilderList() { - return added_; - } - public int getAddedCount() { - return added_.size(); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Block getAdded(int index) { - return added_.get(index); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.BlockOrBuilder getAddedOrBuilder( - int index) { - return added_.get(index); - } - - // repeated .com.bitsofproof.supernode.api.Block removed = 3; - public static final int REMOVED_FIELD_NUMBER = 3; - private java.util.List removed_; - public java.util.List getRemovedList() { - return removed_; - } - public java.util.List - getRemovedOrBuilderList() { - return removed_; - } - public int getRemovedCount() { - return removed_.size(); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Block getRemoved(int index) { - return removed_.get(index); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.BlockOrBuilder getRemovedOrBuilder( - int index) { - return removed_.get(index); - } - - private void initFields() { - bcsapiversion_ = 0; - added_ = java.util.Collections.emptyList(); - removed_ = java.util.Collections.emptyList(); - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasBcsapiversion()) { - memoizedIsInitialized = 0; - return false; - } - for (int i = 0; i < getAddedCount(); i++) { - if (!getAdded(i).isInitialized()) { - memoizedIsInitialized = 0; - return false; - } - } - for (int i = 0; i < getRemovedCount(); i++) { - if (!getRemoved(i).isInitialized()) { - memoizedIsInitialized = 0; - return false; - } - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, bcsapiversion_); - } - for (int i = 0; i < added_.size(); i++) { - output.writeMessage(2, added_.get(i)); - } - for (int i = 0; i < removed_.size(); i++) { - output.writeMessage(3, removed_.get(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, bcsapiversion_); - } - for (int i = 0; i < added_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(2, added_.get(i)); - } - for (int i = 0; i < removed_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(3, removed_.get(i)); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdateOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_TrunkUpdate_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_TrunkUpdate_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getAddedFieldBuilder(); - getRemovedFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - bcsapiversion_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - if (addedBuilder_ == null) { - added_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000002); - } else { - addedBuilder_.clear(); - } - if (removedBuilder_ == null) { - removed_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000004); - } else { - removedBuilder_.clear(); - } - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate.getDescriptor(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate getDefaultInstanceForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate.getDefaultInstance(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate build() { - com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate buildPartial() { - com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate result = new com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.bcsapiversion_ = bcsapiversion_; - if (addedBuilder_ == null) { - if (((bitField0_ & 0x00000002) == 0x00000002)) { - added_ = java.util.Collections.unmodifiableList(added_); - bitField0_ = (bitField0_ & ~0x00000002); - } - result.added_ = added_; - } else { - result.added_ = addedBuilder_.build(); - } - if (removedBuilder_ == null) { - if (((bitField0_ & 0x00000004) == 0x00000004)) { - removed_ = java.util.Collections.unmodifiableList(removed_); - bitField0_ = (bitField0_ & ~0x00000004); - } - result.removed_ = removed_; - } else { - result.removed_ = removedBuilder_.build(); - } - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate) { - return mergeFrom((com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate other) { - if (other == com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate.getDefaultInstance()) return this; - if (other.hasBcsapiversion()) { - setBcsapiversion(other.getBcsapiversion()); - } - if (addedBuilder_ == null) { - if (!other.added_.isEmpty()) { - if (added_.isEmpty()) { - added_ = other.added_; - bitField0_ = (bitField0_ & ~0x00000002); - } else { - ensureAddedIsMutable(); - added_.addAll(other.added_); - } - onChanged(); - } - } else { - if (!other.added_.isEmpty()) { - if (addedBuilder_.isEmpty()) { - addedBuilder_.dispose(); - addedBuilder_ = null; - added_ = other.added_; - bitField0_ = (bitField0_ & ~0x00000002); - addedBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getAddedFieldBuilder() : null; - } else { - addedBuilder_.addAllMessages(other.added_); - } - } - } - if (removedBuilder_ == null) { - if (!other.removed_.isEmpty()) { - if (removed_.isEmpty()) { - removed_ = other.removed_; - bitField0_ = (bitField0_ & ~0x00000004); - } else { - ensureRemovedIsMutable(); - removed_.addAll(other.removed_); - } - onChanged(); - } - } else { - if (!other.removed_.isEmpty()) { - if (removedBuilder_.isEmpty()) { - removedBuilder_.dispose(); - removedBuilder_ = null; - removed_ = other.removed_; - bitField0_ = (bitField0_ & ~0x00000004); - removedBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getRemovedFieldBuilder() : null; - } else { - removedBuilder_.addAllMessages(other.removed_); - } - } - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasBcsapiversion()) { - - return false; - } - for (int i = 0; i < getAddedCount(); i++) { - if (!getAdded(i).isInitialized()) { - - return false; - } - } - for (int i = 0; i < getRemovedCount(); i++) { - if (!getRemoved(i).isInitialized()) { - - return false; - } - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - bcsapiversion_ = input.readUInt32(); - break; - } - case 18: { - com.bitsofproof.supernode.api.BCSAPIMessage.Block.Builder subBuilder = com.bitsofproof.supernode.api.BCSAPIMessage.Block.newBuilder(); - input.readMessage(subBuilder, extensionRegistry); - addAdded(subBuilder.buildPartial()); - break; - } - case 26: { - com.bitsofproof.supernode.api.BCSAPIMessage.Block.Builder subBuilder = com.bitsofproof.supernode.api.BCSAPIMessage.Block.newBuilder(); - input.readMessage(subBuilder, extensionRegistry); - addRemoved(subBuilder.buildPartial()); - break; - } - } - } - } - - private int bitField0_; - - // required uint32 bcsapiversion = 1; - private int bcsapiversion_ ; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - public Builder setBcsapiversion(int value) { - bitField0_ |= 0x00000001; - bcsapiversion_ = value; - onChanged(); - return this; - } - public Builder clearBcsapiversion() { - bitField0_ = (bitField0_ & ~0x00000001); - bcsapiversion_ = 0; - onChanged(); - return this; - } - - // repeated .com.bitsofproof.supernode.api.Block added = 2; - private java.util.List added_ = - java.util.Collections.emptyList(); - private void ensureAddedIsMutable() { - if (!((bitField0_ & 0x00000002) == 0x00000002)) { - added_ = new java.util.ArrayList(added_); - bitField0_ |= 0x00000002; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Block, com.bitsofproof.supernode.api.BCSAPIMessage.Block.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.BlockOrBuilder> addedBuilder_; - - public java.util.List getAddedList() { - if (addedBuilder_ == null) { - return java.util.Collections.unmodifiableList(added_); - } else { - return addedBuilder_.getMessageList(); - } - } - public int getAddedCount() { - if (addedBuilder_ == null) { - return added_.size(); - } else { - return addedBuilder_.getCount(); - } - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Block getAdded(int index) { - if (addedBuilder_ == null) { - return added_.get(index); - } else { - return addedBuilder_.getMessage(index); - } - } - public Builder setAdded( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Block value) { - if (addedBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureAddedIsMutable(); - added_.set(index, value); - onChanged(); - } else { - addedBuilder_.setMessage(index, value); - } - return this; - } - public Builder setAdded( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Block.Builder builderForValue) { - if (addedBuilder_ == null) { - ensureAddedIsMutable(); - added_.set(index, builderForValue.build()); - onChanged(); - } else { - addedBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - public Builder addAdded(com.bitsofproof.supernode.api.BCSAPIMessage.Block value) { - if (addedBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureAddedIsMutable(); - added_.add(value); - onChanged(); - } else { - addedBuilder_.addMessage(value); - } - return this; - } - public Builder addAdded( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Block value) { - if (addedBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureAddedIsMutable(); - added_.add(index, value); - onChanged(); - } else { - addedBuilder_.addMessage(index, value); - } - return this; - } - public Builder addAdded( - com.bitsofproof.supernode.api.BCSAPIMessage.Block.Builder builderForValue) { - if (addedBuilder_ == null) { - ensureAddedIsMutable(); - added_.add(builderForValue.build()); - onChanged(); - } else { - addedBuilder_.addMessage(builderForValue.build()); - } - return this; - } - public Builder addAdded( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Block.Builder builderForValue) { - if (addedBuilder_ == null) { - ensureAddedIsMutable(); - added_.add(index, builderForValue.build()); - onChanged(); - } else { - addedBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - public Builder addAllAdded( - java.lang.Iterable values) { - if (addedBuilder_ == null) { - ensureAddedIsMutable(); - super.addAll(values, added_); - onChanged(); - } else { - addedBuilder_.addAllMessages(values); - } - return this; - } - public Builder clearAdded() { - if (addedBuilder_ == null) { - added_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000002); - onChanged(); - } else { - addedBuilder_.clear(); - } - return this; - } - public Builder removeAdded(int index) { - if (addedBuilder_ == null) { - ensureAddedIsMutable(); - added_.remove(index); - onChanged(); - } else { - addedBuilder_.remove(index); - } - return this; - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Block.Builder getAddedBuilder( - int index) { - return getAddedFieldBuilder().getBuilder(index); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.BlockOrBuilder getAddedOrBuilder( - int index) { - if (addedBuilder_ == null) { - return added_.get(index); } else { - return addedBuilder_.getMessageOrBuilder(index); - } - } - public java.util.List - getAddedOrBuilderList() { - if (addedBuilder_ != null) { - return addedBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(added_); - } - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Block.Builder addAddedBuilder() { - return getAddedFieldBuilder().addBuilder( - com.bitsofproof.supernode.api.BCSAPIMessage.Block.getDefaultInstance()); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Block.Builder addAddedBuilder( - int index) { - return getAddedFieldBuilder().addBuilder( - index, com.bitsofproof.supernode.api.BCSAPIMessage.Block.getDefaultInstance()); - } - public java.util.List - getAddedBuilderList() { - return getAddedFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Block, com.bitsofproof.supernode.api.BCSAPIMessage.Block.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.BlockOrBuilder> - getAddedFieldBuilder() { - if (addedBuilder_ == null) { - addedBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Block, com.bitsofproof.supernode.api.BCSAPIMessage.Block.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.BlockOrBuilder>( - added_, - ((bitField0_ & 0x00000002) == 0x00000002), - getParentForChildren(), - isClean()); - added_ = null; - } - return addedBuilder_; - } - - // repeated .com.bitsofproof.supernode.api.Block removed = 3; - private java.util.List removed_ = - java.util.Collections.emptyList(); - private void ensureRemovedIsMutable() { - if (!((bitField0_ & 0x00000004) == 0x00000004)) { - removed_ = new java.util.ArrayList(removed_); - bitField0_ |= 0x00000004; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Block, com.bitsofproof.supernode.api.BCSAPIMessage.Block.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.BlockOrBuilder> removedBuilder_; - - public java.util.List getRemovedList() { - if (removedBuilder_ == null) { - return java.util.Collections.unmodifiableList(removed_); - } else { - return removedBuilder_.getMessageList(); - } - } - public int getRemovedCount() { - if (removedBuilder_ == null) { - return removed_.size(); - } else { - return removedBuilder_.getCount(); - } - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Block getRemoved(int index) { - if (removedBuilder_ == null) { - return removed_.get(index); - } else { - return removedBuilder_.getMessage(index); - } - } - public Builder setRemoved( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Block value) { - if (removedBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureRemovedIsMutable(); - removed_.set(index, value); - onChanged(); - } else { - removedBuilder_.setMessage(index, value); - } - return this; - } - public Builder setRemoved( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Block.Builder builderForValue) { - if (removedBuilder_ == null) { - ensureRemovedIsMutable(); - removed_.set(index, builderForValue.build()); - onChanged(); - } else { - removedBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - public Builder addRemoved(com.bitsofproof.supernode.api.BCSAPIMessage.Block value) { - if (removedBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureRemovedIsMutable(); - removed_.add(value); - onChanged(); - } else { - removedBuilder_.addMessage(value); - } - return this; - } - public Builder addRemoved( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Block value) { - if (removedBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureRemovedIsMutable(); - removed_.add(index, value); - onChanged(); - } else { - removedBuilder_.addMessage(index, value); - } - return this; - } - public Builder addRemoved( - com.bitsofproof.supernode.api.BCSAPIMessage.Block.Builder builderForValue) { - if (removedBuilder_ == null) { - ensureRemovedIsMutable(); - removed_.add(builderForValue.build()); - onChanged(); - } else { - removedBuilder_.addMessage(builderForValue.build()); - } - return this; - } - public Builder addRemoved( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Block.Builder builderForValue) { - if (removedBuilder_ == null) { - ensureRemovedIsMutable(); - removed_.add(index, builderForValue.build()); - onChanged(); - } else { - removedBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - public Builder addAllRemoved( - java.lang.Iterable values) { - if (removedBuilder_ == null) { - ensureRemovedIsMutable(); - super.addAll(values, removed_); - onChanged(); - } else { - removedBuilder_.addAllMessages(values); - } - return this; - } - public Builder clearRemoved() { - if (removedBuilder_ == null) { - removed_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000004); - onChanged(); - } else { - removedBuilder_.clear(); - } - return this; - } - public Builder removeRemoved(int index) { - if (removedBuilder_ == null) { - ensureRemovedIsMutable(); - removed_.remove(index); - onChanged(); - } else { - removedBuilder_.remove(index); - } - return this; - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Block.Builder getRemovedBuilder( - int index) { - return getRemovedFieldBuilder().getBuilder(index); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.BlockOrBuilder getRemovedOrBuilder( - int index) { - if (removedBuilder_ == null) { - return removed_.get(index); } else { - return removedBuilder_.getMessageOrBuilder(index); - } - } - public java.util.List - getRemovedOrBuilderList() { - if (removedBuilder_ != null) { - return removedBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(removed_); - } - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Block.Builder addRemovedBuilder() { - return getRemovedFieldBuilder().addBuilder( - com.bitsofproof.supernode.api.BCSAPIMessage.Block.getDefaultInstance()); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Block.Builder addRemovedBuilder( - int index) { - return getRemovedFieldBuilder().addBuilder( - index, com.bitsofproof.supernode.api.BCSAPIMessage.Block.getDefaultInstance()); - } - public java.util.List - getRemovedBuilderList() { - return getRemovedFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Block, com.bitsofproof.supernode.api.BCSAPIMessage.Block.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.BlockOrBuilder> - getRemovedFieldBuilder() { - if (removedBuilder_ == null) { - removedBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Block, com.bitsofproof.supernode.api.BCSAPIMessage.Block.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.BlockOrBuilder>( - removed_, - ((bitField0_ & 0x00000004) == 0x00000004), - getParentForChildren(), - isClean()); - removed_ = null; - } - return removedBuilder_; - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.TrunkUpdate) - } - - static { - defaultInstance = new TrunkUpdate(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.api.TrunkUpdate) - } - - public interface HashOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required uint32 bcsapiversion = 1; - boolean hasBcsapiversion(); - int getBcsapiversion(); - - // repeated bytes hash = 2; - java.util.List getHashList(); - int getHashCount(); - com.google.protobuf.ByteString getHash(int index); - } - public static final class Hash extends - com.google.protobuf.GeneratedMessage - implements HashOrBuilder { - // Use Hash.newBuilder() to construct. - private Hash(Builder builder) { - super(builder); - } - private Hash(boolean noInit) {} - - private static final Hash defaultInstance; - public static Hash getDefaultInstance() { - return defaultInstance; - } - - public Hash getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Hash_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Hash_fieldAccessorTable; - } - - private int bitField0_; - // required uint32 bcsapiversion = 1; - public static final int BCSAPIVERSION_FIELD_NUMBER = 1; - private int bcsapiversion_; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - - // repeated bytes hash = 2; - public static final int HASH_FIELD_NUMBER = 2; - private java.util.List hash_; - public java.util.List - getHashList() { - return hash_; - } - public int getHashCount() { - return hash_.size(); - } - public com.google.protobuf.ByteString getHash(int index) { - return hash_.get(index); - } - - private void initFields() { - bcsapiversion_ = 0; - hash_ = java.util.Collections.emptyList();; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasBcsapiversion()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, bcsapiversion_); - } - for (int i = 0; i < hash_.size(); i++) { - output.writeBytes(2, hash_.get(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, bcsapiversion_); - } - { - int dataSize = 0; - for (int i = 0; i < hash_.size(); i++) { - dataSize += com.google.protobuf.CodedOutputStream - .computeBytesSizeNoTag(hash_.get(i)); - } - size += dataSize; - size += 1 * getHashList().size(); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.api.BCSAPIMessage.Hash parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Hash parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Hash parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Hash parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Hash parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Hash parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Hash parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Hash parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Hash parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Hash parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.api.BCSAPIMessage.Hash prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.api.BCSAPIMessage.HashOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Hash_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Hash_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.api.BCSAPIMessage.Hash.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - bcsapiversion_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - hash_ = java.util.Collections.emptyList();; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Hash.getDescriptor(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Hash getDefaultInstanceForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Hash.getDefaultInstance(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Hash build() { - com.bitsofproof.supernode.api.BCSAPIMessage.Hash result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.api.BCSAPIMessage.Hash buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.api.BCSAPIMessage.Hash result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Hash buildPartial() { - com.bitsofproof.supernode.api.BCSAPIMessage.Hash result = new com.bitsofproof.supernode.api.BCSAPIMessage.Hash(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.bcsapiversion_ = bcsapiversion_; - if (((bitField0_ & 0x00000002) == 0x00000002)) { - hash_ = java.util.Collections.unmodifiableList(hash_); - bitField0_ = (bitField0_ & ~0x00000002); - } - result.hash_ = hash_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.api.BCSAPIMessage.Hash) { - return mergeFrom((com.bitsofproof.supernode.api.BCSAPIMessage.Hash)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.Hash other) { - if (other == com.bitsofproof.supernode.api.BCSAPIMessage.Hash.getDefaultInstance()) return this; - if (other.hasBcsapiversion()) { - setBcsapiversion(other.getBcsapiversion()); - } - if (!other.hash_.isEmpty()) { - if (hash_.isEmpty()) { - hash_ = other.hash_; - bitField0_ = (bitField0_ & ~0x00000002); - } else { - ensureHashIsMutable(); - hash_.addAll(other.hash_); - } - onChanged(); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasBcsapiversion()) { - - return false; - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - bcsapiversion_ = input.readUInt32(); - break; - } - case 18: { - ensureHashIsMutable(); - hash_.add(input.readBytes()); - break; - } - } - } - } - - private int bitField0_; - - // required uint32 bcsapiversion = 1; - private int bcsapiversion_ ; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - public Builder setBcsapiversion(int value) { - bitField0_ |= 0x00000001; - bcsapiversion_ = value; - onChanged(); - return this; - } - public Builder clearBcsapiversion() { - bitField0_ = (bitField0_ & ~0x00000001); - bcsapiversion_ = 0; - onChanged(); - return this; - } - - // repeated bytes hash = 2; - private java.util.List hash_ = java.util.Collections.emptyList();; - private void ensureHashIsMutable() { - if (!((bitField0_ & 0x00000002) == 0x00000002)) { - hash_ = new java.util.ArrayList(hash_); - bitField0_ |= 0x00000002; - } - } - public java.util.List - getHashList() { - return java.util.Collections.unmodifiableList(hash_); - } - public int getHashCount() { - return hash_.size(); - } - public com.google.protobuf.ByteString getHash(int index) { - return hash_.get(index); - } - public Builder setHash( - int index, com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureHashIsMutable(); - hash_.set(index, value); - onChanged(); - return this; - } - public Builder addHash(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureHashIsMutable(); - hash_.add(value); - onChanged(); - return this; - } - public Builder addAllHash( - java.lang.Iterable values) { - ensureHashIsMutable(); - super.addAll(values, hash_); - onChanged(); - return this; - } - public Builder clearHash() { - hash_ = java.util.Collections.emptyList();; - bitField0_ = (bitField0_ & ~0x00000002); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.Hash) - } - - static { - defaultInstance = new Hash(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.api.Hash) - } - - public interface ExceptionMessageOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required uint32 bcsapiversion = 1; - boolean hasBcsapiversion(); - int getBcsapiversion(); - - // repeated string message = 2; - java.util.List getMessageList(); - int getMessageCount(); - String getMessage(int index); - } - public static final class ExceptionMessage extends - com.google.protobuf.GeneratedMessage - implements ExceptionMessageOrBuilder { - // Use ExceptionMessage.newBuilder() to construct. - private ExceptionMessage(Builder builder) { - super(builder); - } - private ExceptionMessage(boolean noInit) {} - - private static final ExceptionMessage defaultInstance; - public static ExceptionMessage getDefaultInstance() { - return defaultInstance; - } - - public ExceptionMessage getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_ExceptionMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_ExceptionMessage_fieldAccessorTable; - } - - private int bitField0_; - // required uint32 bcsapiversion = 1; - public static final int BCSAPIVERSION_FIELD_NUMBER = 1; - private int bcsapiversion_; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - - // repeated string message = 2; - public static final int MESSAGE_FIELD_NUMBER = 2; - private com.google.protobuf.LazyStringList message_; - public java.util.List - getMessageList() { - return message_; - } - public int getMessageCount() { - return message_.size(); - } - public String getMessage(int index) { - return message_.get(index); - } - - private void initFields() { - bcsapiversion_ = 0; - message_ = com.google.protobuf.LazyStringArrayList.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasBcsapiversion()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, bcsapiversion_); - } - for (int i = 0; i < message_.size(); i++) { - output.writeBytes(2, message_.getByteString(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, bcsapiversion_); - } - { - int dataSize = 0; - for (int i = 0; i < message_.size(); i++) { - dataSize += com.google.protobuf.CodedOutputStream - .computeBytesSizeNoTag(message_.getByteString(i)); - } - size += dataSize; - size += 1 * getMessageList().size(); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessageOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_ExceptionMessage_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_ExceptionMessage_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - bcsapiversion_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - message_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage.getDescriptor(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage getDefaultInstanceForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage.getDefaultInstance(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage build() { - com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage buildPartial() { - com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage result = new com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.bcsapiversion_ = bcsapiversion_; - if (((bitField0_ & 0x00000002) == 0x00000002)) { - message_ = new com.google.protobuf.UnmodifiableLazyStringList( - message_); - bitField0_ = (bitField0_ & ~0x00000002); - } - result.message_ = message_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage) { - return mergeFrom((com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage other) { - if (other == com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage.getDefaultInstance()) return this; - if (other.hasBcsapiversion()) { - setBcsapiversion(other.getBcsapiversion()); - } - if (!other.message_.isEmpty()) { - if (message_.isEmpty()) { - message_ = other.message_; - bitField0_ = (bitField0_ & ~0x00000002); - } else { - ensureMessageIsMutable(); - message_.addAll(other.message_); - } - onChanged(); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasBcsapiversion()) { - - return false; - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - bcsapiversion_ = input.readUInt32(); - break; - } - case 18: { - ensureMessageIsMutable(); - message_.add(input.readBytes()); - break; - } - } - } - } - - private int bitField0_; - - // required uint32 bcsapiversion = 1; - private int bcsapiversion_ ; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - public Builder setBcsapiversion(int value) { - bitField0_ |= 0x00000001; - bcsapiversion_ = value; - onChanged(); - return this; - } - public Builder clearBcsapiversion() { - bitField0_ = (bitField0_ & ~0x00000001); - bcsapiversion_ = 0; - onChanged(); - return this; - } - - // repeated string message = 2; - private com.google.protobuf.LazyStringList message_ = com.google.protobuf.LazyStringArrayList.EMPTY; - private void ensureMessageIsMutable() { - if (!((bitField0_ & 0x00000002) == 0x00000002)) { - message_ = new com.google.protobuf.LazyStringArrayList(message_); - bitField0_ |= 0x00000002; - } - } - public java.util.List - getMessageList() { - return java.util.Collections.unmodifiableList(message_); - } - public int getMessageCount() { - return message_.size(); - } - public String getMessage(int index) { - return message_.get(index); - } - public Builder setMessage( - int index, String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureMessageIsMutable(); - message_.set(index, value); - onChanged(); - return this; - } - public Builder addMessage(String value) { - if (value == null) { - throw new NullPointerException(); - } - ensureMessageIsMutable(); - message_.add(value); - onChanged(); - return this; - } - public Builder addAllMessage( - java.lang.Iterable values) { - ensureMessageIsMutable(); - super.addAll(values, message_); - onChanged(); - return this; - } - public Builder clearMessage() { - message_ = com.google.protobuf.LazyStringArrayList.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - onChanged(); - return this; - } - void addMessage(com.google.protobuf.ByteString value) { - ensureMessageIsMutable(); - message_.add(value); - onChanged(); - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.ExceptionMessage) - } - - static { - defaultInstance = new ExceptionMessage(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.api.ExceptionMessage) - } - - public interface ColorOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required bytes transaction = 1; - boolean hasTransaction(); - com.google.protobuf.ByteString getTransaction(); - - // required string terms = 2; - boolean hasTerms(); - String getTerms(); - - // required uint64 unit = 3; - boolean hasUnit(); - long getUnit(); - - // required uint32 expiryHeight = 4; - boolean hasExpiryHeight(); - int getExpiryHeight(); - - // required bytes pubkey = 5; - boolean hasPubkey(); - com.google.protobuf.ByteString getPubkey(); - - // required bytes signature = 6; - boolean hasSignature(); - com.google.protobuf.ByteString getSignature(); - } - public static final class Color extends - com.google.protobuf.GeneratedMessage - implements ColorOrBuilder { - // Use Color.newBuilder() to construct. - private Color(Builder builder) { - super(builder); - } - private Color(boolean noInit) {} - - private static final Color defaultInstance; - public static Color getDefaultInstance() { - return defaultInstance; - } - - public Color getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Color_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Color_fieldAccessorTable; - } - - private int bitField0_; - // required bytes transaction = 1; - public static final int TRANSACTION_FIELD_NUMBER = 1; - private com.google.protobuf.ByteString transaction_; - public boolean hasTransaction() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public com.google.protobuf.ByteString getTransaction() { - return transaction_; - } - - // required string terms = 2; - public static final int TERMS_FIELD_NUMBER = 2; - private java.lang.Object terms_; - public boolean hasTerms() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public String getTerms() { - java.lang.Object ref = terms_; - if (ref instanceof String) { - return (String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - String s = bs.toStringUtf8(); - if (com.google.protobuf.Internal.isValidUtf8(bs)) { - terms_ = s; - } - return s; - } - } - private com.google.protobuf.ByteString getTermsBytes() { - java.lang.Object ref = terms_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8((String) ref); - terms_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // required uint64 unit = 3; - public static final int UNIT_FIELD_NUMBER = 3; - private long unit_; - public boolean hasUnit() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public long getUnit() { - return unit_; - } - - // required uint32 expiryHeight = 4; - public static final int EXPIRYHEIGHT_FIELD_NUMBER = 4; - private int expiryHeight_; - public boolean hasExpiryHeight() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public int getExpiryHeight() { - return expiryHeight_; - } - - // required bytes pubkey = 5; - public static final int PUBKEY_FIELD_NUMBER = 5; - private com.google.protobuf.ByteString pubkey_; - public boolean hasPubkey() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - public com.google.protobuf.ByteString getPubkey() { - return pubkey_; - } - - // required bytes signature = 6; - public static final int SIGNATURE_FIELD_NUMBER = 6; - private com.google.protobuf.ByteString signature_; - public boolean hasSignature() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - public com.google.protobuf.ByteString getSignature() { - return signature_; - } - - private void initFields() { - transaction_ = com.google.protobuf.ByteString.EMPTY; - terms_ = ""; - unit_ = 0L; - expiryHeight_ = 0; - pubkey_ = com.google.protobuf.ByteString.EMPTY; - signature_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasTransaction()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasTerms()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasUnit()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasExpiryHeight()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasPubkey()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasSignature()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, transaction_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getTermsBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeUInt64(3, unit_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeUInt32(4, expiryHeight_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeBytes(5, pubkey_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeBytes(6, signature_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, transaction_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getTermsBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(3, unit_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(4, expiryHeight_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(5, pubkey_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(6, signature_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.api.BCSAPIMessage.Color parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Color parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Color parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Color parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Color parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Color parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Color parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Color parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Color parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Color parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.api.BCSAPIMessage.Color prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.api.BCSAPIMessage.ColorOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Color_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Color_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.api.BCSAPIMessage.Color.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - transaction_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000001); - terms_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - unit_ = 0L; - bitField0_ = (bitField0_ & ~0x00000004); - expiryHeight_ = 0; - bitField0_ = (bitField0_ & ~0x00000008); - pubkey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000010); - signature_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000020); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Color.getDescriptor(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Color getDefaultInstanceForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Color.getDefaultInstance(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Color build() { - com.bitsofproof.supernode.api.BCSAPIMessage.Color result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.api.BCSAPIMessage.Color buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.api.BCSAPIMessage.Color result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Color buildPartial() { - com.bitsofproof.supernode.api.BCSAPIMessage.Color result = new com.bitsofproof.supernode.api.BCSAPIMessage.Color(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.transaction_ = transaction_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.terms_ = terms_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.unit_ = unit_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.expiryHeight_ = expiryHeight_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - result.pubkey_ = pubkey_; - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000020; - } - result.signature_ = signature_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.api.BCSAPIMessage.Color) { - return mergeFrom((com.bitsofproof.supernode.api.BCSAPIMessage.Color)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.Color other) { - if (other == com.bitsofproof.supernode.api.BCSAPIMessage.Color.getDefaultInstance()) return this; - if (other.hasTransaction()) { - setTransaction(other.getTransaction()); - } - if (other.hasTerms()) { - setTerms(other.getTerms()); - } - if (other.hasUnit()) { - setUnit(other.getUnit()); - } - if (other.hasExpiryHeight()) { - setExpiryHeight(other.getExpiryHeight()); - } - if (other.hasPubkey()) { - setPubkey(other.getPubkey()); - } - if (other.hasSignature()) { - setSignature(other.getSignature()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasTransaction()) { - - return false; - } - if (!hasTerms()) { - - return false; - } - if (!hasUnit()) { - - return false; - } - if (!hasExpiryHeight()) { - - return false; - } - if (!hasPubkey()) { - - return false; - } - if (!hasSignature()) { - - return false; - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - transaction_ = input.readBytes(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - terms_ = input.readBytes(); - break; - } - case 24: { - bitField0_ |= 0x00000004; - unit_ = input.readUInt64(); - break; - } - case 32: { - bitField0_ |= 0x00000008; - expiryHeight_ = input.readUInt32(); - break; - } - case 42: { - bitField0_ |= 0x00000010; - pubkey_ = input.readBytes(); - break; - } - case 50: { - bitField0_ |= 0x00000020; - signature_ = input.readBytes(); - break; - } - } - } - } - - private int bitField0_; - - // required bytes transaction = 1; - private com.google.protobuf.ByteString transaction_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasTransaction() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public com.google.protobuf.ByteString getTransaction() { - return transaction_; - } - public Builder setTransaction(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - transaction_ = value; - onChanged(); - return this; - } - public Builder clearTransaction() { - bitField0_ = (bitField0_ & ~0x00000001); - transaction_ = getDefaultInstance().getTransaction(); - onChanged(); - return this; - } - - // required string terms = 2; - private java.lang.Object terms_ = ""; - public boolean hasTerms() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public String getTerms() { - java.lang.Object ref = terms_; - if (!(ref instanceof String)) { - String s = ((com.google.protobuf.ByteString) ref).toStringUtf8(); - terms_ = s; - return s; - } else { - return (String) ref; - } - } - public Builder setTerms(String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - terms_ = value; - onChanged(); - return this; - } - public Builder clearTerms() { - bitField0_ = (bitField0_ & ~0x00000002); - terms_ = getDefaultInstance().getTerms(); - onChanged(); - return this; - } - void setTerms(com.google.protobuf.ByteString value) { - bitField0_ |= 0x00000002; - terms_ = value; - onChanged(); - } - - // required uint64 unit = 3; - private long unit_ ; - public boolean hasUnit() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public long getUnit() { - return unit_; - } - public Builder setUnit(long value) { - bitField0_ |= 0x00000004; - unit_ = value; - onChanged(); - return this; - } - public Builder clearUnit() { - bitField0_ = (bitField0_ & ~0x00000004); - unit_ = 0L; - onChanged(); - return this; - } - - // required uint32 expiryHeight = 4; - private int expiryHeight_ ; - public boolean hasExpiryHeight() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public int getExpiryHeight() { - return expiryHeight_; - } - public Builder setExpiryHeight(int value) { - bitField0_ |= 0x00000008; - expiryHeight_ = value; - onChanged(); - return this; - } - public Builder clearExpiryHeight() { - bitField0_ = (bitField0_ & ~0x00000008); - expiryHeight_ = 0; - onChanged(); - return this; - } - - // required bytes pubkey = 5; - private com.google.protobuf.ByteString pubkey_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasPubkey() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - public com.google.protobuf.ByteString getPubkey() { - return pubkey_; - } - public Builder setPubkey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000010; - pubkey_ = value; - onChanged(); - return this; - } - public Builder clearPubkey() { - bitField0_ = (bitField0_ & ~0x00000010); - pubkey_ = getDefaultInstance().getPubkey(); - onChanged(); - return this; - } - - // required bytes signature = 6; - private com.google.protobuf.ByteString signature_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasSignature() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - public com.google.protobuf.ByteString getSignature() { - return signature_; - } - public Builder setSignature(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000020; - signature_ = value; - onChanged(); - return this; - } - public Builder clearSignature() { - bitField0_ = (bitField0_ & ~0x00000020); - signature_ = getDefaultInstance().getSignature(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.Color) - } - - static { - defaultInstance = new Color(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.api.Color) - } - - public interface FilterRequestOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required uint32 bcsapiversion = 1; - boolean hasBcsapiversion(); - int getBcsapiversion(); - - // required bytes filter = 2; - boolean hasFilter(); - com.google.protobuf.ByteString getFilter(); - - // required uint32 hashFunctions = 3; - boolean hasHashFunctions(); - int getHashFunctions(); - - // required uint32 tweak = 4; - boolean hasTweak(); - int getTweak(); - - // required uint32 mode = 5; - boolean hasMode(); - int getMode(); - } - public static final class FilterRequest extends - com.google.protobuf.GeneratedMessage - implements FilterRequestOrBuilder { - // Use FilterRequest.newBuilder() to construct. - private FilterRequest(Builder builder) { - super(builder); - } - private FilterRequest(boolean noInit) {} - - private static final FilterRequest defaultInstance; - public static FilterRequest getDefaultInstance() { - return defaultInstance; - } - - public FilterRequest getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_FilterRequest_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_FilterRequest_fieldAccessorTable; - } - - private int bitField0_; - // required uint32 bcsapiversion = 1; - public static final int BCSAPIVERSION_FIELD_NUMBER = 1; - private int bcsapiversion_; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - - // required bytes filter = 2; - public static final int FILTER_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString filter_; - public boolean hasFilter() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public com.google.protobuf.ByteString getFilter() { - return filter_; - } - - // required uint32 hashFunctions = 3; - public static final int HASHFUNCTIONS_FIELD_NUMBER = 3; - private int hashFunctions_; - public boolean hasHashFunctions() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public int getHashFunctions() { - return hashFunctions_; - } - - // required uint32 tweak = 4; - public static final int TWEAK_FIELD_NUMBER = 4; - private int tweak_; - public boolean hasTweak() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public int getTweak() { - return tweak_; - } - - // required uint32 mode = 5; - public static final int MODE_FIELD_NUMBER = 5; - private int mode_; - public boolean hasMode() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - public int getMode() { - return mode_; - } - - private void initFields() { - bcsapiversion_ = 0; - filter_ = com.google.protobuf.ByteString.EMPTY; - hashFunctions_ = 0; - tweak_ = 0; - mode_ = 0; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasBcsapiversion()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasFilter()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasHashFunctions()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasTweak()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasMode()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, bcsapiversion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, filter_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeUInt32(3, hashFunctions_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeUInt32(4, tweak_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeUInt32(5, mode_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, bcsapiversion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, filter_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(3, hashFunctions_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(4, tweak_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(5, mode_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequestOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_FilterRequest_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_FilterRequest_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - bcsapiversion_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - filter_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - hashFunctions_ = 0; - bitField0_ = (bitField0_ & ~0x00000004); - tweak_ = 0; - bitField0_ = (bitField0_ & ~0x00000008); - mode_ = 0; - bitField0_ = (bitField0_ & ~0x00000010); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest.getDescriptor(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest getDefaultInstanceForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest.getDefaultInstance(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest build() { - com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest buildPartial() { - com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest result = new com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.bcsapiversion_ = bcsapiversion_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.filter_ = filter_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.hashFunctions_ = hashFunctions_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.tweak_ = tweak_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - result.mode_ = mode_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest) { - return mergeFrom((com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest other) { - if (other == com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest.getDefaultInstance()) return this; - if (other.hasBcsapiversion()) { - setBcsapiversion(other.getBcsapiversion()); - } - if (other.hasFilter()) { - setFilter(other.getFilter()); - } - if (other.hasHashFunctions()) { - setHashFunctions(other.getHashFunctions()); - } - if (other.hasTweak()) { - setTweak(other.getTweak()); - } - if (other.hasMode()) { - setMode(other.getMode()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasBcsapiversion()) { - - return false; - } - if (!hasFilter()) { - - return false; - } - if (!hasHashFunctions()) { - - return false; - } - if (!hasTweak()) { - - return false; - } - if (!hasMode()) { - - return false; - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - bcsapiversion_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - filter_ = input.readBytes(); - break; - } - case 24: { - bitField0_ |= 0x00000004; - hashFunctions_ = input.readUInt32(); - break; - } - case 32: { - bitField0_ |= 0x00000008; - tweak_ = input.readUInt32(); - break; - } - case 40: { - bitField0_ |= 0x00000010; - mode_ = input.readUInt32(); - break; - } - } - } - } - - private int bitField0_; - - // required uint32 bcsapiversion = 1; - private int bcsapiversion_ ; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - public Builder setBcsapiversion(int value) { - bitField0_ |= 0x00000001; - bcsapiversion_ = value; - onChanged(); - return this; - } - public Builder clearBcsapiversion() { - bitField0_ = (bitField0_ & ~0x00000001); - bcsapiversion_ = 0; - onChanged(); - return this; - } - - // required bytes filter = 2; - private com.google.protobuf.ByteString filter_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasFilter() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public com.google.protobuf.ByteString getFilter() { - return filter_; - } - public Builder setFilter(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - filter_ = value; - onChanged(); - return this; - } - public Builder clearFilter() { - bitField0_ = (bitField0_ & ~0x00000002); - filter_ = getDefaultInstance().getFilter(); - onChanged(); - return this; - } - - // required uint32 hashFunctions = 3; - private int hashFunctions_ ; - public boolean hasHashFunctions() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public int getHashFunctions() { - return hashFunctions_; - } - public Builder setHashFunctions(int value) { - bitField0_ |= 0x00000004; - hashFunctions_ = value; - onChanged(); - return this; - } - public Builder clearHashFunctions() { - bitField0_ = (bitField0_ & ~0x00000004); - hashFunctions_ = 0; - onChanged(); - return this; - } - - // required uint32 tweak = 4; - private int tweak_ ; - public boolean hasTweak() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public int getTweak() { - return tweak_; - } - public Builder setTweak(int value) { - bitField0_ |= 0x00000008; - tweak_ = value; - onChanged(); - return this; - } - public Builder clearTweak() { - bitField0_ = (bitField0_ & ~0x00000008); - tweak_ = 0; - onChanged(); - return this; - } - - // required uint32 mode = 5; - private int mode_ ; - public boolean hasMode() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - public int getMode() { - return mode_; - } - public Builder setMode(int value) { - bitField0_ |= 0x00000010; - mode_ = value; - onChanged(); - return this; - } - public Builder clearMode() { - bitField0_ = (bitField0_ & ~0x00000010); - mode_ = 0; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.FilterRequest) - } - - static { - defaultInstance = new FilterRequest(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.api.FilterRequest) - } - - public interface ExactMatchRequestOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required uint32 bcsapiversion = 1; - boolean hasBcsapiversion(); - int getBcsapiversion(); - - // required uint32 mode = 2; - boolean hasMode(); - int getMode(); - - // repeated bytes match = 3; - java.util.List getMatchList(); - int getMatchCount(); - com.google.protobuf.ByteString getMatch(int index); - - // optional uint64 after = 4; - boolean hasAfter(); - long getAfter(); - } - public static final class ExactMatchRequest extends - com.google.protobuf.GeneratedMessage - implements ExactMatchRequestOrBuilder { - // Use ExactMatchRequest.newBuilder() to construct. - private ExactMatchRequest(Builder builder) { - super(builder); - } - private ExactMatchRequest(boolean noInit) {} - - private static final ExactMatchRequest defaultInstance; - public static ExactMatchRequest getDefaultInstance() { - return defaultInstance; - } - - public ExactMatchRequest getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_ExactMatchRequest_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_ExactMatchRequest_fieldAccessorTable; - } - - private int bitField0_; - // required uint32 bcsapiversion = 1; - public static final int BCSAPIVERSION_FIELD_NUMBER = 1; - private int bcsapiversion_; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - - // required uint32 mode = 2; - public static final int MODE_FIELD_NUMBER = 2; - private int mode_; - public boolean hasMode() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public int getMode() { - return mode_; - } - - // repeated bytes match = 3; - public static final int MATCH_FIELD_NUMBER = 3; - private java.util.List match_; - public java.util.List - getMatchList() { - return match_; - } - public int getMatchCount() { - return match_.size(); - } - public com.google.protobuf.ByteString getMatch(int index) { - return match_.get(index); - } - - // optional uint64 after = 4; - public static final int AFTER_FIELD_NUMBER = 4; - private long after_; - public boolean hasAfter() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public long getAfter() { - return after_; - } - - private void initFields() { - bcsapiversion_ = 0; - mode_ = 0; - match_ = java.util.Collections.emptyList();; - after_ = 0L; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasBcsapiversion()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasMode()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, bcsapiversion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt32(2, mode_); - } - for (int i = 0; i < match_.size(); i++) { - output.writeBytes(3, match_.get(i)); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeUInt64(4, after_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, bcsapiversion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(2, mode_); - } - { - int dataSize = 0; - for (int i = 0; i < match_.size(); i++) { - dataSize += com.google.protobuf.CodedOutputStream - .computeBytesSizeNoTag(match_.get(i)); - } - size += dataSize; - size += 1 * getMatchList().size(); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(4, after_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequestOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_ExactMatchRequest_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_ExactMatchRequest_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - bcsapiversion_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - mode_ = 0; - bitField0_ = (bitField0_ & ~0x00000002); - match_ = java.util.Collections.emptyList();; - bitField0_ = (bitField0_ & ~0x00000004); - after_ = 0L; - bitField0_ = (bitField0_ & ~0x00000008); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest.getDescriptor(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest getDefaultInstanceForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest.getDefaultInstance(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest build() { - com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest buildPartial() { - com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest result = new com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.bcsapiversion_ = bcsapiversion_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.mode_ = mode_; - if (((bitField0_ & 0x00000004) == 0x00000004)) { - match_ = java.util.Collections.unmodifiableList(match_); - bitField0_ = (bitField0_ & ~0x00000004); - } - result.match_ = match_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000004; - } - result.after_ = after_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest) { - return mergeFrom((com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest other) { - if (other == com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest.getDefaultInstance()) return this; - if (other.hasBcsapiversion()) { - setBcsapiversion(other.getBcsapiversion()); - } - if (other.hasMode()) { - setMode(other.getMode()); - } - if (!other.match_.isEmpty()) { - if (match_.isEmpty()) { - match_ = other.match_; - bitField0_ = (bitField0_ & ~0x00000004); - } else { - ensureMatchIsMutable(); - match_.addAll(other.match_); - } - onChanged(); - } - if (other.hasAfter()) { - setAfter(other.getAfter()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasBcsapiversion()) { - - return false; - } - if (!hasMode()) { - - return false; - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - bcsapiversion_ = input.readUInt32(); - break; - } - case 16: { - bitField0_ |= 0x00000002; - mode_ = input.readUInt32(); - break; - } - case 26: { - ensureMatchIsMutable(); - match_.add(input.readBytes()); - break; - } - case 32: { - bitField0_ |= 0x00000008; - after_ = input.readUInt64(); - break; - } - } - } - } - - private int bitField0_; - - // required uint32 bcsapiversion = 1; - private int bcsapiversion_ ; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - public Builder setBcsapiversion(int value) { - bitField0_ |= 0x00000001; - bcsapiversion_ = value; - onChanged(); - return this; - } - public Builder clearBcsapiversion() { - bitField0_ = (bitField0_ & ~0x00000001); - bcsapiversion_ = 0; - onChanged(); - return this; - } - - // required uint32 mode = 2; - private int mode_ ; - public boolean hasMode() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public int getMode() { - return mode_; - } - public Builder setMode(int value) { - bitField0_ |= 0x00000002; - mode_ = value; - onChanged(); - return this; - } - public Builder clearMode() { - bitField0_ = (bitField0_ & ~0x00000002); - mode_ = 0; - onChanged(); - return this; - } - - // repeated bytes match = 3; - private java.util.List match_ = java.util.Collections.emptyList();; - private void ensureMatchIsMutable() { - if (!((bitField0_ & 0x00000004) == 0x00000004)) { - match_ = new java.util.ArrayList(match_); - bitField0_ |= 0x00000004; - } - } - public java.util.List - getMatchList() { - return java.util.Collections.unmodifiableList(match_); - } - public int getMatchCount() { - return match_.size(); - } - public com.google.protobuf.ByteString getMatch(int index) { - return match_.get(index); - } - public Builder setMatch( - int index, com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureMatchIsMutable(); - match_.set(index, value); - onChanged(); - return this; - } - public Builder addMatch(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureMatchIsMutable(); - match_.add(value); - onChanged(); - return this; - } - public Builder addAllMatch( - java.lang.Iterable values) { - ensureMatchIsMutable(); - super.addAll(values, match_); - onChanged(); - return this; - } - public Builder clearMatch() { - match_ = java.util.Collections.emptyList();; - bitField0_ = (bitField0_ & ~0x00000004); - onChanged(); - return this; - } - - // optional uint64 after = 4; - private long after_ ; - public boolean hasAfter() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public long getAfter() { - return after_; - } - public Builder setAfter(long value) { - bitField0_ |= 0x00000008; - after_ = value; - onChanged(); - return this; - } - public Builder clearAfter() { - bitField0_ = (bitField0_ & ~0x00000008); - after_ = 0L; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.ExactMatchRequest) - } - - static { - defaultInstance = new ExactMatchRequest(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.api.ExactMatchRequest) - } - - public interface AccountRequestOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required uint32 bcsapiversion = 1; - boolean hasBcsapiversion(); - int getBcsapiversion(); - - // required string publicKey = 2; - boolean hasPublicKey(); - String getPublicKey(); - - // required uint32 lookAhead = 3; - boolean hasLookAhead(); - int getLookAhead(); - - // optional uint64 after = 4; - boolean hasAfter(); - long getAfter(); - } - public static final class AccountRequest extends - com.google.protobuf.GeneratedMessage - implements AccountRequestOrBuilder { - // Use AccountRequest.newBuilder() to construct. - private AccountRequest(Builder builder) { - super(builder); - } - private AccountRequest(boolean noInit) {} - - private static final AccountRequest defaultInstance; - public static AccountRequest getDefaultInstance() { - return defaultInstance; - } - - public AccountRequest getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_AccountRequest_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_AccountRequest_fieldAccessorTable; - } - - private int bitField0_; - // required uint32 bcsapiversion = 1; - public static final int BCSAPIVERSION_FIELD_NUMBER = 1; - private int bcsapiversion_; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - - // required string publicKey = 2; - public static final int PUBLICKEY_FIELD_NUMBER = 2; - private java.lang.Object publicKey_; - public boolean hasPublicKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public String getPublicKey() { - java.lang.Object ref = publicKey_; - if (ref instanceof String) { - return (String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - String s = bs.toStringUtf8(); - if (com.google.protobuf.Internal.isValidUtf8(bs)) { - publicKey_ = s; - } - return s; - } - } - private com.google.protobuf.ByteString getPublicKeyBytes() { - java.lang.Object ref = publicKey_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8((String) ref); - publicKey_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // required uint32 lookAhead = 3; - public static final int LOOKAHEAD_FIELD_NUMBER = 3; - private int lookAhead_; - public boolean hasLookAhead() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public int getLookAhead() { - return lookAhead_; - } - - // optional uint64 after = 4; - public static final int AFTER_FIELD_NUMBER = 4; - private long after_; - public boolean hasAfter() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public long getAfter() { - return after_; - } - - private void initFields() { - bcsapiversion_ = 0; - publicKey_ = ""; - lookAhead_ = 0; - after_ = 0L; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasBcsapiversion()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasPublicKey()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasLookAhead()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, bcsapiversion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getPublicKeyBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeUInt32(3, lookAhead_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeUInt64(4, after_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, bcsapiversion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getPublicKeyBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(3, lookAhead_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(4, after_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequestOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_AccountRequest_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_AccountRequest_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - bcsapiversion_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - publicKey_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - lookAhead_ = 0; - bitField0_ = (bitField0_ & ~0x00000004); - after_ = 0L; - bitField0_ = (bitField0_ & ~0x00000008); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest.getDescriptor(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest getDefaultInstanceForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest.getDefaultInstance(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest build() { - com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest buildPartial() { - com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest result = new com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.bcsapiversion_ = bcsapiversion_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.publicKey_ = publicKey_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.lookAhead_ = lookAhead_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.after_ = after_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest) { - return mergeFrom((com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest other) { - if (other == com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest.getDefaultInstance()) return this; - if (other.hasBcsapiversion()) { - setBcsapiversion(other.getBcsapiversion()); - } - if (other.hasPublicKey()) { - setPublicKey(other.getPublicKey()); - } - if (other.hasLookAhead()) { - setLookAhead(other.getLookAhead()); - } - if (other.hasAfter()) { - setAfter(other.getAfter()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasBcsapiversion()) { - - return false; - } - if (!hasPublicKey()) { - - return false; - } - if (!hasLookAhead()) { - - return false; - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - bcsapiversion_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - publicKey_ = input.readBytes(); - break; - } - case 24: { - bitField0_ |= 0x00000004; - lookAhead_ = input.readUInt32(); - break; - } - case 32: { - bitField0_ |= 0x00000008; - after_ = input.readUInt64(); - break; - } - } - } - } - - private int bitField0_; - - // required uint32 bcsapiversion = 1; - private int bcsapiversion_ ; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - public Builder setBcsapiversion(int value) { - bitField0_ |= 0x00000001; - bcsapiversion_ = value; - onChanged(); - return this; - } - public Builder clearBcsapiversion() { - bitField0_ = (bitField0_ & ~0x00000001); - bcsapiversion_ = 0; - onChanged(); - return this; - } - - // required string publicKey = 2; - private java.lang.Object publicKey_ = ""; - public boolean hasPublicKey() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public String getPublicKey() { - java.lang.Object ref = publicKey_; - if (!(ref instanceof String)) { - String s = ((com.google.protobuf.ByteString) ref).toStringUtf8(); - publicKey_ = s; - return s; - } else { - return (String) ref; - } - } - public Builder setPublicKey(String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - publicKey_ = value; - onChanged(); - return this; - } - public Builder clearPublicKey() { - bitField0_ = (bitField0_ & ~0x00000002); - publicKey_ = getDefaultInstance().getPublicKey(); - onChanged(); - return this; - } - void setPublicKey(com.google.protobuf.ByteString value) { - bitField0_ |= 0x00000002; - publicKey_ = value; - onChanged(); - } - - // required uint32 lookAhead = 3; - private int lookAhead_ ; - public boolean hasLookAhead() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public int getLookAhead() { - return lookAhead_; - } - public Builder setLookAhead(int value) { - bitField0_ |= 0x00000004; - lookAhead_ = value; - onChanged(); - return this; - } - public Builder clearLookAhead() { - bitField0_ = (bitField0_ & ~0x00000004); - lookAhead_ = 0; - onChanged(); - return this; - } - - // optional uint64 after = 4; - private long after_ ; - public boolean hasAfter() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public long getAfter() { - return after_; - } - public Builder setAfter(long value) { - bitField0_ |= 0x00000008; - after_ = value; - onChanged(); - return this; - } - public Builder clearAfter() { - bitField0_ = (bitField0_ & ~0x00000008); - after_ = 0L; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.AccountRequest) - } - - static { - defaultInstance = new AccountRequest(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.api.AccountRequest) - } - - public interface WalletOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required uint32 bcsapiversion = 1; - boolean hasBcsapiversion(); - int getBcsapiversion(); - - // required bytes encryptedSeed = 2; - boolean hasEncryptedSeed(); - com.google.protobuf.ByteString getEncryptedSeed(); - - // required bytes signature = 3; - boolean hasSignature(); - com.google.protobuf.ByteString getSignature(); - - // repeated .com.bitsofproof.supernode.api.Wallet.Account accounts = 4; - java.util.List - getAccountsList(); - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account getAccounts(int index); - int getAccountsCount(); - java.util.List - getAccountsOrBuilderList(); - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.AccountOrBuilder getAccountsOrBuilder( - int index); - } - public static final class Wallet extends - com.google.protobuf.GeneratedMessage - implements WalletOrBuilder { - // Use Wallet.newBuilder() to construct. - private Wallet(Builder builder) { - super(builder); - } - private Wallet(boolean noInit) {} - - private static final Wallet defaultInstance; - public static Wallet getDefaultInstance() { - return defaultInstance; - } - - public Wallet getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_fieldAccessorTable; - } - - public interface AccountOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required string publicKey = 1; - boolean hasPublicKey(); - String getPublicKey(); - - // required uint64 created = 2; - boolean hasCreated(); - long getCreated(); - - // required string name = 3; - boolean hasName(); - String getName(); - } - public static final class Account extends - com.google.protobuf.GeneratedMessage - implements AccountOrBuilder { - // Use Account.newBuilder() to construct. - private Account(Builder builder) { - super(builder); - } - private Account(boolean noInit) {} - - private static final Account defaultInstance; - public static Account getDefaultInstance() { - return defaultInstance; - } - - public Account getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_Account_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_Account_fieldAccessorTable; - } - - private int bitField0_; - // required string publicKey = 1; - public static final int PUBLICKEY_FIELD_NUMBER = 1; - private java.lang.Object publicKey_; - public boolean hasPublicKey() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public String getPublicKey() { - java.lang.Object ref = publicKey_; - if (ref instanceof String) { - return (String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - String s = bs.toStringUtf8(); - if (com.google.protobuf.Internal.isValidUtf8(bs)) { - publicKey_ = s; - } - return s; - } - } - private com.google.protobuf.ByteString getPublicKeyBytes() { - java.lang.Object ref = publicKey_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8((String) ref); - publicKey_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // required uint64 created = 2; - public static final int CREATED_FIELD_NUMBER = 2; - private long created_; - public boolean hasCreated() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public long getCreated() { - return created_; - } - - // required string name = 3; - public static final int NAME_FIELD_NUMBER = 3; - private java.lang.Object name_; - public boolean hasName() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public String getName() { - java.lang.Object ref = name_; - if (ref instanceof String) { - return (String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - String s = bs.toStringUtf8(); - if (com.google.protobuf.Internal.isValidUtf8(bs)) { - name_ = s; - } - return s; - } - } - private com.google.protobuf.ByteString getNameBytes() { - java.lang.Object ref = name_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8((String) ref); - name_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - private void initFields() { - publicKey_ = ""; - created_ = 0L; - name_ = ""; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasPublicKey()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasCreated()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasName()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeBytes(1, getPublicKeyBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt64(2, created_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, getNameBytes()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(1, getPublicKeyBytes()); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(2, created_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, getNameBytes()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.AccountOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_Account_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_Account_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - publicKey_ = ""; - bitField0_ = (bitField0_ & ~0x00000001); - created_ = 0L; - bitField0_ = (bitField0_ & ~0x00000002); - name_ = ""; - bitField0_ = (bitField0_ & ~0x00000004); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.getDescriptor(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account getDefaultInstanceForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.getDefaultInstance(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account build() { - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account buildPartial() { - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account result = new com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.publicKey_ = publicKey_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.created_ = created_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.name_ = name_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account) { - return mergeFrom((com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account other) { - if (other == com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.getDefaultInstance()) return this; - if (other.hasPublicKey()) { - setPublicKey(other.getPublicKey()); - } - if (other.hasCreated()) { - setCreated(other.getCreated()); - } - if (other.hasName()) { - setName(other.getName()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasPublicKey()) { - - return false; - } - if (!hasCreated()) { - - return false; - } - if (!hasName()) { - - return false; - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 10: { - bitField0_ |= 0x00000001; - publicKey_ = input.readBytes(); - break; - } - case 16: { - bitField0_ |= 0x00000002; - created_ = input.readUInt64(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - name_ = input.readBytes(); - break; - } - } - } - } - - private int bitField0_; - - // required string publicKey = 1; - private java.lang.Object publicKey_ = ""; - public boolean hasPublicKey() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public String getPublicKey() { - java.lang.Object ref = publicKey_; - if (!(ref instanceof String)) { - String s = ((com.google.protobuf.ByteString) ref).toStringUtf8(); - publicKey_ = s; - return s; - } else { - return (String) ref; - } - } - public Builder setPublicKey(String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000001; - publicKey_ = value; - onChanged(); - return this; - } - public Builder clearPublicKey() { - bitField0_ = (bitField0_ & ~0x00000001); - publicKey_ = getDefaultInstance().getPublicKey(); - onChanged(); - return this; - } - void setPublicKey(com.google.protobuf.ByteString value) { - bitField0_ |= 0x00000001; - publicKey_ = value; - onChanged(); - } - - // required uint64 created = 2; - private long created_ ; - public boolean hasCreated() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public long getCreated() { - return created_; - } - public Builder setCreated(long value) { - bitField0_ |= 0x00000002; - created_ = value; - onChanged(); - return this; - } - public Builder clearCreated() { - bitField0_ = (bitField0_ & ~0x00000002); - created_ = 0L; - onChanged(); - return this; - } - - // required string name = 3; - private java.lang.Object name_ = ""; - public boolean hasName() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public String getName() { - java.lang.Object ref = name_; - if (!(ref instanceof String)) { - String s = ((com.google.protobuf.ByteString) ref).toStringUtf8(); - name_ = s; - return s; - } else { - return (String) ref; - } - } - public Builder setName(String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - name_ = value; - onChanged(); - return this; - } - public Builder clearName() { - bitField0_ = (bitField0_ & ~0x00000004); - name_ = getDefaultInstance().getName(); - onChanged(); - return this; - } - void setName(com.google.protobuf.ByteString value) { - bitField0_ |= 0x00000004; - name_ = value; - onChanged(); - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.Wallet.Account) - } - - static { - defaultInstance = new Account(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.api.Wallet.Account) - } - - private int bitField0_; - // required uint32 bcsapiversion = 1; - public static final int BCSAPIVERSION_FIELD_NUMBER = 1; - private int bcsapiversion_; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - - // required bytes encryptedSeed = 2; - public static final int ENCRYPTEDSEED_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString encryptedSeed_; - public boolean hasEncryptedSeed() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public com.google.protobuf.ByteString getEncryptedSeed() { - return encryptedSeed_; - } - - // required bytes signature = 3; - public static final int SIGNATURE_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString signature_; - public boolean hasSignature() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public com.google.protobuf.ByteString getSignature() { - return signature_; - } - - // repeated .com.bitsofproof.supernode.api.Wallet.Account accounts = 4; - public static final int ACCOUNTS_FIELD_NUMBER = 4; - private java.util.List accounts_; - public java.util.List getAccountsList() { - return accounts_; - } - public java.util.List - getAccountsOrBuilderList() { - return accounts_; - } - public int getAccountsCount() { - return accounts_.size(); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account getAccounts(int index) { - return accounts_.get(index); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.AccountOrBuilder getAccountsOrBuilder( - int index) { - return accounts_.get(index); - } - - private void initFields() { - bcsapiversion_ = 0; - encryptedSeed_ = com.google.protobuf.ByteString.EMPTY; - signature_ = com.google.protobuf.ByteString.EMPTY; - accounts_ = java.util.Collections.emptyList(); - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasBcsapiversion()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasEncryptedSeed()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasSignature()) { - memoizedIsInitialized = 0; - return false; - } - for (int i = 0; i < getAccountsCount(); i++) { - if (!getAccounts(i).isInitialized()) { - memoizedIsInitialized = 0; - return false; - } - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, bcsapiversion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, encryptedSeed_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, signature_); - } - for (int i = 0; i < accounts_.size(); i++) { - output.writeMessage(4, accounts_.get(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, bcsapiversion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, encryptedSeed_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, signature_); - } - for (int i = 0; i < accounts_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(4, accounts_.get(i)); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.api.BCSAPIMessage.Wallet parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.api.BCSAPIMessage.Wallet prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.api.BCSAPIMessage.WalletOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.api.BCSAPIMessage.internal_static_com_bitsofproof_supernode_api_Wallet_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getAccountsFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - bcsapiversion_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - encryptedSeed_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - signature_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - if (accountsBuilder_ == null) { - accounts_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); - } else { - accountsBuilder_.clear(); - } - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.getDescriptor(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet getDefaultInstanceForType() { - return com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.getDefaultInstance(); - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet build() { - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.api.BCSAPIMessage.Wallet buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet buildPartial() { - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet result = new com.bitsofproof.supernode.api.BCSAPIMessage.Wallet(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.bcsapiversion_ = bcsapiversion_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.encryptedSeed_ = encryptedSeed_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.signature_ = signature_; - if (accountsBuilder_ == null) { - if (((bitField0_ & 0x00000008) == 0x00000008)) { - accounts_ = java.util.Collections.unmodifiableList(accounts_); - bitField0_ = (bitField0_ & ~0x00000008); - } - result.accounts_ = accounts_; - } else { - result.accounts_ = accountsBuilder_.build(); - } - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.api.BCSAPIMessage.Wallet) { - return mergeFrom((com.bitsofproof.supernode.api.BCSAPIMessage.Wallet)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.api.BCSAPIMessage.Wallet other) { - if (other == com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.getDefaultInstance()) return this; - if (other.hasBcsapiversion()) { - setBcsapiversion(other.getBcsapiversion()); - } - if (other.hasEncryptedSeed()) { - setEncryptedSeed(other.getEncryptedSeed()); - } - if (other.hasSignature()) { - setSignature(other.getSignature()); - } - if (accountsBuilder_ == null) { - if (!other.accounts_.isEmpty()) { - if (accounts_.isEmpty()) { - accounts_ = other.accounts_; - bitField0_ = (bitField0_ & ~0x00000008); - } else { - ensureAccountsIsMutable(); - accounts_.addAll(other.accounts_); - } - onChanged(); - } - } else { - if (!other.accounts_.isEmpty()) { - if (accountsBuilder_.isEmpty()) { - accountsBuilder_.dispose(); - accountsBuilder_ = null; - accounts_ = other.accounts_; - bitField0_ = (bitField0_ & ~0x00000008); - accountsBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getAccountsFieldBuilder() : null; - } else { - accountsBuilder_.addAllMessages(other.accounts_); - } - } - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasBcsapiversion()) { - - return false; - } - if (!hasEncryptedSeed()) { - - return false; - } - if (!hasSignature()) { - - return false; - } - for (int i = 0; i < getAccountsCount(); i++) { - if (!getAccounts(i).isInitialized()) { - - return false; - } - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - bcsapiversion_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - encryptedSeed_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - signature_ = input.readBytes(); - break; - } - case 34: { - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder subBuilder = com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.newBuilder(); - input.readMessage(subBuilder, extensionRegistry); - addAccounts(subBuilder.buildPartial()); - break; - } - } - } - } - - private int bitField0_; - - // required uint32 bcsapiversion = 1; - private int bcsapiversion_ ; - public boolean hasBcsapiversion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getBcsapiversion() { - return bcsapiversion_; - } - public Builder setBcsapiversion(int value) { - bitField0_ |= 0x00000001; - bcsapiversion_ = value; - onChanged(); - return this; - } - public Builder clearBcsapiversion() { - bitField0_ = (bitField0_ & ~0x00000001); - bcsapiversion_ = 0; - onChanged(); - return this; - } - - // required bytes encryptedSeed = 2; - private com.google.protobuf.ByteString encryptedSeed_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasEncryptedSeed() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public com.google.protobuf.ByteString getEncryptedSeed() { - return encryptedSeed_; - } - public Builder setEncryptedSeed(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - encryptedSeed_ = value; - onChanged(); - return this; - } - public Builder clearEncryptedSeed() { - bitField0_ = (bitField0_ & ~0x00000002); - encryptedSeed_ = getDefaultInstance().getEncryptedSeed(); - onChanged(); - return this; - } - - // required bytes signature = 3; - private com.google.protobuf.ByteString signature_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasSignature() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public com.google.protobuf.ByteString getSignature() { - return signature_; - } - public Builder setSignature(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - signature_ = value; - onChanged(); - return this; - } - public Builder clearSignature() { - bitField0_ = (bitField0_ & ~0x00000004); - signature_ = getDefaultInstance().getSignature(); - onChanged(); - return this; - } - - // repeated .com.bitsofproof.supernode.api.Wallet.Account accounts = 4; - private java.util.List accounts_ = - java.util.Collections.emptyList(); - private void ensureAccountsIsMutable() { - if (!((bitField0_ & 0x00000008) == 0x00000008)) { - accounts_ = new java.util.ArrayList(accounts_); - bitField0_ |= 0x00000008; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.AccountOrBuilder> accountsBuilder_; - - public java.util.List getAccountsList() { - if (accountsBuilder_ == null) { - return java.util.Collections.unmodifiableList(accounts_); - } else { - return accountsBuilder_.getMessageList(); - } - } - public int getAccountsCount() { - if (accountsBuilder_ == null) { - return accounts_.size(); - } else { - return accountsBuilder_.getCount(); - } - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account getAccounts(int index) { - if (accountsBuilder_ == null) { - return accounts_.get(index); - } else { - return accountsBuilder_.getMessage(index); - } - } - public Builder setAccounts( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account value) { - if (accountsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureAccountsIsMutable(); - accounts_.set(index, value); - onChanged(); - } else { - accountsBuilder_.setMessage(index, value); - } - return this; - } - public Builder setAccounts( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder builderForValue) { - if (accountsBuilder_ == null) { - ensureAccountsIsMutable(); - accounts_.set(index, builderForValue.build()); - onChanged(); - } else { - accountsBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - public Builder addAccounts(com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account value) { - if (accountsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureAccountsIsMutable(); - accounts_.add(value); - onChanged(); - } else { - accountsBuilder_.addMessage(value); - } - return this; - } - public Builder addAccounts( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account value) { - if (accountsBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureAccountsIsMutable(); - accounts_.add(index, value); - onChanged(); - } else { - accountsBuilder_.addMessage(index, value); - } - return this; - } - public Builder addAccounts( - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder builderForValue) { - if (accountsBuilder_ == null) { - ensureAccountsIsMutable(); - accounts_.add(builderForValue.build()); - onChanged(); - } else { - accountsBuilder_.addMessage(builderForValue.build()); - } - return this; - } - public Builder addAccounts( - int index, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder builderForValue) { - if (accountsBuilder_ == null) { - ensureAccountsIsMutable(); - accounts_.add(index, builderForValue.build()); - onChanged(); - } else { - accountsBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - public Builder addAllAccounts( - java.lang.Iterable values) { - if (accountsBuilder_ == null) { - ensureAccountsIsMutable(); - super.addAll(values, accounts_); - onChanged(); - } else { - accountsBuilder_.addAllMessages(values); - } - return this; - } - public Builder clearAccounts() { - if (accountsBuilder_ == null) { - accounts_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000008); - onChanged(); - } else { - accountsBuilder_.clear(); - } - return this; - } - public Builder removeAccounts(int index) { - if (accountsBuilder_ == null) { - ensureAccountsIsMutable(); - accounts_.remove(index); - onChanged(); - } else { - accountsBuilder_.remove(index); - } - return this; - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder getAccountsBuilder( - int index) { - return getAccountsFieldBuilder().getBuilder(index); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.AccountOrBuilder getAccountsOrBuilder( - int index) { - if (accountsBuilder_ == null) { - return accounts_.get(index); } else { - return accountsBuilder_.getMessageOrBuilder(index); - } - } - public java.util.List - getAccountsOrBuilderList() { - if (accountsBuilder_ != null) { - return accountsBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(accounts_); - } - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder addAccountsBuilder() { - return getAccountsFieldBuilder().addBuilder( - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.getDefaultInstance()); - } - public com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder addAccountsBuilder( - int index) { - return getAccountsFieldBuilder().addBuilder( - index, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.getDefaultInstance()); - } - public java.util.List - getAccountsBuilderList() { - return getAccountsFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.AccountOrBuilder> - getAccountsFieldBuilder() { - if (accountsBuilder_ == null) { - accountsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder, com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.AccountOrBuilder>( - accounts_, - ((bitField0_ & 0x00000008) == 0x00000008), - getParentForChildren(), - isClean()); - accounts_ = null; - } - return accountsBuilder_; - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.api.Wallet) - } - - static { - defaultInstance = new Wallet(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.api.Wallet) - } - - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_api_Ping_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_api_Ping_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_api_Alert_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_api_Alert_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_api_TransactionInput_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_api_TransactionInput_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_api_TransactionOutput_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_api_TransactionOutput_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_api_Transaction_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_api_Transaction_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_api_Block_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_api_Block_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_api_TrunkUpdate_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_api_TrunkUpdate_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_api_Hash_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_api_Hash_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_api_ExceptionMessage_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_api_ExceptionMessage_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_api_Color_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_api_Color_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_api_FilterRequest_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_api_FilterRequest_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_api_ExactMatchRequest_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_api_ExactMatchRequest_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_api_AccountRequest_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_api_AccountRequest_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_api_Wallet_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_api_Wallet_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_api_Wallet_Account_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_api_Wallet_Account_fieldAccessorTable; - - public static com.google.protobuf.Descriptors.FileDescriptor - getDescriptor() { - return descriptor; - } - private static com.google.protobuf.Descriptors.FileDescriptor - descriptor; - static { - java.lang.String[] descriptorData = { - "\n!main/protobuf/BCSAPIMessage.proto\022\035com" + - ".bitsofproof.supernode.api\",\n\004Ping\022\025\n\rbc" + - "sapiversion\030\001 \002(\r\022\r\n\005nonce\030\002 \002(\004\"?\n\005Aler" + - "t\022\025\n\rbcsapiversion\030\001 \002(\r\022\r\n\005alert\030\002 \002(\t\022" + - "\020\n\010severity\030\003 \002(\r\"V\n\020TransactionInput\022\016\n" + - "\006source\030\001 \002(\014\022\020\n\010sourceix\030\002 \002(\r\022\020\n\010seque" + - "nce\030\003 \002(\r\022\016\n\006script\030\004 \002(\014\"A\n\021Transaction" + - "Output\022\r\n\005value\030\001 \002(\004\022\016\n\006script\030\002 \002(\014\022\r\n" + - "\005color\030\003 \001(\014\"\377\001\n\013Transaction\022\025\n\rbcsapive" + - "rsion\030\001 \002(\r\022\017\n\007version\030\003 \002(\r\022\020\n\010locktime", - "\030\004 \002(\r\022?\n\006inputs\030\005 \003(\0132/.com.bitsofproof" + - ".supernode.api.TransactionInput\022A\n\007outpu" + - "ts\030\006 \003(\01320.com.bitsofproof.supernode.api" + - ".TransactionOutput\022\r\n\005block\030\007 \001(\014\022\023\n\013dou" + - "bleSpend\030\010 \001(\010\022\016\n\006height\030\t \001(\r\"\342\001\n\005Block" + - "\022\025\n\rbcsapiversion\030\001 \002(\r\022\017\n\007version\030\002 \002(\r" + - "\022\025\n\rpreviousBlock\030\003 \002(\014\022\022\n\nmerkleRoot\030\004 " + - "\002(\014\022\021\n\ttimestamp\030\005 \002(\r\022\022\n\ndifficulty\030\006 \002" + - "(\r\022\r\n\005nonce\030\007 \002(\r\022@\n\014transactions\030\010 \003(\0132" + - "*.com.bitsofproof.supernode.api.Transact", - "ion\022\016\n\006height\030\t \001(\r\"\220\001\n\013TrunkUpdate\022\025\n\rb" + - "csapiversion\030\001 \002(\r\0223\n\005added\030\002 \003(\0132$.com." + - "bitsofproof.supernode.api.Block\0225\n\007remov" + - "ed\030\003 \003(\0132$.com.bitsofproof.supernode.api" + - ".Block\"+\n\004Hash\022\025\n\rbcsapiversion\030\001 \002(\r\022\014\n" + - "\004hash\030\002 \003(\014\":\n\020ExceptionMessage\022\025\n\rbcsap" + - "iversion\030\001 \002(\r\022\017\n\007message\030\002 \003(\t\"r\n\005Color" + - "\022\023\n\013transaction\030\001 \002(\014\022\r\n\005terms\030\002 \002(\t\022\014\n\004" + - "unit\030\003 \002(\004\022\024\n\014expiryHeight\030\004 \002(\r\022\016\n\006pubk" + - "ey\030\005 \002(\014\022\021\n\tsignature\030\006 \002(\014\"j\n\rFilterReq", - "uest\022\025\n\rbcsapiversion\030\001 \002(\r\022\016\n\006filter\030\002 " + - "\002(\014\022\025\n\rhashFunctions\030\003 \002(\r\022\r\n\005tweak\030\004 \002(" + - "\r\022\014\n\004mode\030\005 \002(\r\"V\n\021ExactMatchRequest\022\025\n\r" + - "bcsapiversion\030\001 \002(\r\022\014\n\004mode\030\002 \002(\r\022\r\n\005mat" + - "ch\030\003 \003(\014\022\r\n\005after\030\004 \001(\004\"\\\n\016AccountReques" + - "t\022\025\n\rbcsapiversion\030\001 \002(\r\022\021\n\tpublicKey\030\002 " + - "\002(\t\022\021\n\tlookAhead\030\003 \002(\r\022\r\n\005after\030\004 \001(\004\"\307\001" + - "\n\006Wallet\022\025\n\rbcsapiversion\030\001 \002(\r\022\025\n\rencry" + - "ptedSeed\030\002 \002(\014\022\021\n\tsignature\030\003 \002(\014\022?\n\010acc" + - "ounts\030\004 \003(\0132-.com.bitsofproof.supernode.", - "api.Wallet.Account\032;\n\007Account\022\021\n\tpublicK" + - "ey\030\001 \002(\t\022\017\n\007created\030\002 \002(\004\022\014\n\004name\030\003 \002(\t" - }; - com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = - new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { - public com.google.protobuf.ExtensionRegistry assignDescriptors( - com.google.protobuf.Descriptors.FileDescriptor root) { - descriptor = root; - internal_static_com_bitsofproof_supernode_api_Ping_descriptor = - getDescriptor().getMessageTypes().get(0); - internal_static_com_bitsofproof_supernode_api_Ping_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_api_Ping_descriptor, - new java.lang.String[] { "Bcsapiversion", "Nonce", }, - com.bitsofproof.supernode.api.BCSAPIMessage.Ping.class, - com.bitsofproof.supernode.api.BCSAPIMessage.Ping.Builder.class); - internal_static_com_bitsofproof_supernode_api_Alert_descriptor = - getDescriptor().getMessageTypes().get(1); - internal_static_com_bitsofproof_supernode_api_Alert_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_api_Alert_descriptor, - new java.lang.String[] { "Bcsapiversion", "Alert", "Severity", }, - com.bitsofproof.supernode.api.BCSAPIMessage.Alert.class, - com.bitsofproof.supernode.api.BCSAPIMessage.Alert.Builder.class); - internal_static_com_bitsofproof_supernode_api_TransactionInput_descriptor = - getDescriptor().getMessageTypes().get(2); - internal_static_com_bitsofproof_supernode_api_TransactionInput_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_api_TransactionInput_descriptor, - new java.lang.String[] { "Source", "Sourceix", "Sequence", "Script", }, - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput.class, - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionInput.Builder.class); - internal_static_com_bitsofproof_supernode_api_TransactionOutput_descriptor = - getDescriptor().getMessageTypes().get(3); - internal_static_com_bitsofproof_supernode_api_TransactionOutput_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_api_TransactionOutput_descriptor, - new java.lang.String[] { "Value", "Script", "Color", }, - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput.class, - com.bitsofproof.supernode.api.BCSAPIMessage.TransactionOutput.Builder.class); - internal_static_com_bitsofproof_supernode_api_Transaction_descriptor = - getDescriptor().getMessageTypes().get(4); - internal_static_com_bitsofproof_supernode_api_Transaction_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_api_Transaction_descriptor, - new java.lang.String[] { "Bcsapiversion", "Version", "Locktime", "Inputs", "Outputs", "Block", "DoubleSpend", "Height", }, - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.class, - com.bitsofproof.supernode.api.BCSAPIMessage.Transaction.Builder.class); - internal_static_com_bitsofproof_supernode_api_Block_descriptor = - getDescriptor().getMessageTypes().get(5); - internal_static_com_bitsofproof_supernode_api_Block_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_api_Block_descriptor, - new java.lang.String[] { "Bcsapiversion", "Version", "PreviousBlock", "MerkleRoot", "Timestamp", "Difficulty", "Nonce", "Transactions", "Height", }, - com.bitsofproof.supernode.api.BCSAPIMessage.Block.class, - com.bitsofproof.supernode.api.BCSAPIMessage.Block.Builder.class); - internal_static_com_bitsofproof_supernode_api_TrunkUpdate_descriptor = - getDescriptor().getMessageTypes().get(6); - internal_static_com_bitsofproof_supernode_api_TrunkUpdate_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_api_TrunkUpdate_descriptor, - new java.lang.String[] { "Bcsapiversion", "Added", "Removed", }, - com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate.class, - com.bitsofproof.supernode.api.BCSAPIMessage.TrunkUpdate.Builder.class); - internal_static_com_bitsofproof_supernode_api_Hash_descriptor = - getDescriptor().getMessageTypes().get(7); - internal_static_com_bitsofproof_supernode_api_Hash_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_api_Hash_descriptor, - new java.lang.String[] { "Bcsapiversion", "Hash", }, - com.bitsofproof.supernode.api.BCSAPIMessage.Hash.class, - com.bitsofproof.supernode.api.BCSAPIMessage.Hash.Builder.class); - internal_static_com_bitsofproof_supernode_api_ExceptionMessage_descriptor = - getDescriptor().getMessageTypes().get(8); - internal_static_com_bitsofproof_supernode_api_ExceptionMessage_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_api_ExceptionMessage_descriptor, - new java.lang.String[] { "Bcsapiversion", "Message", }, - com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage.class, - com.bitsofproof.supernode.api.BCSAPIMessage.ExceptionMessage.Builder.class); - internal_static_com_bitsofproof_supernode_api_Color_descriptor = - getDescriptor().getMessageTypes().get(9); - internal_static_com_bitsofproof_supernode_api_Color_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_api_Color_descriptor, - new java.lang.String[] { "Transaction", "Terms", "Unit", "ExpiryHeight", "Pubkey", "Signature", }, - com.bitsofproof.supernode.api.BCSAPIMessage.Color.class, - com.bitsofproof.supernode.api.BCSAPIMessage.Color.Builder.class); - internal_static_com_bitsofproof_supernode_api_FilterRequest_descriptor = - getDescriptor().getMessageTypes().get(10); - internal_static_com_bitsofproof_supernode_api_FilterRequest_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_api_FilterRequest_descriptor, - new java.lang.String[] { "Bcsapiversion", "Filter", "HashFunctions", "Tweak", "Mode", }, - com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest.class, - com.bitsofproof.supernode.api.BCSAPIMessage.FilterRequest.Builder.class); - internal_static_com_bitsofproof_supernode_api_ExactMatchRequest_descriptor = - getDescriptor().getMessageTypes().get(11); - internal_static_com_bitsofproof_supernode_api_ExactMatchRequest_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_api_ExactMatchRequest_descriptor, - new java.lang.String[] { "Bcsapiversion", "Mode", "Match", "After", }, - com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest.class, - com.bitsofproof.supernode.api.BCSAPIMessage.ExactMatchRequest.Builder.class); - internal_static_com_bitsofproof_supernode_api_AccountRequest_descriptor = - getDescriptor().getMessageTypes().get(12); - internal_static_com_bitsofproof_supernode_api_AccountRequest_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_api_AccountRequest_descriptor, - new java.lang.String[] { "Bcsapiversion", "PublicKey", "LookAhead", "After", }, - com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest.class, - com.bitsofproof.supernode.api.BCSAPIMessage.AccountRequest.Builder.class); - internal_static_com_bitsofproof_supernode_api_Wallet_descriptor = - getDescriptor().getMessageTypes().get(13); - internal_static_com_bitsofproof_supernode_api_Wallet_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_api_Wallet_descriptor, - new java.lang.String[] { "Bcsapiversion", "EncryptedSeed", "Signature", "Accounts", }, - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.class, - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Builder.class); - internal_static_com_bitsofproof_supernode_api_Wallet_Account_descriptor = - internal_static_com_bitsofproof_supernode_api_Wallet_descriptor.getNestedTypes().get(0); - internal_static_com_bitsofproof_supernode_api_Wallet_Account_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_api_Wallet_Account_descriptor, - new java.lang.String[] { "PublicKey", "Created", "Name", }, - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.class, - com.bitsofproof.supernode.api.BCSAPIMessage.Wallet.Account.Builder.class); - return null; - } - }; - com.google.protobuf.Descriptors.FileDescriptor - .internalBuildGeneratedFileFrom(descriptorData, - new com.google.protobuf.Descriptors.FileDescriptor[] { - }, assigner); - } - - // @@protoc_insertion_point(outer_class_scope) -} diff --git a/api/src/main/protobuf/BCSAPIMessage.proto b/api/src/main/proto/BCSAPIMessage.proto similarity index 100% rename from api/src/main/protobuf/BCSAPIMessage.proto rename to api/src/main/proto/BCSAPIMessage.proto diff --git a/pom.xml b/pom.xml index e684279f..7d83e93b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.1.3 + 1.1.4-SNAPSHOT supernode The bitsofproof supernode implementation of the Bitcoin protocol pom @@ -58,7 +58,7 @@ 3.6.9.Final 9.1-901.jdbc4 1.6.1 - 2.4.1 + 2.5.0 4.3.0.Final 20090211 1.2 @@ -74,10 +74,19 @@ default + scm:git:git://github.com/bitsofproof/supernode.git https://github.com/bitsofproof/supernode + + + + dtrott + http://maven.davidtrott.com/repository + + + diff --git a/server/pom.xml b/server/pom.xml index d6044758..cc262a80 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.3 + 1.1.4-SNAPSHOT ../ @@ -226,27 +226,21 @@ src/main/assembly/supernode.xml - - maven-antrun-plugin - - - compile-protoc - generate-sources - - - - - - - - - - - run - - - - + + com.google.protobuf.tools + maven-protoc-plugin + + /usr/local/bin/protoc + + + + + compile + testCompile + + + + diff --git a/server/src/main/java/com/bitsofproof/supernode/model/LevelDBStore.java b/server/src/main/java/com/bitsofproof/supernode/model/LevelDBStore.java deleted file mode 100644 index a827e970..00000000 --- a/server/src/main/java/com/bitsofproof/supernode/model/LevelDBStore.java +++ /dev/null @@ -1,6838 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: main/protobuf/LevelDBStore.proto - -package com.bitsofproof.supernode.model; - -public final class LevelDBStore { - private LevelDBStore() {} - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistry registry) { - } - public interface TXOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required uint32 storeVersion = 1; - boolean hasStoreVersion(); - int getStoreVersion(); - - // required bytes blockHash = 2; - boolean hasBlockHash(); - com.google.protobuf.ByteString getBlockHash(); - - // required bytes hash = 3; - boolean hasHash(); - com.google.protobuf.ByteString getHash(); - - // required uint32 version = 4; - boolean hasVersion(); - int getVersion(); - - // required uint32 lockTime = 5; - boolean hasLockTime(); - int getLockTime(); - - // required uint32 ix = 6; - boolean hasIx(); - int getIx(); - - // repeated .com.bitsofproof.supernode.model.TX.TXIN txin = 7; - java.util.List - getTxinList(); - com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN getTxin(int index); - int getTxinCount(); - java.util.List - getTxinOrBuilderList(); - com.bitsofproof.supernode.model.LevelDBStore.TX.TXINOrBuilder getTxinOrBuilder( - int index); - - // repeated .com.bitsofproof.supernode.model.TX.TXOUT txout = 8; - java.util.List - getTxoutList(); - com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT getTxout(int index); - int getTxoutCount(); - java.util.List - getTxoutOrBuilderList(); - com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUTOrBuilder getTxoutOrBuilder( - int index); - } - public static final class TX extends - com.google.protobuf.GeneratedMessage - implements TXOrBuilder { - // Use TX.newBuilder() to construct. - private TX(Builder builder) { - super(builder); - } - private TX(boolean noInit) {} - - private static final TX defaultInstance; - public static TX getDefaultInstance() { - return defaultInstance; - } - - public TX getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_TX_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_TX_fieldAccessorTable; - } - - public interface TXINOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required uint32 ix = 1; - boolean hasIx(); - int getIx(); - - // required bytes sourceHash = 2; - boolean hasSourceHash(); - com.google.protobuf.ByteString getSourceHash(); - - // required bytes script = 3; - boolean hasScript(); - com.google.protobuf.ByteString getScript(); - - // required uint32 sequence = 4; - boolean hasSequence(); - int getSequence(); - } - public static final class TXIN extends - com.google.protobuf.GeneratedMessage - implements TXINOrBuilder { - // Use TXIN.newBuilder() to construct. - private TXIN(Builder builder) { - super(builder); - } - private TXIN(boolean noInit) {} - - private static final TXIN defaultInstance; - public static TXIN getDefaultInstance() { - return defaultInstance; - } - - public TXIN getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_TX_TXIN_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_TX_TXIN_fieldAccessorTable; - } - - private int bitField0_; - // required uint32 ix = 1; - public static final int IX_FIELD_NUMBER = 1; - private int ix_; - public boolean hasIx() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getIx() { - return ix_; - } - - // required bytes sourceHash = 2; - public static final int SOURCEHASH_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString sourceHash_; - public boolean hasSourceHash() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public com.google.protobuf.ByteString getSourceHash() { - return sourceHash_; - } - - // required bytes script = 3; - public static final int SCRIPT_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString script_; - public boolean hasScript() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public com.google.protobuf.ByteString getScript() { - return script_; - } - - // required uint32 sequence = 4; - public static final int SEQUENCE_FIELD_NUMBER = 4; - private int sequence_; - public boolean hasSequence() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public int getSequence() { - return sequence_; - } - - private void initFields() { - ix_ = 0; - sourceHash_ = com.google.protobuf.ByteString.EMPTY; - script_ = com.google.protobuf.ByteString.EMPTY; - sequence_ = 0; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasIx()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasSourceHash()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasScript()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasSequence()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, ix_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, sourceHash_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, script_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeUInt32(4, sequence_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, ix_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, sourceHash_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, script_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(4, sequence_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.model.LevelDBStore.TX.TXINOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_TX_TXIN_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_TX_TXIN_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - ix_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - sourceHash_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - script_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - sequence_ = 0; - bitField0_ = (bitField0_ & ~0x00000008); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN.getDescriptor(); - } - - public com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN getDefaultInstanceForType() { - return com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN.getDefaultInstance(); - } - - public com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN build() { - com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN buildPartial() { - com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN result = new com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.ix_ = ix_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.sourceHash_ = sourceHash_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.script_ = script_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.sequence_ = sequence_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN) { - return mergeFrom((com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN other) { - if (other == com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN.getDefaultInstance()) return this; - if (other.hasIx()) { - setIx(other.getIx()); - } - if (other.hasSourceHash()) { - setSourceHash(other.getSourceHash()); - } - if (other.hasScript()) { - setScript(other.getScript()); - } - if (other.hasSequence()) { - setSequence(other.getSequence()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasIx()) { - - return false; - } - if (!hasSourceHash()) { - - return false; - } - if (!hasScript()) { - - return false; - } - if (!hasSequence()) { - - return false; - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - ix_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - sourceHash_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - script_ = input.readBytes(); - break; - } - case 32: { - bitField0_ |= 0x00000008; - sequence_ = input.readUInt32(); - break; - } - } - } - } - - private int bitField0_; - - // required uint32 ix = 1; - private int ix_ ; - public boolean hasIx() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getIx() { - return ix_; - } - public Builder setIx(int value) { - bitField0_ |= 0x00000001; - ix_ = value; - onChanged(); - return this; - } - public Builder clearIx() { - bitField0_ = (bitField0_ & ~0x00000001); - ix_ = 0; - onChanged(); - return this; - } - - // required bytes sourceHash = 2; - private com.google.protobuf.ByteString sourceHash_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasSourceHash() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public com.google.protobuf.ByteString getSourceHash() { - return sourceHash_; - } - public Builder setSourceHash(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - sourceHash_ = value; - onChanged(); - return this; - } - public Builder clearSourceHash() { - bitField0_ = (bitField0_ & ~0x00000002); - sourceHash_ = getDefaultInstance().getSourceHash(); - onChanged(); - return this; - } - - // required bytes script = 3; - private com.google.protobuf.ByteString script_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasScript() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public com.google.protobuf.ByteString getScript() { - return script_; - } - public Builder setScript(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - script_ = value; - onChanged(); - return this; - } - public Builder clearScript() { - bitField0_ = (bitField0_ & ~0x00000004); - script_ = getDefaultInstance().getScript(); - onChanged(); - return this; - } - - // required uint32 sequence = 4; - private int sequence_ ; - public boolean hasSequence() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public int getSequence() { - return sequence_; - } - public Builder setSequence(int value) { - bitField0_ |= 0x00000008; - sequence_ = value; - onChanged(); - return this; - } - public Builder clearSequence() { - bitField0_ = (bitField0_ & ~0x00000008); - sequence_ = 0; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.model.TX.TXIN) - } - - static { - defaultInstance = new TXIN(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.model.TX.TXIN) - } - - public interface TXOUTOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required uint64 value = 1; - boolean hasValue(); - long getValue(); - - // required bytes script = 2; - boolean hasScript(); - com.google.protobuf.ByteString getScript(); - - // required uint32 height = 3; - boolean hasHeight(); - int getHeight(); - - // required bool coinbase = 4; - boolean hasCoinbase(); - boolean getCoinbase(); - - // required bool available = 5; - boolean hasAvailable(); - boolean getAvailable(); - - // optional string color = 6; - boolean hasColor(); - String getColor(); - } - public static final class TXOUT extends - com.google.protobuf.GeneratedMessage - implements TXOUTOrBuilder { - // Use TXOUT.newBuilder() to construct. - private TXOUT(Builder builder) { - super(builder); - } - private TXOUT(boolean noInit) {} - - private static final TXOUT defaultInstance; - public static TXOUT getDefaultInstance() { - return defaultInstance; - } - - public TXOUT getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_TX_TXOUT_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_TX_TXOUT_fieldAccessorTable; - } - - private int bitField0_; - // required uint64 value = 1; - public static final int VALUE_FIELD_NUMBER = 1; - private long value_; - public boolean hasValue() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public long getValue() { - return value_; - } - - // required bytes script = 2; - public static final int SCRIPT_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString script_; - public boolean hasScript() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public com.google.protobuf.ByteString getScript() { - return script_; - } - - // required uint32 height = 3; - public static final int HEIGHT_FIELD_NUMBER = 3; - private int height_; - public boolean hasHeight() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public int getHeight() { - return height_; - } - - // required bool coinbase = 4; - public static final int COINBASE_FIELD_NUMBER = 4; - private boolean coinbase_; - public boolean hasCoinbase() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public boolean getCoinbase() { - return coinbase_; - } - - // required bool available = 5; - public static final int AVAILABLE_FIELD_NUMBER = 5; - private boolean available_; - public boolean hasAvailable() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - public boolean getAvailable() { - return available_; - } - - // optional string color = 6; - public static final int COLOR_FIELD_NUMBER = 6; - private java.lang.Object color_; - public boolean hasColor() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - public String getColor() { - java.lang.Object ref = color_; - if (ref instanceof String) { - return (String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - String s = bs.toStringUtf8(); - if (com.google.protobuf.Internal.isValidUtf8(bs)) { - color_ = s; - } - return s; - } - } - private com.google.protobuf.ByteString getColorBytes() { - java.lang.Object ref = color_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8((String) ref); - color_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - private void initFields() { - value_ = 0L; - script_ = com.google.protobuf.ByteString.EMPTY; - height_ = 0; - coinbase_ = false; - available_ = false; - color_ = ""; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasValue()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasScript()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasHeight()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasCoinbase()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasAvailable()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt64(1, value_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, script_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeUInt32(3, height_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBool(4, coinbase_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeBool(5, available_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeBytes(6, getColorBytes()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(1, value_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, script_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(3, height_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBoolSize(4, coinbase_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeBoolSize(5, available_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(6, getColorBytes()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUTOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_TX_TXOUT_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_TX_TXOUT_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - value_ = 0L; - bitField0_ = (bitField0_ & ~0x00000001); - script_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - height_ = 0; - bitField0_ = (bitField0_ & ~0x00000004); - coinbase_ = false; - bitField0_ = (bitField0_ & ~0x00000008); - available_ = false; - bitField0_ = (bitField0_ & ~0x00000010); - color_ = ""; - bitField0_ = (bitField0_ & ~0x00000020); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT.getDescriptor(); - } - - public com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT getDefaultInstanceForType() { - return com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT.getDefaultInstance(); - } - - public com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT build() { - com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT buildPartial() { - com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT result = new com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.value_ = value_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.script_ = script_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.height_ = height_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.coinbase_ = coinbase_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - result.available_ = available_; - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000020; - } - result.color_ = color_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT) { - return mergeFrom((com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT other) { - if (other == com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT.getDefaultInstance()) return this; - if (other.hasValue()) { - setValue(other.getValue()); - } - if (other.hasScript()) { - setScript(other.getScript()); - } - if (other.hasHeight()) { - setHeight(other.getHeight()); - } - if (other.hasCoinbase()) { - setCoinbase(other.getCoinbase()); - } - if (other.hasAvailable()) { - setAvailable(other.getAvailable()); - } - if (other.hasColor()) { - setColor(other.getColor()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasValue()) { - - return false; - } - if (!hasScript()) { - - return false; - } - if (!hasHeight()) { - - return false; - } - if (!hasCoinbase()) { - - return false; - } - if (!hasAvailable()) { - - return false; - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - value_ = input.readUInt64(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - script_ = input.readBytes(); - break; - } - case 24: { - bitField0_ |= 0x00000004; - height_ = input.readUInt32(); - break; - } - case 32: { - bitField0_ |= 0x00000008; - coinbase_ = input.readBool(); - break; - } - case 40: { - bitField0_ |= 0x00000010; - available_ = input.readBool(); - break; - } - case 50: { - bitField0_ |= 0x00000020; - color_ = input.readBytes(); - break; - } - } - } - } - - private int bitField0_; - - // required uint64 value = 1; - private long value_ ; - public boolean hasValue() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public long getValue() { - return value_; - } - public Builder setValue(long value) { - bitField0_ |= 0x00000001; - value_ = value; - onChanged(); - return this; - } - public Builder clearValue() { - bitField0_ = (bitField0_ & ~0x00000001); - value_ = 0L; - onChanged(); - return this; - } - - // required bytes script = 2; - private com.google.protobuf.ByteString script_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasScript() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public com.google.protobuf.ByteString getScript() { - return script_; - } - public Builder setScript(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - script_ = value; - onChanged(); - return this; - } - public Builder clearScript() { - bitField0_ = (bitField0_ & ~0x00000002); - script_ = getDefaultInstance().getScript(); - onChanged(); - return this; - } - - // required uint32 height = 3; - private int height_ ; - public boolean hasHeight() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public int getHeight() { - return height_; - } - public Builder setHeight(int value) { - bitField0_ |= 0x00000004; - height_ = value; - onChanged(); - return this; - } - public Builder clearHeight() { - bitField0_ = (bitField0_ & ~0x00000004); - height_ = 0; - onChanged(); - return this; - } - - // required bool coinbase = 4; - private boolean coinbase_ ; - public boolean hasCoinbase() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public boolean getCoinbase() { - return coinbase_; - } - public Builder setCoinbase(boolean value) { - bitField0_ |= 0x00000008; - coinbase_ = value; - onChanged(); - return this; - } - public Builder clearCoinbase() { - bitField0_ = (bitField0_ & ~0x00000008); - coinbase_ = false; - onChanged(); - return this; - } - - // required bool available = 5; - private boolean available_ ; - public boolean hasAvailable() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - public boolean getAvailable() { - return available_; - } - public Builder setAvailable(boolean value) { - bitField0_ |= 0x00000010; - available_ = value; - onChanged(); - return this; - } - public Builder clearAvailable() { - bitField0_ = (bitField0_ & ~0x00000010); - available_ = false; - onChanged(); - return this; - } - - // optional string color = 6; - private java.lang.Object color_ = ""; - public boolean hasColor() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - public String getColor() { - java.lang.Object ref = color_; - if (!(ref instanceof String)) { - String s = ((com.google.protobuf.ByteString) ref).toStringUtf8(); - color_ = s; - return s; - } else { - return (String) ref; - } - } - public Builder setColor(String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000020; - color_ = value; - onChanged(); - return this; - } - public Builder clearColor() { - bitField0_ = (bitField0_ & ~0x00000020); - color_ = getDefaultInstance().getColor(); - onChanged(); - return this; - } - void setColor(com.google.protobuf.ByteString value) { - bitField0_ |= 0x00000020; - color_ = value; - onChanged(); - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.model.TX.TXOUT) - } - - static { - defaultInstance = new TXOUT(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.model.TX.TXOUT) - } - - private int bitField0_; - // required uint32 storeVersion = 1; - public static final int STOREVERSION_FIELD_NUMBER = 1; - private int storeVersion_; - public boolean hasStoreVersion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getStoreVersion() { - return storeVersion_; - } - - // required bytes blockHash = 2; - public static final int BLOCKHASH_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString blockHash_; - public boolean hasBlockHash() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public com.google.protobuf.ByteString getBlockHash() { - return blockHash_; - } - - // required bytes hash = 3; - public static final int HASH_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString hash_; - public boolean hasHash() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public com.google.protobuf.ByteString getHash() { - return hash_; - } - - // required uint32 version = 4; - public static final int VERSION_FIELD_NUMBER = 4; - private int version_; - public boolean hasVersion() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public int getVersion() { - return version_; - } - - // required uint32 lockTime = 5; - public static final int LOCKTIME_FIELD_NUMBER = 5; - private int lockTime_; - public boolean hasLockTime() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - public int getLockTime() { - return lockTime_; - } - - // required uint32 ix = 6; - public static final int IX_FIELD_NUMBER = 6; - private int ix_; - public boolean hasIx() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - public int getIx() { - return ix_; - } - - // repeated .com.bitsofproof.supernode.model.TX.TXIN txin = 7; - public static final int TXIN_FIELD_NUMBER = 7; - private java.util.List txin_; - public java.util.List getTxinList() { - return txin_; - } - public java.util.List - getTxinOrBuilderList() { - return txin_; - } - public int getTxinCount() { - return txin_.size(); - } - public com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN getTxin(int index) { - return txin_.get(index); - } - public com.bitsofproof.supernode.model.LevelDBStore.TX.TXINOrBuilder getTxinOrBuilder( - int index) { - return txin_.get(index); - } - - // repeated .com.bitsofproof.supernode.model.TX.TXOUT txout = 8; - public static final int TXOUT_FIELD_NUMBER = 8; - private java.util.List txout_; - public java.util.List getTxoutList() { - return txout_; - } - public java.util.List - getTxoutOrBuilderList() { - return txout_; - } - public int getTxoutCount() { - return txout_.size(); - } - public com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT getTxout(int index) { - return txout_.get(index); - } - public com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUTOrBuilder getTxoutOrBuilder( - int index) { - return txout_.get(index); - } - - private void initFields() { - storeVersion_ = 0; - blockHash_ = com.google.protobuf.ByteString.EMPTY; - hash_ = com.google.protobuf.ByteString.EMPTY; - version_ = 0; - lockTime_ = 0; - ix_ = 0; - txin_ = java.util.Collections.emptyList(); - txout_ = java.util.Collections.emptyList(); - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasStoreVersion()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasBlockHash()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasHash()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasVersion()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasLockTime()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasIx()) { - memoizedIsInitialized = 0; - return false; - } - for (int i = 0; i < getTxinCount(); i++) { - if (!getTxin(i).isInitialized()) { - memoizedIsInitialized = 0; - return false; - } - } - for (int i = 0; i < getTxoutCount(); i++) { - if (!getTxout(i).isInitialized()) { - memoizedIsInitialized = 0; - return false; - } - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, storeVersion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, blockHash_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, hash_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeUInt32(4, version_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeUInt32(5, lockTime_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeUInt32(6, ix_); - } - for (int i = 0; i < txin_.size(); i++) { - output.writeMessage(7, txin_.get(i)); - } - for (int i = 0; i < txout_.size(); i++) { - output.writeMessage(8, txout_.get(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, storeVersion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, blockHash_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, hash_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(4, version_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(5, lockTime_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(6, ix_); - } - for (int i = 0; i < txin_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(7, txin_.get(i)); - } - for (int i = 0; i < txout_.size(); i++) { - size += com.google.protobuf.CodedOutputStream - .computeMessageSize(8, txout_.get(i)); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.model.LevelDBStore.TX parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.TX parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.model.LevelDBStore.TX prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.model.LevelDBStore.TXOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_TX_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_TX_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.model.LevelDBStore.TX.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - getTxinFieldBuilder(); - getTxoutFieldBuilder(); - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - storeVersion_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - blockHash_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - hash_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - version_ = 0; - bitField0_ = (bitField0_ & ~0x00000008); - lockTime_ = 0; - bitField0_ = (bitField0_ & ~0x00000010); - ix_ = 0; - bitField0_ = (bitField0_ & ~0x00000020); - if (txinBuilder_ == null) { - txin_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000040); - } else { - txinBuilder_.clear(); - } - if (txoutBuilder_ == null) { - txout_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000080); - } else { - txoutBuilder_.clear(); - } - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.model.LevelDBStore.TX.getDescriptor(); - } - - public com.bitsofproof.supernode.model.LevelDBStore.TX getDefaultInstanceForType() { - return com.bitsofproof.supernode.model.LevelDBStore.TX.getDefaultInstance(); - } - - public com.bitsofproof.supernode.model.LevelDBStore.TX build() { - com.bitsofproof.supernode.model.LevelDBStore.TX result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.model.LevelDBStore.TX buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.model.LevelDBStore.TX result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.model.LevelDBStore.TX buildPartial() { - com.bitsofproof.supernode.model.LevelDBStore.TX result = new com.bitsofproof.supernode.model.LevelDBStore.TX(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.storeVersion_ = storeVersion_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.blockHash_ = blockHash_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.hash_ = hash_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.version_ = version_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - result.lockTime_ = lockTime_; - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000020; - } - result.ix_ = ix_; - if (txinBuilder_ == null) { - if (((bitField0_ & 0x00000040) == 0x00000040)) { - txin_ = java.util.Collections.unmodifiableList(txin_); - bitField0_ = (bitField0_ & ~0x00000040); - } - result.txin_ = txin_; - } else { - result.txin_ = txinBuilder_.build(); - } - if (txoutBuilder_ == null) { - if (((bitField0_ & 0x00000080) == 0x00000080)) { - txout_ = java.util.Collections.unmodifiableList(txout_); - bitField0_ = (bitField0_ & ~0x00000080); - } - result.txout_ = txout_; - } else { - result.txout_ = txoutBuilder_.build(); - } - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.model.LevelDBStore.TX) { - return mergeFrom((com.bitsofproof.supernode.model.LevelDBStore.TX)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.model.LevelDBStore.TX other) { - if (other == com.bitsofproof.supernode.model.LevelDBStore.TX.getDefaultInstance()) return this; - if (other.hasStoreVersion()) { - setStoreVersion(other.getStoreVersion()); - } - if (other.hasBlockHash()) { - setBlockHash(other.getBlockHash()); - } - if (other.hasHash()) { - setHash(other.getHash()); - } - if (other.hasVersion()) { - setVersion(other.getVersion()); - } - if (other.hasLockTime()) { - setLockTime(other.getLockTime()); - } - if (other.hasIx()) { - setIx(other.getIx()); - } - if (txinBuilder_ == null) { - if (!other.txin_.isEmpty()) { - if (txin_.isEmpty()) { - txin_ = other.txin_; - bitField0_ = (bitField0_ & ~0x00000040); - } else { - ensureTxinIsMutable(); - txin_.addAll(other.txin_); - } - onChanged(); - } - } else { - if (!other.txin_.isEmpty()) { - if (txinBuilder_.isEmpty()) { - txinBuilder_.dispose(); - txinBuilder_ = null; - txin_ = other.txin_; - bitField0_ = (bitField0_ & ~0x00000040); - txinBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getTxinFieldBuilder() : null; - } else { - txinBuilder_.addAllMessages(other.txin_); - } - } - } - if (txoutBuilder_ == null) { - if (!other.txout_.isEmpty()) { - if (txout_.isEmpty()) { - txout_ = other.txout_; - bitField0_ = (bitField0_ & ~0x00000080); - } else { - ensureTxoutIsMutable(); - txout_.addAll(other.txout_); - } - onChanged(); - } - } else { - if (!other.txout_.isEmpty()) { - if (txoutBuilder_.isEmpty()) { - txoutBuilder_.dispose(); - txoutBuilder_ = null; - txout_ = other.txout_; - bitField0_ = (bitField0_ & ~0x00000080); - txoutBuilder_ = - com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? - getTxoutFieldBuilder() : null; - } else { - txoutBuilder_.addAllMessages(other.txout_); - } - } - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasStoreVersion()) { - - return false; - } - if (!hasBlockHash()) { - - return false; - } - if (!hasHash()) { - - return false; - } - if (!hasVersion()) { - - return false; - } - if (!hasLockTime()) { - - return false; - } - if (!hasIx()) { - - return false; - } - for (int i = 0; i < getTxinCount(); i++) { - if (!getTxin(i).isInitialized()) { - - return false; - } - } - for (int i = 0; i < getTxoutCount(); i++) { - if (!getTxout(i).isInitialized()) { - - return false; - } - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - storeVersion_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - blockHash_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - hash_ = input.readBytes(); - break; - } - case 32: { - bitField0_ |= 0x00000008; - version_ = input.readUInt32(); - break; - } - case 40: { - bitField0_ |= 0x00000010; - lockTime_ = input.readUInt32(); - break; - } - case 48: { - bitField0_ |= 0x00000020; - ix_ = input.readUInt32(); - break; - } - case 58: { - com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN.Builder subBuilder = com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN.newBuilder(); - input.readMessage(subBuilder, extensionRegistry); - addTxin(subBuilder.buildPartial()); - break; - } - case 66: { - com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT.Builder subBuilder = com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT.newBuilder(); - input.readMessage(subBuilder, extensionRegistry); - addTxout(subBuilder.buildPartial()); - break; - } - } - } - } - - private int bitField0_; - - // required uint32 storeVersion = 1; - private int storeVersion_ ; - public boolean hasStoreVersion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getStoreVersion() { - return storeVersion_; - } - public Builder setStoreVersion(int value) { - bitField0_ |= 0x00000001; - storeVersion_ = value; - onChanged(); - return this; - } - public Builder clearStoreVersion() { - bitField0_ = (bitField0_ & ~0x00000001); - storeVersion_ = 0; - onChanged(); - return this; - } - - // required bytes blockHash = 2; - private com.google.protobuf.ByteString blockHash_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasBlockHash() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public com.google.protobuf.ByteString getBlockHash() { - return blockHash_; - } - public Builder setBlockHash(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - blockHash_ = value; - onChanged(); - return this; - } - public Builder clearBlockHash() { - bitField0_ = (bitField0_ & ~0x00000002); - blockHash_ = getDefaultInstance().getBlockHash(); - onChanged(); - return this; - } - - // required bytes hash = 3; - private com.google.protobuf.ByteString hash_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasHash() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public com.google.protobuf.ByteString getHash() { - return hash_; - } - public Builder setHash(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - hash_ = value; - onChanged(); - return this; - } - public Builder clearHash() { - bitField0_ = (bitField0_ & ~0x00000004); - hash_ = getDefaultInstance().getHash(); - onChanged(); - return this; - } - - // required uint32 version = 4; - private int version_ ; - public boolean hasVersion() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public int getVersion() { - return version_; - } - public Builder setVersion(int value) { - bitField0_ |= 0x00000008; - version_ = value; - onChanged(); - return this; - } - public Builder clearVersion() { - bitField0_ = (bitField0_ & ~0x00000008); - version_ = 0; - onChanged(); - return this; - } - - // required uint32 lockTime = 5; - private int lockTime_ ; - public boolean hasLockTime() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - public int getLockTime() { - return lockTime_; - } - public Builder setLockTime(int value) { - bitField0_ |= 0x00000010; - lockTime_ = value; - onChanged(); - return this; - } - public Builder clearLockTime() { - bitField0_ = (bitField0_ & ~0x00000010); - lockTime_ = 0; - onChanged(); - return this; - } - - // required uint32 ix = 6; - private int ix_ ; - public boolean hasIx() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - public int getIx() { - return ix_; - } - public Builder setIx(int value) { - bitField0_ |= 0x00000020; - ix_ = value; - onChanged(); - return this; - } - public Builder clearIx() { - bitField0_ = (bitField0_ & ~0x00000020); - ix_ = 0; - onChanged(); - return this; - } - - // repeated .com.bitsofproof.supernode.model.TX.TXIN txin = 7; - private java.util.List txin_ = - java.util.Collections.emptyList(); - private void ensureTxinIsMutable() { - if (!((bitField0_ & 0x00000040) == 0x00000040)) { - txin_ = new java.util.ArrayList(txin_); - bitField0_ |= 0x00000040; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN, com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN.Builder, com.bitsofproof.supernode.model.LevelDBStore.TX.TXINOrBuilder> txinBuilder_; - - public java.util.List getTxinList() { - if (txinBuilder_ == null) { - return java.util.Collections.unmodifiableList(txin_); - } else { - return txinBuilder_.getMessageList(); - } - } - public int getTxinCount() { - if (txinBuilder_ == null) { - return txin_.size(); - } else { - return txinBuilder_.getCount(); - } - } - public com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN getTxin(int index) { - if (txinBuilder_ == null) { - return txin_.get(index); - } else { - return txinBuilder_.getMessage(index); - } - } - public Builder setTxin( - int index, com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN value) { - if (txinBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureTxinIsMutable(); - txin_.set(index, value); - onChanged(); - } else { - txinBuilder_.setMessage(index, value); - } - return this; - } - public Builder setTxin( - int index, com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN.Builder builderForValue) { - if (txinBuilder_ == null) { - ensureTxinIsMutable(); - txin_.set(index, builderForValue.build()); - onChanged(); - } else { - txinBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - public Builder addTxin(com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN value) { - if (txinBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureTxinIsMutable(); - txin_.add(value); - onChanged(); - } else { - txinBuilder_.addMessage(value); - } - return this; - } - public Builder addTxin( - int index, com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN value) { - if (txinBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureTxinIsMutable(); - txin_.add(index, value); - onChanged(); - } else { - txinBuilder_.addMessage(index, value); - } - return this; - } - public Builder addTxin( - com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN.Builder builderForValue) { - if (txinBuilder_ == null) { - ensureTxinIsMutable(); - txin_.add(builderForValue.build()); - onChanged(); - } else { - txinBuilder_.addMessage(builderForValue.build()); - } - return this; - } - public Builder addTxin( - int index, com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN.Builder builderForValue) { - if (txinBuilder_ == null) { - ensureTxinIsMutable(); - txin_.add(index, builderForValue.build()); - onChanged(); - } else { - txinBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - public Builder addAllTxin( - java.lang.Iterable values) { - if (txinBuilder_ == null) { - ensureTxinIsMutable(); - super.addAll(values, txin_); - onChanged(); - } else { - txinBuilder_.addAllMessages(values); - } - return this; - } - public Builder clearTxin() { - if (txinBuilder_ == null) { - txin_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000040); - onChanged(); - } else { - txinBuilder_.clear(); - } - return this; - } - public Builder removeTxin(int index) { - if (txinBuilder_ == null) { - ensureTxinIsMutable(); - txin_.remove(index); - onChanged(); - } else { - txinBuilder_.remove(index); - } - return this; - } - public com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN.Builder getTxinBuilder( - int index) { - return getTxinFieldBuilder().getBuilder(index); - } - public com.bitsofproof.supernode.model.LevelDBStore.TX.TXINOrBuilder getTxinOrBuilder( - int index) { - if (txinBuilder_ == null) { - return txin_.get(index); } else { - return txinBuilder_.getMessageOrBuilder(index); - } - } - public java.util.List - getTxinOrBuilderList() { - if (txinBuilder_ != null) { - return txinBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(txin_); - } - } - public com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN.Builder addTxinBuilder() { - return getTxinFieldBuilder().addBuilder( - com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN.getDefaultInstance()); - } - public com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN.Builder addTxinBuilder( - int index) { - return getTxinFieldBuilder().addBuilder( - index, com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN.getDefaultInstance()); - } - public java.util.List - getTxinBuilderList() { - return getTxinFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN, com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN.Builder, com.bitsofproof.supernode.model.LevelDBStore.TX.TXINOrBuilder> - getTxinFieldBuilder() { - if (txinBuilder_ == null) { - txinBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN, com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN.Builder, com.bitsofproof.supernode.model.LevelDBStore.TX.TXINOrBuilder>( - txin_, - ((bitField0_ & 0x00000040) == 0x00000040), - getParentForChildren(), - isClean()); - txin_ = null; - } - return txinBuilder_; - } - - // repeated .com.bitsofproof.supernode.model.TX.TXOUT txout = 8; - private java.util.List txout_ = - java.util.Collections.emptyList(); - private void ensureTxoutIsMutable() { - if (!((bitField0_ & 0x00000080) == 0x00000080)) { - txout_ = new java.util.ArrayList(txout_); - bitField0_ |= 0x00000080; - } - } - - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT, com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT.Builder, com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUTOrBuilder> txoutBuilder_; - - public java.util.List getTxoutList() { - if (txoutBuilder_ == null) { - return java.util.Collections.unmodifiableList(txout_); - } else { - return txoutBuilder_.getMessageList(); - } - } - public int getTxoutCount() { - if (txoutBuilder_ == null) { - return txout_.size(); - } else { - return txoutBuilder_.getCount(); - } - } - public com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT getTxout(int index) { - if (txoutBuilder_ == null) { - return txout_.get(index); - } else { - return txoutBuilder_.getMessage(index); - } - } - public Builder setTxout( - int index, com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT value) { - if (txoutBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureTxoutIsMutable(); - txout_.set(index, value); - onChanged(); - } else { - txoutBuilder_.setMessage(index, value); - } - return this; - } - public Builder setTxout( - int index, com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT.Builder builderForValue) { - if (txoutBuilder_ == null) { - ensureTxoutIsMutable(); - txout_.set(index, builderForValue.build()); - onChanged(); - } else { - txoutBuilder_.setMessage(index, builderForValue.build()); - } - return this; - } - public Builder addTxout(com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT value) { - if (txoutBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureTxoutIsMutable(); - txout_.add(value); - onChanged(); - } else { - txoutBuilder_.addMessage(value); - } - return this; - } - public Builder addTxout( - int index, com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT value) { - if (txoutBuilder_ == null) { - if (value == null) { - throw new NullPointerException(); - } - ensureTxoutIsMutable(); - txout_.add(index, value); - onChanged(); - } else { - txoutBuilder_.addMessage(index, value); - } - return this; - } - public Builder addTxout( - com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT.Builder builderForValue) { - if (txoutBuilder_ == null) { - ensureTxoutIsMutable(); - txout_.add(builderForValue.build()); - onChanged(); - } else { - txoutBuilder_.addMessage(builderForValue.build()); - } - return this; - } - public Builder addTxout( - int index, com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT.Builder builderForValue) { - if (txoutBuilder_ == null) { - ensureTxoutIsMutable(); - txout_.add(index, builderForValue.build()); - onChanged(); - } else { - txoutBuilder_.addMessage(index, builderForValue.build()); - } - return this; - } - public Builder addAllTxout( - java.lang.Iterable values) { - if (txoutBuilder_ == null) { - ensureTxoutIsMutable(); - super.addAll(values, txout_); - onChanged(); - } else { - txoutBuilder_.addAllMessages(values); - } - return this; - } - public Builder clearTxout() { - if (txoutBuilder_ == null) { - txout_ = java.util.Collections.emptyList(); - bitField0_ = (bitField0_ & ~0x00000080); - onChanged(); - } else { - txoutBuilder_.clear(); - } - return this; - } - public Builder removeTxout(int index) { - if (txoutBuilder_ == null) { - ensureTxoutIsMutable(); - txout_.remove(index); - onChanged(); - } else { - txoutBuilder_.remove(index); - } - return this; - } - public com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT.Builder getTxoutBuilder( - int index) { - return getTxoutFieldBuilder().getBuilder(index); - } - public com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUTOrBuilder getTxoutOrBuilder( - int index) { - if (txoutBuilder_ == null) { - return txout_.get(index); } else { - return txoutBuilder_.getMessageOrBuilder(index); - } - } - public java.util.List - getTxoutOrBuilderList() { - if (txoutBuilder_ != null) { - return txoutBuilder_.getMessageOrBuilderList(); - } else { - return java.util.Collections.unmodifiableList(txout_); - } - } - public com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT.Builder addTxoutBuilder() { - return getTxoutFieldBuilder().addBuilder( - com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT.getDefaultInstance()); - } - public com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT.Builder addTxoutBuilder( - int index) { - return getTxoutFieldBuilder().addBuilder( - index, com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT.getDefaultInstance()); - } - public java.util.List - getTxoutBuilderList() { - return getTxoutFieldBuilder().getBuilderList(); - } - private com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT, com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT.Builder, com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUTOrBuilder> - getTxoutFieldBuilder() { - if (txoutBuilder_ == null) { - txoutBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< - com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT, com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT.Builder, com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUTOrBuilder>( - txout_, - ((bitField0_ & 0x00000080) == 0x00000080), - getParentForChildren(), - isClean()); - txout_ = null; - } - return txoutBuilder_; - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.model.TX) - } - - static { - defaultInstance = new TX(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.model.TX) - } - - public interface BLOCKOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required uint32 storeVersion = 1; - boolean hasStoreVersion(); - int getStoreVersion(); - - // required bytes hash = 2; - boolean hasHash(); - com.google.protobuf.ByteString getHash(); - - // required uint32 height = 3; - boolean hasHeight(); - int getHeight(); - - // required uint32 version = 4; - boolean hasVersion(); - int getVersion(); - - // required bytes previousHash = 5; - boolean hasPreviousHash(); - com.google.protobuf.ByteString getPreviousHash(); - - // required bytes merkleRoot = 6; - boolean hasMerkleRoot(); - com.google.protobuf.ByteString getMerkleRoot(); - - // required uint32 createTime = 7; - boolean hasCreateTime(); - int getCreateTime(); - - // required uint32 difficultyTarget = 8; - boolean hasDifficultyTarget(); - int getDifficultyTarget(); - - // required uint32 nonce = 9; - boolean hasNonce(); - int getNonce(); - - // required uint64 chainWork = 10; - boolean hasChainWork(); - long getChainWork(); - - // required uint64 headId = 11; - boolean hasHeadId(); - long getHeadId(); - - // required bytes filterMap = 12; - boolean hasFilterMap(); - com.google.protobuf.ByteString getFilterMap(); - - // required uint32 filterFunctions = 13; - boolean hasFilterFunctions(); - int getFilterFunctions(); - - // repeated bytes txHashes = 14; - java.util.List getTxHashesList(); - int getTxHashesCount(); - com.google.protobuf.ByteString getTxHashes(int index); - } - public static final class BLOCK extends - com.google.protobuf.GeneratedMessage - implements BLOCKOrBuilder { - // Use BLOCK.newBuilder() to construct. - private BLOCK(Builder builder) { - super(builder); - } - private BLOCK(boolean noInit) {} - - private static final BLOCK defaultInstance; - public static BLOCK getDefaultInstance() { - return defaultInstance; - } - - public BLOCK getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_BLOCK_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_BLOCK_fieldAccessorTable; - } - - private int bitField0_; - // required uint32 storeVersion = 1; - public static final int STOREVERSION_FIELD_NUMBER = 1; - private int storeVersion_; - public boolean hasStoreVersion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getStoreVersion() { - return storeVersion_; - } - - // required bytes hash = 2; - public static final int HASH_FIELD_NUMBER = 2; - private com.google.protobuf.ByteString hash_; - public boolean hasHash() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public com.google.protobuf.ByteString getHash() { - return hash_; - } - - // required uint32 height = 3; - public static final int HEIGHT_FIELD_NUMBER = 3; - private int height_; - public boolean hasHeight() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public int getHeight() { - return height_; - } - - // required uint32 version = 4; - public static final int VERSION_FIELD_NUMBER = 4; - private int version_; - public boolean hasVersion() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public int getVersion() { - return version_; - } - - // required bytes previousHash = 5; - public static final int PREVIOUSHASH_FIELD_NUMBER = 5; - private com.google.protobuf.ByteString previousHash_; - public boolean hasPreviousHash() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - public com.google.protobuf.ByteString getPreviousHash() { - return previousHash_; - } - - // required bytes merkleRoot = 6; - public static final int MERKLEROOT_FIELD_NUMBER = 6; - private com.google.protobuf.ByteString merkleRoot_; - public boolean hasMerkleRoot() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - public com.google.protobuf.ByteString getMerkleRoot() { - return merkleRoot_; - } - - // required uint32 createTime = 7; - public static final int CREATETIME_FIELD_NUMBER = 7; - private int createTime_; - public boolean hasCreateTime() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - public int getCreateTime() { - return createTime_; - } - - // required uint32 difficultyTarget = 8; - public static final int DIFFICULTYTARGET_FIELD_NUMBER = 8; - private int difficultyTarget_; - public boolean hasDifficultyTarget() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - public int getDifficultyTarget() { - return difficultyTarget_; - } - - // required uint32 nonce = 9; - public static final int NONCE_FIELD_NUMBER = 9; - private int nonce_; - public boolean hasNonce() { - return ((bitField0_ & 0x00000100) == 0x00000100); - } - public int getNonce() { - return nonce_; - } - - // required uint64 chainWork = 10; - public static final int CHAINWORK_FIELD_NUMBER = 10; - private long chainWork_; - public boolean hasChainWork() { - return ((bitField0_ & 0x00000200) == 0x00000200); - } - public long getChainWork() { - return chainWork_; - } - - // required uint64 headId = 11; - public static final int HEADID_FIELD_NUMBER = 11; - private long headId_; - public boolean hasHeadId() { - return ((bitField0_ & 0x00000400) == 0x00000400); - } - public long getHeadId() { - return headId_; - } - - // required bytes filterMap = 12; - public static final int FILTERMAP_FIELD_NUMBER = 12; - private com.google.protobuf.ByteString filterMap_; - public boolean hasFilterMap() { - return ((bitField0_ & 0x00000800) == 0x00000800); - } - public com.google.protobuf.ByteString getFilterMap() { - return filterMap_; - } - - // required uint32 filterFunctions = 13; - public static final int FILTERFUNCTIONS_FIELD_NUMBER = 13; - private int filterFunctions_; - public boolean hasFilterFunctions() { - return ((bitField0_ & 0x00001000) == 0x00001000); - } - public int getFilterFunctions() { - return filterFunctions_; - } - - // repeated bytes txHashes = 14; - public static final int TXHASHES_FIELD_NUMBER = 14; - private java.util.List txHashes_; - public java.util.List - getTxHashesList() { - return txHashes_; - } - public int getTxHashesCount() { - return txHashes_.size(); - } - public com.google.protobuf.ByteString getTxHashes(int index) { - return txHashes_.get(index); - } - - private void initFields() { - storeVersion_ = 0; - hash_ = com.google.protobuf.ByteString.EMPTY; - height_ = 0; - version_ = 0; - previousHash_ = com.google.protobuf.ByteString.EMPTY; - merkleRoot_ = com.google.protobuf.ByteString.EMPTY; - createTime_ = 0; - difficultyTarget_ = 0; - nonce_ = 0; - chainWork_ = 0L; - headId_ = 0L; - filterMap_ = com.google.protobuf.ByteString.EMPTY; - filterFunctions_ = 0; - txHashes_ = java.util.Collections.emptyList();; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasStoreVersion()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasHash()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasHeight()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasVersion()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasPreviousHash()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasMerkleRoot()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasCreateTime()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasDifficultyTarget()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasNonce()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasChainWork()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasHeadId()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasFilterMap()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasFilterFunctions()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, storeVersion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, hash_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeUInt32(3, height_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeUInt32(4, version_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeBytes(5, previousHash_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeBytes(6, merkleRoot_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - output.writeUInt32(7, createTime_); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - output.writeUInt32(8, difficultyTarget_); - } - if (((bitField0_ & 0x00000100) == 0x00000100)) { - output.writeUInt32(9, nonce_); - } - if (((bitField0_ & 0x00000200) == 0x00000200)) { - output.writeUInt64(10, chainWork_); - } - if (((bitField0_ & 0x00000400) == 0x00000400)) { - output.writeUInt64(11, headId_); - } - if (((bitField0_ & 0x00000800) == 0x00000800)) { - output.writeBytes(12, filterMap_); - } - if (((bitField0_ & 0x00001000) == 0x00001000)) { - output.writeUInt32(13, filterFunctions_); - } - for (int i = 0; i < txHashes_.size(); i++) { - output.writeBytes(14, txHashes_.get(i)); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, storeVersion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, hash_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(3, height_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(4, version_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(5, previousHash_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(6, merkleRoot_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(7, createTime_); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(8, difficultyTarget_); - } - if (((bitField0_ & 0x00000100) == 0x00000100)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(9, nonce_); - } - if (((bitField0_ & 0x00000200) == 0x00000200)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(10, chainWork_); - } - if (((bitField0_ & 0x00000400) == 0x00000400)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(11, headId_); - } - if (((bitField0_ & 0x00000800) == 0x00000800)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(12, filterMap_); - } - if (((bitField0_ & 0x00001000) == 0x00001000)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(13, filterFunctions_); - } - { - int dataSize = 0; - for (int i = 0; i < txHashes_.size(); i++) { - dataSize += com.google.protobuf.CodedOutputStream - .computeBytesSizeNoTag(txHashes_.get(i)); - } - size += dataSize; - size += 1 * getTxHashesList().size(); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.model.LevelDBStore.BLOCK parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.BLOCK parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.BLOCK parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.BLOCK parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.BLOCK parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.BLOCK parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.BLOCK parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.model.LevelDBStore.BLOCK parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.model.LevelDBStore.BLOCK parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.BLOCK parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.model.LevelDBStore.BLOCK prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.model.LevelDBStore.BLOCKOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_BLOCK_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_BLOCK_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.model.LevelDBStore.BLOCK.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - storeVersion_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - hash_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000002); - height_ = 0; - bitField0_ = (bitField0_ & ~0x00000004); - version_ = 0; - bitField0_ = (bitField0_ & ~0x00000008); - previousHash_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000010); - merkleRoot_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000020); - createTime_ = 0; - bitField0_ = (bitField0_ & ~0x00000040); - difficultyTarget_ = 0; - bitField0_ = (bitField0_ & ~0x00000080); - nonce_ = 0; - bitField0_ = (bitField0_ & ~0x00000100); - chainWork_ = 0L; - bitField0_ = (bitField0_ & ~0x00000200); - headId_ = 0L; - bitField0_ = (bitField0_ & ~0x00000400); - filterMap_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000800); - filterFunctions_ = 0; - bitField0_ = (bitField0_ & ~0x00001000); - txHashes_ = java.util.Collections.emptyList();; - bitField0_ = (bitField0_ & ~0x00002000); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.model.LevelDBStore.BLOCK.getDescriptor(); - } - - public com.bitsofproof.supernode.model.LevelDBStore.BLOCK getDefaultInstanceForType() { - return com.bitsofproof.supernode.model.LevelDBStore.BLOCK.getDefaultInstance(); - } - - public com.bitsofproof.supernode.model.LevelDBStore.BLOCK build() { - com.bitsofproof.supernode.model.LevelDBStore.BLOCK result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.model.LevelDBStore.BLOCK buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.model.LevelDBStore.BLOCK result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.model.LevelDBStore.BLOCK buildPartial() { - com.bitsofproof.supernode.model.LevelDBStore.BLOCK result = new com.bitsofproof.supernode.model.LevelDBStore.BLOCK(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.storeVersion_ = storeVersion_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.hash_ = hash_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.height_ = height_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.version_ = version_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - result.previousHash_ = previousHash_; - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000020; - } - result.merkleRoot_ = merkleRoot_; - if (((from_bitField0_ & 0x00000040) == 0x00000040)) { - to_bitField0_ |= 0x00000040; - } - result.createTime_ = createTime_; - if (((from_bitField0_ & 0x00000080) == 0x00000080)) { - to_bitField0_ |= 0x00000080; - } - result.difficultyTarget_ = difficultyTarget_; - if (((from_bitField0_ & 0x00000100) == 0x00000100)) { - to_bitField0_ |= 0x00000100; - } - result.nonce_ = nonce_; - if (((from_bitField0_ & 0x00000200) == 0x00000200)) { - to_bitField0_ |= 0x00000200; - } - result.chainWork_ = chainWork_; - if (((from_bitField0_ & 0x00000400) == 0x00000400)) { - to_bitField0_ |= 0x00000400; - } - result.headId_ = headId_; - if (((from_bitField0_ & 0x00000800) == 0x00000800)) { - to_bitField0_ |= 0x00000800; - } - result.filterMap_ = filterMap_; - if (((from_bitField0_ & 0x00001000) == 0x00001000)) { - to_bitField0_ |= 0x00001000; - } - result.filterFunctions_ = filterFunctions_; - if (((bitField0_ & 0x00002000) == 0x00002000)) { - txHashes_ = java.util.Collections.unmodifiableList(txHashes_); - bitField0_ = (bitField0_ & ~0x00002000); - } - result.txHashes_ = txHashes_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.model.LevelDBStore.BLOCK) { - return mergeFrom((com.bitsofproof.supernode.model.LevelDBStore.BLOCK)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.model.LevelDBStore.BLOCK other) { - if (other == com.bitsofproof.supernode.model.LevelDBStore.BLOCK.getDefaultInstance()) return this; - if (other.hasStoreVersion()) { - setStoreVersion(other.getStoreVersion()); - } - if (other.hasHash()) { - setHash(other.getHash()); - } - if (other.hasHeight()) { - setHeight(other.getHeight()); - } - if (other.hasVersion()) { - setVersion(other.getVersion()); - } - if (other.hasPreviousHash()) { - setPreviousHash(other.getPreviousHash()); - } - if (other.hasMerkleRoot()) { - setMerkleRoot(other.getMerkleRoot()); - } - if (other.hasCreateTime()) { - setCreateTime(other.getCreateTime()); - } - if (other.hasDifficultyTarget()) { - setDifficultyTarget(other.getDifficultyTarget()); - } - if (other.hasNonce()) { - setNonce(other.getNonce()); - } - if (other.hasChainWork()) { - setChainWork(other.getChainWork()); - } - if (other.hasHeadId()) { - setHeadId(other.getHeadId()); - } - if (other.hasFilterMap()) { - setFilterMap(other.getFilterMap()); - } - if (other.hasFilterFunctions()) { - setFilterFunctions(other.getFilterFunctions()); - } - if (!other.txHashes_.isEmpty()) { - if (txHashes_.isEmpty()) { - txHashes_ = other.txHashes_; - bitField0_ = (bitField0_ & ~0x00002000); - } else { - ensureTxHashesIsMutable(); - txHashes_.addAll(other.txHashes_); - } - onChanged(); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasStoreVersion()) { - - return false; - } - if (!hasHash()) { - - return false; - } - if (!hasHeight()) { - - return false; - } - if (!hasVersion()) { - - return false; - } - if (!hasPreviousHash()) { - - return false; - } - if (!hasMerkleRoot()) { - - return false; - } - if (!hasCreateTime()) { - - return false; - } - if (!hasDifficultyTarget()) { - - return false; - } - if (!hasNonce()) { - - return false; - } - if (!hasChainWork()) { - - return false; - } - if (!hasHeadId()) { - - return false; - } - if (!hasFilterMap()) { - - return false; - } - if (!hasFilterFunctions()) { - - return false; - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - storeVersion_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - hash_ = input.readBytes(); - break; - } - case 24: { - bitField0_ |= 0x00000004; - height_ = input.readUInt32(); - break; - } - case 32: { - bitField0_ |= 0x00000008; - version_ = input.readUInt32(); - break; - } - case 42: { - bitField0_ |= 0x00000010; - previousHash_ = input.readBytes(); - break; - } - case 50: { - bitField0_ |= 0x00000020; - merkleRoot_ = input.readBytes(); - break; - } - case 56: { - bitField0_ |= 0x00000040; - createTime_ = input.readUInt32(); - break; - } - case 64: { - bitField0_ |= 0x00000080; - difficultyTarget_ = input.readUInt32(); - break; - } - case 72: { - bitField0_ |= 0x00000100; - nonce_ = input.readUInt32(); - break; - } - case 80: { - bitField0_ |= 0x00000200; - chainWork_ = input.readUInt64(); - break; - } - case 88: { - bitField0_ |= 0x00000400; - headId_ = input.readUInt64(); - break; - } - case 98: { - bitField0_ |= 0x00000800; - filterMap_ = input.readBytes(); - break; - } - case 104: { - bitField0_ |= 0x00001000; - filterFunctions_ = input.readUInt32(); - break; - } - case 114: { - ensureTxHashesIsMutable(); - txHashes_.add(input.readBytes()); - break; - } - } - } - } - - private int bitField0_; - - // required uint32 storeVersion = 1; - private int storeVersion_ ; - public boolean hasStoreVersion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getStoreVersion() { - return storeVersion_; - } - public Builder setStoreVersion(int value) { - bitField0_ |= 0x00000001; - storeVersion_ = value; - onChanged(); - return this; - } - public Builder clearStoreVersion() { - bitField0_ = (bitField0_ & ~0x00000001); - storeVersion_ = 0; - onChanged(); - return this; - } - - // required bytes hash = 2; - private com.google.protobuf.ByteString hash_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasHash() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public com.google.protobuf.ByteString getHash() { - return hash_; - } - public Builder setHash(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - hash_ = value; - onChanged(); - return this; - } - public Builder clearHash() { - bitField0_ = (bitField0_ & ~0x00000002); - hash_ = getDefaultInstance().getHash(); - onChanged(); - return this; - } - - // required uint32 height = 3; - private int height_ ; - public boolean hasHeight() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public int getHeight() { - return height_; - } - public Builder setHeight(int value) { - bitField0_ |= 0x00000004; - height_ = value; - onChanged(); - return this; - } - public Builder clearHeight() { - bitField0_ = (bitField0_ & ~0x00000004); - height_ = 0; - onChanged(); - return this; - } - - // required uint32 version = 4; - private int version_ ; - public boolean hasVersion() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public int getVersion() { - return version_; - } - public Builder setVersion(int value) { - bitField0_ |= 0x00000008; - version_ = value; - onChanged(); - return this; - } - public Builder clearVersion() { - bitField0_ = (bitField0_ & ~0x00000008); - version_ = 0; - onChanged(); - return this; - } - - // required bytes previousHash = 5; - private com.google.protobuf.ByteString previousHash_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasPreviousHash() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - public com.google.protobuf.ByteString getPreviousHash() { - return previousHash_; - } - public Builder setPreviousHash(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000010; - previousHash_ = value; - onChanged(); - return this; - } - public Builder clearPreviousHash() { - bitField0_ = (bitField0_ & ~0x00000010); - previousHash_ = getDefaultInstance().getPreviousHash(); - onChanged(); - return this; - } - - // required bytes merkleRoot = 6; - private com.google.protobuf.ByteString merkleRoot_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasMerkleRoot() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - public com.google.protobuf.ByteString getMerkleRoot() { - return merkleRoot_; - } - public Builder setMerkleRoot(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000020; - merkleRoot_ = value; - onChanged(); - return this; - } - public Builder clearMerkleRoot() { - bitField0_ = (bitField0_ & ~0x00000020); - merkleRoot_ = getDefaultInstance().getMerkleRoot(); - onChanged(); - return this; - } - - // required uint32 createTime = 7; - private int createTime_ ; - public boolean hasCreateTime() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - public int getCreateTime() { - return createTime_; - } - public Builder setCreateTime(int value) { - bitField0_ |= 0x00000040; - createTime_ = value; - onChanged(); - return this; - } - public Builder clearCreateTime() { - bitField0_ = (bitField0_ & ~0x00000040); - createTime_ = 0; - onChanged(); - return this; - } - - // required uint32 difficultyTarget = 8; - private int difficultyTarget_ ; - public boolean hasDifficultyTarget() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - public int getDifficultyTarget() { - return difficultyTarget_; - } - public Builder setDifficultyTarget(int value) { - bitField0_ |= 0x00000080; - difficultyTarget_ = value; - onChanged(); - return this; - } - public Builder clearDifficultyTarget() { - bitField0_ = (bitField0_ & ~0x00000080); - difficultyTarget_ = 0; - onChanged(); - return this; - } - - // required uint32 nonce = 9; - private int nonce_ ; - public boolean hasNonce() { - return ((bitField0_ & 0x00000100) == 0x00000100); - } - public int getNonce() { - return nonce_; - } - public Builder setNonce(int value) { - bitField0_ |= 0x00000100; - nonce_ = value; - onChanged(); - return this; - } - public Builder clearNonce() { - bitField0_ = (bitField0_ & ~0x00000100); - nonce_ = 0; - onChanged(); - return this; - } - - // required uint64 chainWork = 10; - private long chainWork_ ; - public boolean hasChainWork() { - return ((bitField0_ & 0x00000200) == 0x00000200); - } - public long getChainWork() { - return chainWork_; - } - public Builder setChainWork(long value) { - bitField0_ |= 0x00000200; - chainWork_ = value; - onChanged(); - return this; - } - public Builder clearChainWork() { - bitField0_ = (bitField0_ & ~0x00000200); - chainWork_ = 0L; - onChanged(); - return this; - } - - // required uint64 headId = 11; - private long headId_ ; - public boolean hasHeadId() { - return ((bitField0_ & 0x00000400) == 0x00000400); - } - public long getHeadId() { - return headId_; - } - public Builder setHeadId(long value) { - bitField0_ |= 0x00000400; - headId_ = value; - onChanged(); - return this; - } - public Builder clearHeadId() { - bitField0_ = (bitField0_ & ~0x00000400); - headId_ = 0L; - onChanged(); - return this; - } - - // required bytes filterMap = 12; - private com.google.protobuf.ByteString filterMap_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasFilterMap() { - return ((bitField0_ & 0x00000800) == 0x00000800); - } - public com.google.protobuf.ByteString getFilterMap() { - return filterMap_; - } - public Builder setFilterMap(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000800; - filterMap_ = value; - onChanged(); - return this; - } - public Builder clearFilterMap() { - bitField0_ = (bitField0_ & ~0x00000800); - filterMap_ = getDefaultInstance().getFilterMap(); - onChanged(); - return this; - } - - // required uint32 filterFunctions = 13; - private int filterFunctions_ ; - public boolean hasFilterFunctions() { - return ((bitField0_ & 0x00001000) == 0x00001000); - } - public int getFilterFunctions() { - return filterFunctions_; - } - public Builder setFilterFunctions(int value) { - bitField0_ |= 0x00001000; - filterFunctions_ = value; - onChanged(); - return this; - } - public Builder clearFilterFunctions() { - bitField0_ = (bitField0_ & ~0x00001000); - filterFunctions_ = 0; - onChanged(); - return this; - } - - // repeated bytes txHashes = 14; - private java.util.List txHashes_ = java.util.Collections.emptyList();; - private void ensureTxHashesIsMutable() { - if (!((bitField0_ & 0x00002000) == 0x00002000)) { - txHashes_ = new java.util.ArrayList(txHashes_); - bitField0_ |= 0x00002000; - } - } - public java.util.List - getTxHashesList() { - return java.util.Collections.unmodifiableList(txHashes_); - } - public int getTxHashesCount() { - return txHashes_.size(); - } - public com.google.protobuf.ByteString getTxHashes(int index) { - return txHashes_.get(index); - } - public Builder setTxHashes( - int index, com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureTxHashesIsMutable(); - txHashes_.set(index, value); - onChanged(); - return this; - } - public Builder addTxHashes(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - ensureTxHashesIsMutable(); - txHashes_.add(value); - onChanged(); - return this; - } - public Builder addAllTxHashes( - java.lang.Iterable values) { - ensureTxHashesIsMutable(); - super.addAll(values, txHashes_); - onChanged(); - return this; - } - public Builder clearTxHashes() { - txHashes_ = java.util.Collections.emptyList();; - bitField0_ = (bitField0_ & ~0x00002000); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.model.BLOCK) - } - - static { - defaultInstance = new BLOCK(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.model.BLOCK) - } - - public interface PEEROrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required uint32 storeVersion = 1; - boolean hasStoreVersion(); - int getStoreVersion(); - - // required string address = 2; - boolean hasAddress(); - String getAddress(); - - // required uint32 version = 3; - boolean hasVersion(); - int getVersion(); - - // required uint64 services = 4; - boolean hasServices(); - long getServices(); - - // required uint32 height = 5; - boolean hasHeight(); - int getHeight(); - - // required string name = 6; - boolean hasName(); - String getName(); - - // required string agent = 7; - boolean hasAgent(); - String getAgent(); - - // required uint64 responseTime = 8; - boolean hasResponseTime(); - long getResponseTime(); - - // required uint64 connected = 9; - boolean hasConnected(); - long getConnected(); - - // required uint64 disconnected = 10; - boolean hasDisconnected(); - long getDisconnected(); - - // required uint64 trafficIn = 11; - boolean hasTrafficIn(); - long getTrafficIn(); - - // required uint64 trafficOut = 12; - boolean hasTrafficOut(); - long getTrafficOut(); - - // required uint64 banned = 13; - boolean hasBanned(); - long getBanned(); - - // optional string banReason = 14; - boolean hasBanReason(); - String getBanReason(); - } - public static final class PEER extends - com.google.protobuf.GeneratedMessage - implements PEEROrBuilder { - // Use PEER.newBuilder() to construct. - private PEER(Builder builder) { - super(builder); - } - private PEER(boolean noInit) {} - - private static final PEER defaultInstance; - public static PEER getDefaultInstance() { - return defaultInstance; - } - - public PEER getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_PEER_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_PEER_fieldAccessorTable; - } - - private int bitField0_; - // required uint32 storeVersion = 1; - public static final int STOREVERSION_FIELD_NUMBER = 1; - private int storeVersion_; - public boolean hasStoreVersion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getStoreVersion() { - return storeVersion_; - } - - // required string address = 2; - public static final int ADDRESS_FIELD_NUMBER = 2; - private java.lang.Object address_; - public boolean hasAddress() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public String getAddress() { - java.lang.Object ref = address_; - if (ref instanceof String) { - return (String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - String s = bs.toStringUtf8(); - if (com.google.protobuf.Internal.isValidUtf8(bs)) { - address_ = s; - } - return s; - } - } - private com.google.protobuf.ByteString getAddressBytes() { - java.lang.Object ref = address_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8((String) ref); - address_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // required uint32 version = 3; - public static final int VERSION_FIELD_NUMBER = 3; - private int version_; - public boolean hasVersion() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public int getVersion() { - return version_; - } - - // required uint64 services = 4; - public static final int SERVICES_FIELD_NUMBER = 4; - private long services_; - public boolean hasServices() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public long getServices() { - return services_; - } - - // required uint32 height = 5; - public static final int HEIGHT_FIELD_NUMBER = 5; - private int height_; - public boolean hasHeight() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - public int getHeight() { - return height_; - } - - // required string name = 6; - public static final int NAME_FIELD_NUMBER = 6; - private java.lang.Object name_; - public boolean hasName() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - public String getName() { - java.lang.Object ref = name_; - if (ref instanceof String) { - return (String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - String s = bs.toStringUtf8(); - if (com.google.protobuf.Internal.isValidUtf8(bs)) { - name_ = s; - } - return s; - } - } - private com.google.protobuf.ByteString getNameBytes() { - java.lang.Object ref = name_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8((String) ref); - name_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // required string agent = 7; - public static final int AGENT_FIELD_NUMBER = 7; - private java.lang.Object agent_; - public boolean hasAgent() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - public String getAgent() { - java.lang.Object ref = agent_; - if (ref instanceof String) { - return (String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - String s = bs.toStringUtf8(); - if (com.google.protobuf.Internal.isValidUtf8(bs)) { - agent_ = s; - } - return s; - } - } - private com.google.protobuf.ByteString getAgentBytes() { - java.lang.Object ref = agent_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8((String) ref); - agent_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // required uint64 responseTime = 8; - public static final int RESPONSETIME_FIELD_NUMBER = 8; - private long responseTime_; - public boolean hasResponseTime() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - public long getResponseTime() { - return responseTime_; - } - - // required uint64 connected = 9; - public static final int CONNECTED_FIELD_NUMBER = 9; - private long connected_; - public boolean hasConnected() { - return ((bitField0_ & 0x00000100) == 0x00000100); - } - public long getConnected() { - return connected_; - } - - // required uint64 disconnected = 10; - public static final int DISCONNECTED_FIELD_NUMBER = 10; - private long disconnected_; - public boolean hasDisconnected() { - return ((bitField0_ & 0x00000200) == 0x00000200); - } - public long getDisconnected() { - return disconnected_; - } - - // required uint64 trafficIn = 11; - public static final int TRAFFICIN_FIELD_NUMBER = 11; - private long trafficIn_; - public boolean hasTrafficIn() { - return ((bitField0_ & 0x00000400) == 0x00000400); - } - public long getTrafficIn() { - return trafficIn_; - } - - // required uint64 trafficOut = 12; - public static final int TRAFFICOUT_FIELD_NUMBER = 12; - private long trafficOut_; - public boolean hasTrafficOut() { - return ((bitField0_ & 0x00000800) == 0x00000800); - } - public long getTrafficOut() { - return trafficOut_; - } - - // required uint64 banned = 13; - public static final int BANNED_FIELD_NUMBER = 13; - private long banned_; - public boolean hasBanned() { - return ((bitField0_ & 0x00001000) == 0x00001000); - } - public long getBanned() { - return banned_; - } - - // optional string banReason = 14; - public static final int BANREASON_FIELD_NUMBER = 14; - private java.lang.Object banReason_; - public boolean hasBanReason() { - return ((bitField0_ & 0x00002000) == 0x00002000); - } - public String getBanReason() { - java.lang.Object ref = banReason_; - if (ref instanceof String) { - return (String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - String s = bs.toStringUtf8(); - if (com.google.protobuf.Internal.isValidUtf8(bs)) { - banReason_ = s; - } - return s; - } - } - private com.google.protobuf.ByteString getBanReasonBytes() { - java.lang.Object ref = banReason_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8((String) ref); - banReason_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - private void initFields() { - storeVersion_ = 0; - address_ = ""; - version_ = 0; - services_ = 0L; - height_ = 0; - name_ = ""; - agent_ = ""; - responseTime_ = 0L; - connected_ = 0L; - disconnected_ = 0L; - trafficIn_ = 0L; - trafficOut_ = 0L; - banned_ = 0L; - banReason_ = ""; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasStoreVersion()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasAddress()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasVersion()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasServices()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasHeight()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasName()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasAgent()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasResponseTime()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasConnected()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasDisconnected()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasTrafficIn()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasTrafficOut()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasBanned()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, storeVersion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getAddressBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeUInt32(3, version_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeUInt64(4, services_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeUInt32(5, height_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeBytes(6, getNameBytes()); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - output.writeBytes(7, getAgentBytes()); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - output.writeUInt64(8, responseTime_); - } - if (((bitField0_ & 0x00000100) == 0x00000100)) { - output.writeUInt64(9, connected_); - } - if (((bitField0_ & 0x00000200) == 0x00000200)) { - output.writeUInt64(10, disconnected_); - } - if (((bitField0_ & 0x00000400) == 0x00000400)) { - output.writeUInt64(11, trafficIn_); - } - if (((bitField0_ & 0x00000800) == 0x00000800)) { - output.writeUInt64(12, trafficOut_); - } - if (((bitField0_ & 0x00001000) == 0x00001000)) { - output.writeUInt64(13, banned_); - } - if (((bitField0_ & 0x00002000) == 0x00002000)) { - output.writeBytes(14, getBanReasonBytes()); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, storeVersion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getAddressBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(3, version_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(4, services_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(5, height_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(6, getNameBytes()); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(7, getAgentBytes()); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(8, responseTime_); - } - if (((bitField0_ & 0x00000100) == 0x00000100)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(9, connected_); - } - if (((bitField0_ & 0x00000200) == 0x00000200)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(10, disconnected_); - } - if (((bitField0_ & 0x00000400) == 0x00000400)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(11, trafficIn_); - } - if (((bitField0_ & 0x00000800) == 0x00000800)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(12, trafficOut_); - } - if (((bitField0_ & 0x00001000) == 0x00001000)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(13, banned_); - } - if (((bitField0_ & 0x00002000) == 0x00002000)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(14, getBanReasonBytes()); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.model.LevelDBStore.PEER parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.PEER parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.PEER parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.PEER parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.PEER parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.PEER parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.PEER parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.model.LevelDBStore.PEER parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.model.LevelDBStore.PEER parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.PEER parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.model.LevelDBStore.PEER prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.model.LevelDBStore.PEEROrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_PEER_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_PEER_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.model.LevelDBStore.PEER.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - storeVersion_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - address_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - version_ = 0; - bitField0_ = (bitField0_ & ~0x00000004); - services_ = 0L; - bitField0_ = (bitField0_ & ~0x00000008); - height_ = 0; - bitField0_ = (bitField0_ & ~0x00000010); - name_ = ""; - bitField0_ = (bitField0_ & ~0x00000020); - agent_ = ""; - bitField0_ = (bitField0_ & ~0x00000040); - responseTime_ = 0L; - bitField0_ = (bitField0_ & ~0x00000080); - connected_ = 0L; - bitField0_ = (bitField0_ & ~0x00000100); - disconnected_ = 0L; - bitField0_ = (bitField0_ & ~0x00000200); - trafficIn_ = 0L; - bitField0_ = (bitField0_ & ~0x00000400); - trafficOut_ = 0L; - bitField0_ = (bitField0_ & ~0x00000800); - banned_ = 0L; - bitField0_ = (bitField0_ & ~0x00001000); - banReason_ = ""; - bitField0_ = (bitField0_ & ~0x00002000); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.model.LevelDBStore.PEER.getDescriptor(); - } - - public com.bitsofproof.supernode.model.LevelDBStore.PEER getDefaultInstanceForType() { - return com.bitsofproof.supernode.model.LevelDBStore.PEER.getDefaultInstance(); - } - - public com.bitsofproof.supernode.model.LevelDBStore.PEER build() { - com.bitsofproof.supernode.model.LevelDBStore.PEER result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.model.LevelDBStore.PEER buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.model.LevelDBStore.PEER result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.model.LevelDBStore.PEER buildPartial() { - com.bitsofproof.supernode.model.LevelDBStore.PEER result = new com.bitsofproof.supernode.model.LevelDBStore.PEER(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.storeVersion_ = storeVersion_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.address_ = address_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.version_ = version_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.services_ = services_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - result.height_ = height_; - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000020; - } - result.name_ = name_; - if (((from_bitField0_ & 0x00000040) == 0x00000040)) { - to_bitField0_ |= 0x00000040; - } - result.agent_ = agent_; - if (((from_bitField0_ & 0x00000080) == 0x00000080)) { - to_bitField0_ |= 0x00000080; - } - result.responseTime_ = responseTime_; - if (((from_bitField0_ & 0x00000100) == 0x00000100)) { - to_bitField0_ |= 0x00000100; - } - result.connected_ = connected_; - if (((from_bitField0_ & 0x00000200) == 0x00000200)) { - to_bitField0_ |= 0x00000200; - } - result.disconnected_ = disconnected_; - if (((from_bitField0_ & 0x00000400) == 0x00000400)) { - to_bitField0_ |= 0x00000400; - } - result.trafficIn_ = trafficIn_; - if (((from_bitField0_ & 0x00000800) == 0x00000800)) { - to_bitField0_ |= 0x00000800; - } - result.trafficOut_ = trafficOut_; - if (((from_bitField0_ & 0x00001000) == 0x00001000)) { - to_bitField0_ |= 0x00001000; - } - result.banned_ = banned_; - if (((from_bitField0_ & 0x00002000) == 0x00002000)) { - to_bitField0_ |= 0x00002000; - } - result.banReason_ = banReason_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.model.LevelDBStore.PEER) { - return mergeFrom((com.bitsofproof.supernode.model.LevelDBStore.PEER)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.model.LevelDBStore.PEER other) { - if (other == com.bitsofproof.supernode.model.LevelDBStore.PEER.getDefaultInstance()) return this; - if (other.hasStoreVersion()) { - setStoreVersion(other.getStoreVersion()); - } - if (other.hasAddress()) { - setAddress(other.getAddress()); - } - if (other.hasVersion()) { - setVersion(other.getVersion()); - } - if (other.hasServices()) { - setServices(other.getServices()); - } - if (other.hasHeight()) { - setHeight(other.getHeight()); - } - if (other.hasName()) { - setName(other.getName()); - } - if (other.hasAgent()) { - setAgent(other.getAgent()); - } - if (other.hasResponseTime()) { - setResponseTime(other.getResponseTime()); - } - if (other.hasConnected()) { - setConnected(other.getConnected()); - } - if (other.hasDisconnected()) { - setDisconnected(other.getDisconnected()); - } - if (other.hasTrafficIn()) { - setTrafficIn(other.getTrafficIn()); - } - if (other.hasTrafficOut()) { - setTrafficOut(other.getTrafficOut()); - } - if (other.hasBanned()) { - setBanned(other.getBanned()); - } - if (other.hasBanReason()) { - setBanReason(other.getBanReason()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasStoreVersion()) { - - return false; - } - if (!hasAddress()) { - - return false; - } - if (!hasVersion()) { - - return false; - } - if (!hasServices()) { - - return false; - } - if (!hasHeight()) { - - return false; - } - if (!hasName()) { - - return false; - } - if (!hasAgent()) { - - return false; - } - if (!hasResponseTime()) { - - return false; - } - if (!hasConnected()) { - - return false; - } - if (!hasDisconnected()) { - - return false; - } - if (!hasTrafficIn()) { - - return false; - } - if (!hasTrafficOut()) { - - return false; - } - if (!hasBanned()) { - - return false; - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - storeVersion_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - address_ = input.readBytes(); - break; - } - case 24: { - bitField0_ |= 0x00000004; - version_ = input.readUInt32(); - break; - } - case 32: { - bitField0_ |= 0x00000008; - services_ = input.readUInt64(); - break; - } - case 40: { - bitField0_ |= 0x00000010; - height_ = input.readUInt32(); - break; - } - case 50: { - bitField0_ |= 0x00000020; - name_ = input.readBytes(); - break; - } - case 58: { - bitField0_ |= 0x00000040; - agent_ = input.readBytes(); - break; - } - case 64: { - bitField0_ |= 0x00000080; - responseTime_ = input.readUInt64(); - break; - } - case 72: { - bitField0_ |= 0x00000100; - connected_ = input.readUInt64(); - break; - } - case 80: { - bitField0_ |= 0x00000200; - disconnected_ = input.readUInt64(); - break; - } - case 88: { - bitField0_ |= 0x00000400; - trafficIn_ = input.readUInt64(); - break; - } - case 96: { - bitField0_ |= 0x00000800; - trafficOut_ = input.readUInt64(); - break; - } - case 104: { - bitField0_ |= 0x00001000; - banned_ = input.readUInt64(); - break; - } - case 114: { - bitField0_ |= 0x00002000; - banReason_ = input.readBytes(); - break; - } - } - } - } - - private int bitField0_; - - // required uint32 storeVersion = 1; - private int storeVersion_ ; - public boolean hasStoreVersion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getStoreVersion() { - return storeVersion_; - } - public Builder setStoreVersion(int value) { - bitField0_ |= 0x00000001; - storeVersion_ = value; - onChanged(); - return this; - } - public Builder clearStoreVersion() { - bitField0_ = (bitField0_ & ~0x00000001); - storeVersion_ = 0; - onChanged(); - return this; - } - - // required string address = 2; - private java.lang.Object address_ = ""; - public boolean hasAddress() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public String getAddress() { - java.lang.Object ref = address_; - if (!(ref instanceof String)) { - String s = ((com.google.protobuf.ByteString) ref).toStringUtf8(); - address_ = s; - return s; - } else { - return (String) ref; - } - } - public Builder setAddress(String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - address_ = value; - onChanged(); - return this; - } - public Builder clearAddress() { - bitField0_ = (bitField0_ & ~0x00000002); - address_ = getDefaultInstance().getAddress(); - onChanged(); - return this; - } - void setAddress(com.google.protobuf.ByteString value) { - bitField0_ |= 0x00000002; - address_ = value; - onChanged(); - } - - // required uint32 version = 3; - private int version_ ; - public boolean hasVersion() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public int getVersion() { - return version_; - } - public Builder setVersion(int value) { - bitField0_ |= 0x00000004; - version_ = value; - onChanged(); - return this; - } - public Builder clearVersion() { - bitField0_ = (bitField0_ & ~0x00000004); - version_ = 0; - onChanged(); - return this; - } - - // required uint64 services = 4; - private long services_ ; - public boolean hasServices() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public long getServices() { - return services_; - } - public Builder setServices(long value) { - bitField0_ |= 0x00000008; - services_ = value; - onChanged(); - return this; - } - public Builder clearServices() { - bitField0_ = (bitField0_ & ~0x00000008); - services_ = 0L; - onChanged(); - return this; - } - - // required uint32 height = 5; - private int height_ ; - public boolean hasHeight() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - public int getHeight() { - return height_; - } - public Builder setHeight(int value) { - bitField0_ |= 0x00000010; - height_ = value; - onChanged(); - return this; - } - public Builder clearHeight() { - bitField0_ = (bitField0_ & ~0x00000010); - height_ = 0; - onChanged(); - return this; - } - - // required string name = 6; - private java.lang.Object name_ = ""; - public boolean hasName() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - public String getName() { - java.lang.Object ref = name_; - if (!(ref instanceof String)) { - String s = ((com.google.protobuf.ByteString) ref).toStringUtf8(); - name_ = s; - return s; - } else { - return (String) ref; - } - } - public Builder setName(String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000020; - name_ = value; - onChanged(); - return this; - } - public Builder clearName() { - bitField0_ = (bitField0_ & ~0x00000020); - name_ = getDefaultInstance().getName(); - onChanged(); - return this; - } - void setName(com.google.protobuf.ByteString value) { - bitField0_ |= 0x00000020; - name_ = value; - onChanged(); - } - - // required string agent = 7; - private java.lang.Object agent_ = ""; - public boolean hasAgent() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - public String getAgent() { - java.lang.Object ref = agent_; - if (!(ref instanceof String)) { - String s = ((com.google.protobuf.ByteString) ref).toStringUtf8(); - agent_ = s; - return s; - } else { - return (String) ref; - } - } - public Builder setAgent(String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000040; - agent_ = value; - onChanged(); - return this; - } - public Builder clearAgent() { - bitField0_ = (bitField0_ & ~0x00000040); - agent_ = getDefaultInstance().getAgent(); - onChanged(); - return this; - } - void setAgent(com.google.protobuf.ByteString value) { - bitField0_ |= 0x00000040; - agent_ = value; - onChanged(); - } - - // required uint64 responseTime = 8; - private long responseTime_ ; - public boolean hasResponseTime() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - public long getResponseTime() { - return responseTime_; - } - public Builder setResponseTime(long value) { - bitField0_ |= 0x00000080; - responseTime_ = value; - onChanged(); - return this; - } - public Builder clearResponseTime() { - bitField0_ = (bitField0_ & ~0x00000080); - responseTime_ = 0L; - onChanged(); - return this; - } - - // required uint64 connected = 9; - private long connected_ ; - public boolean hasConnected() { - return ((bitField0_ & 0x00000100) == 0x00000100); - } - public long getConnected() { - return connected_; - } - public Builder setConnected(long value) { - bitField0_ |= 0x00000100; - connected_ = value; - onChanged(); - return this; - } - public Builder clearConnected() { - bitField0_ = (bitField0_ & ~0x00000100); - connected_ = 0L; - onChanged(); - return this; - } - - // required uint64 disconnected = 10; - private long disconnected_ ; - public boolean hasDisconnected() { - return ((bitField0_ & 0x00000200) == 0x00000200); - } - public long getDisconnected() { - return disconnected_; - } - public Builder setDisconnected(long value) { - bitField0_ |= 0x00000200; - disconnected_ = value; - onChanged(); - return this; - } - public Builder clearDisconnected() { - bitField0_ = (bitField0_ & ~0x00000200); - disconnected_ = 0L; - onChanged(); - return this; - } - - // required uint64 trafficIn = 11; - private long trafficIn_ ; - public boolean hasTrafficIn() { - return ((bitField0_ & 0x00000400) == 0x00000400); - } - public long getTrafficIn() { - return trafficIn_; - } - public Builder setTrafficIn(long value) { - bitField0_ |= 0x00000400; - trafficIn_ = value; - onChanged(); - return this; - } - public Builder clearTrafficIn() { - bitField0_ = (bitField0_ & ~0x00000400); - trafficIn_ = 0L; - onChanged(); - return this; - } - - // required uint64 trafficOut = 12; - private long trafficOut_ ; - public boolean hasTrafficOut() { - return ((bitField0_ & 0x00000800) == 0x00000800); - } - public long getTrafficOut() { - return trafficOut_; - } - public Builder setTrafficOut(long value) { - bitField0_ |= 0x00000800; - trafficOut_ = value; - onChanged(); - return this; - } - public Builder clearTrafficOut() { - bitField0_ = (bitField0_ & ~0x00000800); - trafficOut_ = 0L; - onChanged(); - return this; - } - - // required uint64 banned = 13; - private long banned_ ; - public boolean hasBanned() { - return ((bitField0_ & 0x00001000) == 0x00001000); - } - public long getBanned() { - return banned_; - } - public Builder setBanned(long value) { - bitField0_ |= 0x00001000; - banned_ = value; - onChanged(); - return this; - } - public Builder clearBanned() { - bitField0_ = (bitField0_ & ~0x00001000); - banned_ = 0L; - onChanged(); - return this; - } - - // optional string banReason = 14; - private java.lang.Object banReason_ = ""; - public boolean hasBanReason() { - return ((bitField0_ & 0x00002000) == 0x00002000); - } - public String getBanReason() { - java.lang.Object ref = banReason_; - if (!(ref instanceof String)) { - String s = ((com.google.protobuf.ByteString) ref).toStringUtf8(); - banReason_ = s; - return s; - } else { - return (String) ref; - } - } - public Builder setBanReason(String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00002000; - banReason_ = value; - onChanged(); - return this; - } - public Builder clearBanReason() { - bitField0_ = (bitField0_ & ~0x00002000); - banReason_ = getDefaultInstance().getBanReason(); - onChanged(); - return this; - } - void setBanReason(com.google.protobuf.ByteString value) { - bitField0_ |= 0x00002000; - banReason_ = value; - onChanged(); - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.model.PEER) - } - - static { - defaultInstance = new PEER(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.model.PEER) - } - - public interface COLOROrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required uint32 storeVersion = 1; - boolean hasStoreVersion(); - int getStoreVersion(); - - // required string fungibleName = 2; - boolean hasFungibleName(); - String getFungibleName(); - - // required bytes txHash = 3; - boolean hasTxHash(); - com.google.protobuf.ByteString getTxHash(); - - // required string terms = 4; - boolean hasTerms(); - String getTerms(); - - // required uint64 unit = 5; - boolean hasUnit(); - long getUnit(); - - // required uint32 expiryHeight = 6; - boolean hasExpiryHeight(); - int getExpiryHeight(); - - // required bytes signature = 7; - boolean hasSignature(); - com.google.protobuf.ByteString getSignature(); - - // required bytes pubkey = 8; - boolean hasPubkey(); - com.google.protobuf.ByteString getPubkey(); - } - public static final class COLOR extends - com.google.protobuf.GeneratedMessage - implements COLOROrBuilder { - // Use COLOR.newBuilder() to construct. - private COLOR(Builder builder) { - super(builder); - } - private COLOR(boolean noInit) {} - - private static final COLOR defaultInstance; - public static COLOR getDefaultInstance() { - return defaultInstance; - } - - public COLOR getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_COLOR_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_COLOR_fieldAccessorTable; - } - - private int bitField0_; - // required uint32 storeVersion = 1; - public static final int STOREVERSION_FIELD_NUMBER = 1; - private int storeVersion_; - public boolean hasStoreVersion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getStoreVersion() { - return storeVersion_; - } - - // required string fungibleName = 2; - public static final int FUNGIBLENAME_FIELD_NUMBER = 2; - private java.lang.Object fungibleName_; - public boolean hasFungibleName() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public String getFungibleName() { - java.lang.Object ref = fungibleName_; - if (ref instanceof String) { - return (String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - String s = bs.toStringUtf8(); - if (com.google.protobuf.Internal.isValidUtf8(bs)) { - fungibleName_ = s; - } - return s; - } - } - private com.google.protobuf.ByteString getFungibleNameBytes() { - java.lang.Object ref = fungibleName_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8((String) ref); - fungibleName_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // required bytes txHash = 3; - public static final int TXHASH_FIELD_NUMBER = 3; - private com.google.protobuf.ByteString txHash_; - public boolean hasTxHash() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public com.google.protobuf.ByteString getTxHash() { - return txHash_; - } - - // required string terms = 4; - public static final int TERMS_FIELD_NUMBER = 4; - private java.lang.Object terms_; - public boolean hasTerms() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public String getTerms() { - java.lang.Object ref = terms_; - if (ref instanceof String) { - return (String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - String s = bs.toStringUtf8(); - if (com.google.protobuf.Internal.isValidUtf8(bs)) { - terms_ = s; - } - return s; - } - } - private com.google.protobuf.ByteString getTermsBytes() { - java.lang.Object ref = terms_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8((String) ref); - terms_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - - // required uint64 unit = 5; - public static final int UNIT_FIELD_NUMBER = 5; - private long unit_; - public boolean hasUnit() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - public long getUnit() { - return unit_; - } - - // required uint32 expiryHeight = 6; - public static final int EXPIRYHEIGHT_FIELD_NUMBER = 6; - private int expiryHeight_; - public boolean hasExpiryHeight() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - public int getExpiryHeight() { - return expiryHeight_; - } - - // required bytes signature = 7; - public static final int SIGNATURE_FIELD_NUMBER = 7; - private com.google.protobuf.ByteString signature_; - public boolean hasSignature() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - public com.google.protobuf.ByteString getSignature() { - return signature_; - } - - // required bytes pubkey = 8; - public static final int PUBKEY_FIELD_NUMBER = 8; - private com.google.protobuf.ByteString pubkey_; - public boolean hasPubkey() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - public com.google.protobuf.ByteString getPubkey() { - return pubkey_; - } - - private void initFields() { - storeVersion_ = 0; - fungibleName_ = ""; - txHash_ = com.google.protobuf.ByteString.EMPTY; - terms_ = ""; - unit_ = 0L; - expiryHeight_ = 0; - signature_ = com.google.protobuf.ByteString.EMPTY; - pubkey_ = com.google.protobuf.ByteString.EMPTY; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasStoreVersion()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasFungibleName()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasTxHash()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasTerms()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasUnit()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasExpiryHeight()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasSignature()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasPubkey()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, storeVersion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeBytes(2, getFungibleNameBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeBytes(3, txHash_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeBytes(4, getTermsBytes()); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeUInt64(5, unit_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeUInt32(6, expiryHeight_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - output.writeBytes(7, signature_); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - output.writeBytes(8, pubkey_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, storeVersion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(2, getFungibleNameBytes()); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(3, txHash_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(4, getTermsBytes()); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(5, unit_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(6, expiryHeight_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(7, signature_); - } - if (((bitField0_ & 0x00000080) == 0x00000080)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(8, pubkey_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.model.LevelDBStore.COLOR parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.COLOR parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.COLOR parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.COLOR parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.COLOR parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.COLOR parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.COLOR parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.model.LevelDBStore.COLOR parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.model.LevelDBStore.COLOR parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.COLOR parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.model.LevelDBStore.COLOR prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.model.LevelDBStore.COLOROrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_COLOR_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_COLOR_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.model.LevelDBStore.COLOR.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - storeVersion_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - fungibleName_ = ""; - bitField0_ = (bitField0_ & ~0x00000002); - txHash_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000004); - terms_ = ""; - bitField0_ = (bitField0_ & ~0x00000008); - unit_ = 0L; - bitField0_ = (bitField0_ & ~0x00000010); - expiryHeight_ = 0; - bitField0_ = (bitField0_ & ~0x00000020); - signature_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000040); - pubkey_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000080); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.model.LevelDBStore.COLOR.getDescriptor(); - } - - public com.bitsofproof.supernode.model.LevelDBStore.COLOR getDefaultInstanceForType() { - return com.bitsofproof.supernode.model.LevelDBStore.COLOR.getDefaultInstance(); - } - - public com.bitsofproof.supernode.model.LevelDBStore.COLOR build() { - com.bitsofproof.supernode.model.LevelDBStore.COLOR result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.model.LevelDBStore.COLOR buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.model.LevelDBStore.COLOR result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.model.LevelDBStore.COLOR buildPartial() { - com.bitsofproof.supernode.model.LevelDBStore.COLOR result = new com.bitsofproof.supernode.model.LevelDBStore.COLOR(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.storeVersion_ = storeVersion_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.fungibleName_ = fungibleName_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.txHash_ = txHash_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.terms_ = terms_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - result.unit_ = unit_; - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000020; - } - result.expiryHeight_ = expiryHeight_; - if (((from_bitField0_ & 0x00000040) == 0x00000040)) { - to_bitField0_ |= 0x00000040; - } - result.signature_ = signature_; - if (((from_bitField0_ & 0x00000080) == 0x00000080)) { - to_bitField0_ |= 0x00000080; - } - result.pubkey_ = pubkey_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.model.LevelDBStore.COLOR) { - return mergeFrom((com.bitsofproof.supernode.model.LevelDBStore.COLOR)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.model.LevelDBStore.COLOR other) { - if (other == com.bitsofproof.supernode.model.LevelDBStore.COLOR.getDefaultInstance()) return this; - if (other.hasStoreVersion()) { - setStoreVersion(other.getStoreVersion()); - } - if (other.hasFungibleName()) { - setFungibleName(other.getFungibleName()); - } - if (other.hasTxHash()) { - setTxHash(other.getTxHash()); - } - if (other.hasTerms()) { - setTerms(other.getTerms()); - } - if (other.hasUnit()) { - setUnit(other.getUnit()); - } - if (other.hasExpiryHeight()) { - setExpiryHeight(other.getExpiryHeight()); - } - if (other.hasSignature()) { - setSignature(other.getSignature()); - } - if (other.hasPubkey()) { - setPubkey(other.getPubkey()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasStoreVersion()) { - - return false; - } - if (!hasFungibleName()) { - - return false; - } - if (!hasTxHash()) { - - return false; - } - if (!hasTerms()) { - - return false; - } - if (!hasUnit()) { - - return false; - } - if (!hasExpiryHeight()) { - - return false; - } - if (!hasSignature()) { - - return false; - } - if (!hasPubkey()) { - - return false; - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - storeVersion_ = input.readUInt32(); - break; - } - case 18: { - bitField0_ |= 0x00000002; - fungibleName_ = input.readBytes(); - break; - } - case 26: { - bitField0_ |= 0x00000004; - txHash_ = input.readBytes(); - break; - } - case 34: { - bitField0_ |= 0x00000008; - terms_ = input.readBytes(); - break; - } - case 40: { - bitField0_ |= 0x00000010; - unit_ = input.readUInt64(); - break; - } - case 48: { - bitField0_ |= 0x00000020; - expiryHeight_ = input.readUInt32(); - break; - } - case 58: { - bitField0_ |= 0x00000040; - signature_ = input.readBytes(); - break; - } - case 66: { - bitField0_ |= 0x00000080; - pubkey_ = input.readBytes(); - break; - } - } - } - } - - private int bitField0_; - - // required uint32 storeVersion = 1; - private int storeVersion_ ; - public boolean hasStoreVersion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getStoreVersion() { - return storeVersion_; - } - public Builder setStoreVersion(int value) { - bitField0_ |= 0x00000001; - storeVersion_ = value; - onChanged(); - return this; - } - public Builder clearStoreVersion() { - bitField0_ = (bitField0_ & ~0x00000001); - storeVersion_ = 0; - onChanged(); - return this; - } - - // required string fungibleName = 2; - private java.lang.Object fungibleName_ = ""; - public boolean hasFungibleName() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public String getFungibleName() { - java.lang.Object ref = fungibleName_; - if (!(ref instanceof String)) { - String s = ((com.google.protobuf.ByteString) ref).toStringUtf8(); - fungibleName_ = s; - return s; - } else { - return (String) ref; - } - } - public Builder setFungibleName(String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000002; - fungibleName_ = value; - onChanged(); - return this; - } - public Builder clearFungibleName() { - bitField0_ = (bitField0_ & ~0x00000002); - fungibleName_ = getDefaultInstance().getFungibleName(); - onChanged(); - return this; - } - void setFungibleName(com.google.protobuf.ByteString value) { - bitField0_ |= 0x00000002; - fungibleName_ = value; - onChanged(); - } - - // required bytes txHash = 3; - private com.google.protobuf.ByteString txHash_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasTxHash() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public com.google.protobuf.ByteString getTxHash() { - return txHash_; - } - public Builder setTxHash(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000004; - txHash_ = value; - onChanged(); - return this; - } - public Builder clearTxHash() { - bitField0_ = (bitField0_ & ~0x00000004); - txHash_ = getDefaultInstance().getTxHash(); - onChanged(); - return this; - } - - // required string terms = 4; - private java.lang.Object terms_ = ""; - public boolean hasTerms() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public String getTerms() { - java.lang.Object ref = terms_; - if (!(ref instanceof String)) { - String s = ((com.google.protobuf.ByteString) ref).toStringUtf8(); - terms_ = s; - return s; - } else { - return (String) ref; - } - } - public Builder setTerms(String value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000008; - terms_ = value; - onChanged(); - return this; - } - public Builder clearTerms() { - bitField0_ = (bitField0_ & ~0x00000008); - terms_ = getDefaultInstance().getTerms(); - onChanged(); - return this; - } - void setTerms(com.google.protobuf.ByteString value) { - bitField0_ |= 0x00000008; - terms_ = value; - onChanged(); - } - - // required uint64 unit = 5; - private long unit_ ; - public boolean hasUnit() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - public long getUnit() { - return unit_; - } - public Builder setUnit(long value) { - bitField0_ |= 0x00000010; - unit_ = value; - onChanged(); - return this; - } - public Builder clearUnit() { - bitField0_ = (bitField0_ & ~0x00000010); - unit_ = 0L; - onChanged(); - return this; - } - - // required uint32 expiryHeight = 6; - private int expiryHeight_ ; - public boolean hasExpiryHeight() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - public int getExpiryHeight() { - return expiryHeight_; - } - public Builder setExpiryHeight(int value) { - bitField0_ |= 0x00000020; - expiryHeight_ = value; - onChanged(); - return this; - } - public Builder clearExpiryHeight() { - bitField0_ = (bitField0_ & ~0x00000020); - expiryHeight_ = 0; - onChanged(); - return this; - } - - // required bytes signature = 7; - private com.google.protobuf.ByteString signature_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasSignature() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - public com.google.protobuf.ByteString getSignature() { - return signature_; - } - public Builder setSignature(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000040; - signature_ = value; - onChanged(); - return this; - } - public Builder clearSignature() { - bitField0_ = (bitField0_ & ~0x00000040); - signature_ = getDefaultInstance().getSignature(); - onChanged(); - return this; - } - - // required bytes pubkey = 8; - private com.google.protobuf.ByteString pubkey_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasPubkey() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - public com.google.protobuf.ByteString getPubkey() { - return pubkey_; - } - public Builder setPubkey(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000080; - pubkey_ = value; - onChanged(); - return this; - } - public Builder clearPubkey() { - bitField0_ = (bitField0_ & ~0x00000080); - pubkey_ = getDefaultInstance().getPubkey(); - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.model.COLOR) - } - - static { - defaultInstance = new COLOR(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.model.COLOR) - } - - public interface HEADOrBuilder - extends com.google.protobuf.MessageOrBuilder { - - // required uint32 storeVersion = 1; - boolean hasStoreVersion(); - int getStoreVersion(); - - // required uint64 id = 2; - boolean hasId(); - long getId(); - - // required uint64 chainWork = 3; - boolean hasChainWork(); - long getChainWork(); - - // required uint32 height = 4; - boolean hasHeight(); - int getHeight(); - - // required bytes leaf = 5; - boolean hasLeaf(); - com.google.protobuf.ByteString getLeaf(); - - // optional uint64 previousId = 6; - boolean hasPreviousId(); - long getPreviousId(); - - // optional uint32 previousHeight = 7; - boolean hasPreviousHeight(); - int getPreviousHeight(); - } - public static final class HEAD extends - com.google.protobuf.GeneratedMessage - implements HEADOrBuilder { - // Use HEAD.newBuilder() to construct. - private HEAD(Builder builder) { - super(builder); - } - private HEAD(boolean noInit) {} - - private static final HEAD defaultInstance; - public static HEAD getDefaultInstance() { - return defaultInstance; - } - - public HEAD getDefaultInstanceForType() { - return defaultInstance; - } - - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_HEAD_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_HEAD_fieldAccessorTable; - } - - private int bitField0_; - // required uint32 storeVersion = 1; - public static final int STOREVERSION_FIELD_NUMBER = 1; - private int storeVersion_; - public boolean hasStoreVersion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getStoreVersion() { - return storeVersion_; - } - - // required uint64 id = 2; - public static final int ID_FIELD_NUMBER = 2; - private long id_; - public boolean hasId() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public long getId() { - return id_; - } - - // required uint64 chainWork = 3; - public static final int CHAINWORK_FIELD_NUMBER = 3; - private long chainWork_; - public boolean hasChainWork() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public long getChainWork() { - return chainWork_; - } - - // required uint32 height = 4; - public static final int HEIGHT_FIELD_NUMBER = 4; - private int height_; - public boolean hasHeight() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public int getHeight() { - return height_; - } - - // required bytes leaf = 5; - public static final int LEAF_FIELD_NUMBER = 5; - private com.google.protobuf.ByteString leaf_; - public boolean hasLeaf() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - public com.google.protobuf.ByteString getLeaf() { - return leaf_; - } - - // optional uint64 previousId = 6; - public static final int PREVIOUSID_FIELD_NUMBER = 6; - private long previousId_; - public boolean hasPreviousId() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - public long getPreviousId() { - return previousId_; - } - - // optional uint32 previousHeight = 7; - public static final int PREVIOUSHEIGHT_FIELD_NUMBER = 7; - private int previousHeight_; - public boolean hasPreviousHeight() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - public int getPreviousHeight() { - return previousHeight_; - } - - private void initFields() { - storeVersion_ = 0; - id_ = 0L; - chainWork_ = 0L; - height_ = 0; - leaf_ = com.google.protobuf.ByteString.EMPTY; - previousId_ = 0L; - previousHeight_ = 0; - } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized != -1) return isInitialized == 1; - - if (!hasStoreVersion()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasId()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasChainWork()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasHeight()) { - memoizedIsInitialized = 0; - return false; - } - if (!hasLeaf()) { - memoizedIsInitialized = 0; - return false; - } - memoizedIsInitialized = 1; - return true; - } - - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - getSerializedSize(); - if (((bitField0_ & 0x00000001) == 0x00000001)) { - output.writeUInt32(1, storeVersion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - output.writeUInt64(2, id_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - output.writeUInt64(3, chainWork_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - output.writeUInt32(4, height_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - output.writeBytes(5, leaf_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - output.writeUInt64(6, previousId_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - output.writeUInt32(7, previousHeight_); - } - getUnknownFields().writeTo(output); - } - - private int memoizedSerializedSize = -1; - public int getSerializedSize() { - int size = memoizedSerializedSize; - if (size != -1) return size; - - size = 0; - if (((bitField0_ & 0x00000001) == 0x00000001)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(1, storeVersion_); - } - if (((bitField0_ & 0x00000002) == 0x00000002)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(2, id_); - } - if (((bitField0_ & 0x00000004) == 0x00000004)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(3, chainWork_); - } - if (((bitField0_ & 0x00000008) == 0x00000008)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(4, height_); - } - if (((bitField0_ & 0x00000010) == 0x00000010)) { - size += com.google.protobuf.CodedOutputStream - .computeBytesSize(5, leaf_); - } - if (((bitField0_ & 0x00000020) == 0x00000020)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt64Size(6, previousId_); - } - if (((bitField0_ & 0x00000040) == 0x00000040)) { - size += com.google.protobuf.CodedOutputStream - .computeUInt32Size(7, previousHeight_); - } - size += getUnknownFields().getSerializedSize(); - memoizedSerializedSize = size; - return size; - } - - private static final long serialVersionUID = 0L; - @java.lang.Override - protected java.lang.Object writeReplace() - throws java.io.ObjectStreamException { - return super.writeReplace(); - } - - public static com.bitsofproof.supernode.model.LevelDBStore.HEAD parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.HEAD parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.HEAD parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.HEAD parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return newBuilder().mergeFrom(data, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.HEAD parseFrom(java.io.InputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.HEAD parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.HEAD parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.model.LevelDBStore.HEAD parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Builder builder = newBuilder(); - if (builder.mergeDelimitedFrom(input, extensionRegistry)) { - return builder.buildParsed(); - } else { - return null; - } - } - public static com.bitsofproof.supernode.model.LevelDBStore.HEAD parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return newBuilder().mergeFrom(input).buildParsed(); - } - public static com.bitsofproof.supernode.model.LevelDBStore.HEAD parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return newBuilder().mergeFrom(input, extensionRegistry) - .buildParsed(); - } - - public static Builder newBuilder() { return Builder.create(); } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder(com.bitsofproof.supernode.model.LevelDBStore.HEAD prototype) { - return newBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { return newBuilder(this); } - - @java.lang.Override - protected Builder newBuilderForType( - com.google.protobuf.GeneratedMessage.BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - public static final class Builder extends - com.google.protobuf.GeneratedMessage.Builder - implements com.bitsofproof.supernode.model.LevelDBStore.HEADOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_HEAD_descriptor; - } - - protected com.google.protobuf.GeneratedMessage.FieldAccessorTable - internalGetFieldAccessorTable() { - return com.bitsofproof.supernode.model.LevelDBStore.internal_static_com_bitsofproof_supernode_model_HEAD_fieldAccessorTable; - } - - // Construct using com.bitsofproof.supernode.model.LevelDBStore.HEAD.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } - - private Builder(BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { - } - } - private static Builder create() { - return new Builder(); - } - - public Builder clear() { - super.clear(); - storeVersion_ = 0; - bitField0_ = (bitField0_ & ~0x00000001); - id_ = 0L; - bitField0_ = (bitField0_ & ~0x00000002); - chainWork_ = 0L; - bitField0_ = (bitField0_ & ~0x00000004); - height_ = 0; - bitField0_ = (bitField0_ & ~0x00000008); - leaf_ = com.google.protobuf.ByteString.EMPTY; - bitField0_ = (bitField0_ & ~0x00000010); - previousId_ = 0L; - bitField0_ = (bitField0_ & ~0x00000020); - previousHeight_ = 0; - bitField0_ = (bitField0_ & ~0x00000040); - return this; - } - - public Builder clone() { - return create().mergeFrom(buildPartial()); - } - - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return com.bitsofproof.supernode.model.LevelDBStore.HEAD.getDescriptor(); - } - - public com.bitsofproof.supernode.model.LevelDBStore.HEAD getDefaultInstanceForType() { - return com.bitsofproof.supernode.model.LevelDBStore.HEAD.getDefaultInstance(); - } - - public com.bitsofproof.supernode.model.LevelDBStore.HEAD build() { - com.bitsofproof.supernode.model.LevelDBStore.HEAD result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } - - private com.bitsofproof.supernode.model.LevelDBStore.HEAD buildParsed() - throws com.google.protobuf.InvalidProtocolBufferException { - com.bitsofproof.supernode.model.LevelDBStore.HEAD result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException( - result).asInvalidProtocolBufferException(); - } - return result; - } - - public com.bitsofproof.supernode.model.LevelDBStore.HEAD buildPartial() { - com.bitsofproof.supernode.model.LevelDBStore.HEAD result = new com.bitsofproof.supernode.model.LevelDBStore.HEAD(this); - int from_bitField0_ = bitField0_; - int to_bitField0_ = 0; - if (((from_bitField0_ & 0x00000001) == 0x00000001)) { - to_bitField0_ |= 0x00000001; - } - result.storeVersion_ = storeVersion_; - if (((from_bitField0_ & 0x00000002) == 0x00000002)) { - to_bitField0_ |= 0x00000002; - } - result.id_ = id_; - if (((from_bitField0_ & 0x00000004) == 0x00000004)) { - to_bitField0_ |= 0x00000004; - } - result.chainWork_ = chainWork_; - if (((from_bitField0_ & 0x00000008) == 0x00000008)) { - to_bitField0_ |= 0x00000008; - } - result.height_ = height_; - if (((from_bitField0_ & 0x00000010) == 0x00000010)) { - to_bitField0_ |= 0x00000010; - } - result.leaf_ = leaf_; - if (((from_bitField0_ & 0x00000020) == 0x00000020)) { - to_bitField0_ |= 0x00000020; - } - result.previousId_ = previousId_; - if (((from_bitField0_ & 0x00000040) == 0x00000040)) { - to_bitField0_ |= 0x00000040; - } - result.previousHeight_ = previousHeight_; - result.bitField0_ = to_bitField0_; - onBuilt(); - return result; - } - - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof com.bitsofproof.supernode.model.LevelDBStore.HEAD) { - return mergeFrom((com.bitsofproof.supernode.model.LevelDBStore.HEAD)other); - } else { - super.mergeFrom(other); - return this; - } - } - - public Builder mergeFrom(com.bitsofproof.supernode.model.LevelDBStore.HEAD other) { - if (other == com.bitsofproof.supernode.model.LevelDBStore.HEAD.getDefaultInstance()) return this; - if (other.hasStoreVersion()) { - setStoreVersion(other.getStoreVersion()); - } - if (other.hasId()) { - setId(other.getId()); - } - if (other.hasChainWork()) { - setChainWork(other.getChainWork()); - } - if (other.hasHeight()) { - setHeight(other.getHeight()); - } - if (other.hasLeaf()) { - setLeaf(other.getLeaf()); - } - if (other.hasPreviousId()) { - setPreviousId(other.getPreviousId()); - } - if (other.hasPreviousHeight()) { - setPreviousHeight(other.getPreviousHeight()); - } - this.mergeUnknownFields(other.getUnknownFields()); - return this; - } - - public final boolean isInitialized() { - if (!hasStoreVersion()) { - - return false; - } - if (!hasId()) { - - return false; - } - if (!hasChainWork()) { - - return false; - } - if (!hasHeight()) { - - return false; - } - if (!hasLeaf()) { - - return false; - } - return true; - } - - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder( - this.getUnknownFields()); - while (true) { - int tag = input.readTag(); - switch (tag) { - case 0: - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - default: { - if (!parseUnknownField(input, unknownFields, - extensionRegistry, tag)) { - this.setUnknownFields(unknownFields.build()); - onChanged(); - return this; - } - break; - } - case 8: { - bitField0_ |= 0x00000001; - storeVersion_ = input.readUInt32(); - break; - } - case 16: { - bitField0_ |= 0x00000002; - id_ = input.readUInt64(); - break; - } - case 24: { - bitField0_ |= 0x00000004; - chainWork_ = input.readUInt64(); - break; - } - case 32: { - bitField0_ |= 0x00000008; - height_ = input.readUInt32(); - break; - } - case 42: { - bitField0_ |= 0x00000010; - leaf_ = input.readBytes(); - break; - } - case 48: { - bitField0_ |= 0x00000020; - previousId_ = input.readUInt64(); - break; - } - case 56: { - bitField0_ |= 0x00000040; - previousHeight_ = input.readUInt32(); - break; - } - } - } - } - - private int bitField0_; - - // required uint32 storeVersion = 1; - private int storeVersion_ ; - public boolean hasStoreVersion() { - return ((bitField0_ & 0x00000001) == 0x00000001); - } - public int getStoreVersion() { - return storeVersion_; - } - public Builder setStoreVersion(int value) { - bitField0_ |= 0x00000001; - storeVersion_ = value; - onChanged(); - return this; - } - public Builder clearStoreVersion() { - bitField0_ = (bitField0_ & ~0x00000001); - storeVersion_ = 0; - onChanged(); - return this; - } - - // required uint64 id = 2; - private long id_ ; - public boolean hasId() { - return ((bitField0_ & 0x00000002) == 0x00000002); - } - public long getId() { - return id_; - } - public Builder setId(long value) { - bitField0_ |= 0x00000002; - id_ = value; - onChanged(); - return this; - } - public Builder clearId() { - bitField0_ = (bitField0_ & ~0x00000002); - id_ = 0L; - onChanged(); - return this; - } - - // required uint64 chainWork = 3; - private long chainWork_ ; - public boolean hasChainWork() { - return ((bitField0_ & 0x00000004) == 0x00000004); - } - public long getChainWork() { - return chainWork_; - } - public Builder setChainWork(long value) { - bitField0_ |= 0x00000004; - chainWork_ = value; - onChanged(); - return this; - } - public Builder clearChainWork() { - bitField0_ = (bitField0_ & ~0x00000004); - chainWork_ = 0L; - onChanged(); - return this; - } - - // required uint32 height = 4; - private int height_ ; - public boolean hasHeight() { - return ((bitField0_ & 0x00000008) == 0x00000008); - } - public int getHeight() { - return height_; - } - public Builder setHeight(int value) { - bitField0_ |= 0x00000008; - height_ = value; - onChanged(); - return this; - } - public Builder clearHeight() { - bitField0_ = (bitField0_ & ~0x00000008); - height_ = 0; - onChanged(); - return this; - } - - // required bytes leaf = 5; - private com.google.protobuf.ByteString leaf_ = com.google.protobuf.ByteString.EMPTY; - public boolean hasLeaf() { - return ((bitField0_ & 0x00000010) == 0x00000010); - } - public com.google.protobuf.ByteString getLeaf() { - return leaf_; - } - public Builder setLeaf(com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - bitField0_ |= 0x00000010; - leaf_ = value; - onChanged(); - return this; - } - public Builder clearLeaf() { - bitField0_ = (bitField0_ & ~0x00000010); - leaf_ = getDefaultInstance().getLeaf(); - onChanged(); - return this; - } - - // optional uint64 previousId = 6; - private long previousId_ ; - public boolean hasPreviousId() { - return ((bitField0_ & 0x00000020) == 0x00000020); - } - public long getPreviousId() { - return previousId_; - } - public Builder setPreviousId(long value) { - bitField0_ |= 0x00000020; - previousId_ = value; - onChanged(); - return this; - } - public Builder clearPreviousId() { - bitField0_ = (bitField0_ & ~0x00000020); - previousId_ = 0L; - onChanged(); - return this; - } - - // optional uint32 previousHeight = 7; - private int previousHeight_ ; - public boolean hasPreviousHeight() { - return ((bitField0_ & 0x00000040) == 0x00000040); - } - public int getPreviousHeight() { - return previousHeight_; - } - public Builder setPreviousHeight(int value) { - bitField0_ |= 0x00000040; - previousHeight_ = value; - onChanged(); - return this; - } - public Builder clearPreviousHeight() { - bitField0_ = (bitField0_ & ~0x00000040); - previousHeight_ = 0; - onChanged(); - return this; - } - - // @@protoc_insertion_point(builder_scope:com.bitsofproof.supernode.model.HEAD) - } - - static { - defaultInstance = new HEAD(true); - defaultInstance.initFields(); - } - - // @@protoc_insertion_point(class_scope:com.bitsofproof.supernode.model.HEAD) - } - - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_model_TX_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_model_TX_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_model_TX_TXIN_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_model_TX_TXIN_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_model_TX_TXOUT_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_model_TX_TXOUT_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_model_BLOCK_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_model_BLOCK_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_model_PEER_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_model_PEER_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_model_COLOR_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_model_COLOR_fieldAccessorTable; - private static com.google.protobuf.Descriptors.Descriptor - internal_static_com_bitsofproof_supernode_model_HEAD_descriptor; - private static - com.google.protobuf.GeneratedMessage.FieldAccessorTable - internal_static_com_bitsofproof_supernode_model_HEAD_fieldAccessorTable; - - public static com.google.protobuf.Descriptors.FileDescriptor - getDescriptor() { - return descriptor; - } - private static com.google.protobuf.Descriptors.FileDescriptor - descriptor; - static { - java.lang.String[] descriptorData = { - "\n main/protobuf/LevelDBStore.proto\022\037com." + - "bitsofproof.supernode.model\"\222\003\n\002TX\022\024\n\014st" + - "oreVersion\030\001 \002(\r\022\021\n\tblockHash\030\002 \002(\014\022\014\n\004h" + - "ash\030\003 \002(\014\022\017\n\007version\030\004 \002(\r\022\020\n\010lockTime\030\005" + - " \002(\r\022\n\n\002ix\030\006 \002(\r\0226\n\004txin\030\007 \003(\0132(.com.bit" + - "sofproof.supernode.model.TX.TXIN\0228\n\005txou" + - "t\030\010 \003(\0132).com.bitsofproof.supernode.mode" + - "l.TX.TXOUT\032H\n\004TXIN\022\n\n\002ix\030\001 \002(\r\022\022\n\nsource" + - "Hash\030\002 \002(\014\022\016\n\006script\030\003 \002(\014\022\020\n\010sequence\030\004" + - " \002(\r\032j\n\005TXOUT\022\r\n\005value\030\001 \002(\004\022\016\n\006script\030\002", - " \002(\014\022\016\n\006height\030\003 \002(\r\022\020\n\010coinbase\030\004 \002(\010\022\021" + - "\n\tavailable\030\005 \002(\010\022\r\n\005color\030\006 \001(\t\"\224\002\n\005BLO" + - "CK\022\024\n\014storeVersion\030\001 \002(\r\022\014\n\004hash\030\002 \002(\014\022\016" + - "\n\006height\030\003 \002(\r\022\017\n\007version\030\004 \002(\r\022\024\n\014previ" + - "ousHash\030\005 \002(\014\022\022\n\nmerkleRoot\030\006 \002(\014\022\022\n\ncre" + - "ateTime\030\007 \002(\r\022\030\n\020difficultyTarget\030\010 \002(\r\022" + - "\r\n\005nonce\030\t \002(\r\022\021\n\tchainWork\030\n \002(\004\022\016\n\006hea" + - "dId\030\013 \002(\004\022\021\n\tfilterMap\030\014 \002(\014\022\027\n\017filterFu" + - "nctions\030\r \002(\r\022\020\n\010txHashes\030\016 \003(\014\"\206\002\n\004PEER" + - "\022\024\n\014storeVersion\030\001 \002(\r\022\017\n\007address\030\002 \002(\t\022", - "\017\n\007version\030\003 \002(\r\022\020\n\010services\030\004 \002(\004\022\016\n\006he" + - "ight\030\005 \002(\r\022\014\n\004name\030\006 \002(\t\022\r\n\005agent\030\007 \002(\t\022" + - "\024\n\014responseTime\030\010 \002(\004\022\021\n\tconnected\030\t \002(\004" + - "\022\024\n\014disconnected\030\n \002(\004\022\021\n\ttrafficIn\030\013 \002(" + - "\004\022\022\n\ntrafficOut\030\014 \002(\004\022\016\n\006banned\030\r \002(\004\022\021\n" + - "\tbanReason\030\016 \001(\t\"\231\001\n\005COLOR\022\024\n\014storeVersi" + - "on\030\001 \002(\r\022\024\n\014fungibleName\030\002 \002(\t\022\016\n\006txHash" + - "\030\003 \002(\014\022\r\n\005terms\030\004 \002(\t\022\014\n\004unit\030\005 \002(\004\022\024\n\014e" + - "xpiryHeight\030\006 \002(\r\022\021\n\tsignature\030\007 \002(\014\022\016\n\006" + - "pubkey\030\010 \002(\014\"\205\001\n\004HEAD\022\024\n\014storeVersion\030\001 ", - "\002(\r\022\n\n\002id\030\002 \002(\004\022\021\n\tchainWork\030\003 \002(\004\022\016\n\006he" + - "ight\030\004 \002(\r\022\014\n\004leaf\030\005 \002(\014\022\022\n\npreviousId\030\006" + - " \001(\004\022\026\n\016previousHeight\030\007 \001(\r" - }; - com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = - new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { - public com.google.protobuf.ExtensionRegistry assignDescriptors( - com.google.protobuf.Descriptors.FileDescriptor root) { - descriptor = root; - internal_static_com_bitsofproof_supernode_model_TX_descriptor = - getDescriptor().getMessageTypes().get(0); - internal_static_com_bitsofproof_supernode_model_TX_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_model_TX_descriptor, - new java.lang.String[] { "StoreVersion", "BlockHash", "Hash", "Version", "LockTime", "Ix", "Txin", "Txout", }, - com.bitsofproof.supernode.model.LevelDBStore.TX.class, - com.bitsofproof.supernode.model.LevelDBStore.TX.Builder.class); - internal_static_com_bitsofproof_supernode_model_TX_TXIN_descriptor = - internal_static_com_bitsofproof_supernode_model_TX_descriptor.getNestedTypes().get(0); - internal_static_com_bitsofproof_supernode_model_TX_TXIN_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_model_TX_TXIN_descriptor, - new java.lang.String[] { "Ix", "SourceHash", "Script", "Sequence", }, - com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN.class, - com.bitsofproof.supernode.model.LevelDBStore.TX.TXIN.Builder.class); - internal_static_com_bitsofproof_supernode_model_TX_TXOUT_descriptor = - internal_static_com_bitsofproof_supernode_model_TX_descriptor.getNestedTypes().get(1); - internal_static_com_bitsofproof_supernode_model_TX_TXOUT_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_model_TX_TXOUT_descriptor, - new java.lang.String[] { "Value", "Script", "Height", "Coinbase", "Available", "Color", }, - com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT.class, - com.bitsofproof.supernode.model.LevelDBStore.TX.TXOUT.Builder.class); - internal_static_com_bitsofproof_supernode_model_BLOCK_descriptor = - getDescriptor().getMessageTypes().get(1); - internal_static_com_bitsofproof_supernode_model_BLOCK_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_model_BLOCK_descriptor, - new java.lang.String[] { "StoreVersion", "Hash", "Height", "Version", "PreviousHash", "MerkleRoot", "CreateTime", "DifficultyTarget", "Nonce", "ChainWork", "HeadId", "FilterMap", "FilterFunctions", "TxHashes", }, - com.bitsofproof.supernode.model.LevelDBStore.BLOCK.class, - com.bitsofproof.supernode.model.LevelDBStore.BLOCK.Builder.class); - internal_static_com_bitsofproof_supernode_model_PEER_descriptor = - getDescriptor().getMessageTypes().get(2); - internal_static_com_bitsofproof_supernode_model_PEER_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_model_PEER_descriptor, - new java.lang.String[] { "StoreVersion", "Address", "Version", "Services", "Height", "Name", "Agent", "ResponseTime", "Connected", "Disconnected", "TrafficIn", "TrafficOut", "Banned", "BanReason", }, - com.bitsofproof.supernode.model.LevelDBStore.PEER.class, - com.bitsofproof.supernode.model.LevelDBStore.PEER.Builder.class); - internal_static_com_bitsofproof_supernode_model_COLOR_descriptor = - getDescriptor().getMessageTypes().get(3); - internal_static_com_bitsofproof_supernode_model_COLOR_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_model_COLOR_descriptor, - new java.lang.String[] { "StoreVersion", "FungibleName", "TxHash", "Terms", "Unit", "ExpiryHeight", "Signature", "Pubkey", }, - com.bitsofproof.supernode.model.LevelDBStore.COLOR.class, - com.bitsofproof.supernode.model.LevelDBStore.COLOR.Builder.class); - internal_static_com_bitsofproof_supernode_model_HEAD_descriptor = - getDescriptor().getMessageTypes().get(4); - internal_static_com_bitsofproof_supernode_model_HEAD_fieldAccessorTable = new - com.google.protobuf.GeneratedMessage.FieldAccessorTable( - internal_static_com_bitsofproof_supernode_model_HEAD_descriptor, - new java.lang.String[] { "StoreVersion", "Id", "ChainWork", "Height", "Leaf", "PreviousId", "PreviousHeight", }, - com.bitsofproof.supernode.model.LevelDBStore.HEAD.class, - com.bitsofproof.supernode.model.LevelDBStore.HEAD.Builder.class); - return null; - } - }; - com.google.protobuf.Descriptors.FileDescriptor - .internalBuildGeneratedFileFrom(descriptorData, - new com.google.protobuf.Descriptors.FileDescriptor[] { - }, assigner); - } - - // @@protoc_insertion_point(outer_class_scope) -} diff --git a/server/src/main/protobuf/LevelDBStore.proto b/server/src/main/proto/LevelDBStore.proto similarity index 100% rename from server/src/main/protobuf/LevelDBStore.proto rename to server/src/main/proto/LevelDBStore.proto From 1bb3883b95cae57de188ee6822edf05c8c5657ed Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sun, 30 Jun 2013 13:07:31 +0200 Subject: [PATCH 110/299] do not validate if slave(under no circumstances) --- .../java/com/bitsofproof/supernode/core/CachedBlockStore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 7303499a..26bc4988 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -989,7 +989,7 @@ private void lockedStoreBlock (Blk b) throws ValidationException } } - if ( !chain.isSlave () && !chain.isProduction () || b.getHeight () > lastCheckPoint || chain.checkBeforeCheckpoint () ) + if ( !chain.isSlave () && (!chain.isProduction () || b.getHeight () > lastCheckPoint || chain.checkBeforeCheckpoint ()) ) { log.trace ("validating block " + b.getHash ()); From 8c864ed86683a451505787c4f783e6fc9c6f5a9a Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Tue, 9 Jul 2013 14:03:35 +0200 Subject: [PATCH 111/299] fixes Issue #37 --- .../supernode/common/ScriptFormat.java | 15 +++++++++++++-- .../supernode/test/TransactionTest.java | 9 +++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/common/ScriptFormat.java b/api/src/main/java/com/bitsofproof/supernode/common/ScriptFormat.java index d48f629d..2c03974e 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/ScriptFormat.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/ScriptFormat.java @@ -22,7 +22,6 @@ import java.util.List; import java.util.StringTokenizer; - public class ScriptFormat { // unfortunatelly unused: https://bitcointalk.org/index.php?topic=120836.0 @@ -281,7 +280,7 @@ else if ( data.length <= 0xffff ) else if ( data.length <= 0x7fffffff ) { writeByte (Opcode.OP_PUSHDATA4.o); - writeInt16 (data.length); + writeInt32 (data.length); writeBytes (data); } } @@ -293,6 +292,18 @@ public void writeToken (Token token) { try { + if ( token.op.o == Opcode.OP_PUSHDATA1.o ) + { + writeByte (token.data.length); + } + if ( token.op.o == Opcode.OP_PUSHDATA2.o ) + { + writeInt16 (token.data.length); + } + if ( token.op.o == Opcode.OP_PUSHDATA4.o ) + { + writeInt32 (token.data.length); + } s.write (token.data); } catch ( IOException e ) diff --git a/server/src/test/java/com/bitsofproof/supernode/test/TransactionTest.java b/server/src/test/java/com/bitsofproof/supernode/test/TransactionTest.java index c99f2975..03191ce0 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/TransactionTest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/TransactionTest.java @@ -549,5 +549,14 @@ public ValidationException call () throws Exception t1.getInputs ().get (0).setSource (t2.getOutputs ().get (1)); assertTrue (new ScriptEvaluation (t1, 0).evaluate (true)); + + // multisig using invalid keys stored with PUSHDATA1, first seen in block 244029 + t2 = + Tx.fromWireDump ("0100000001dbb3197449c0ede76729d75fdf845d1b4e367770211a0b30305e17ba7fa57f41010000006b483045022100b6db6e71652d2dcaa818c24d7dcba0ddca9a9a09f45a5a8b175784dde494d78602204e4b1042564da546b032f09eb02d2211f9e8fa3aeb3c987a28962a0a7a6d312b0121037a380cf3628417c275e9010eea2d7b2a05337d38af25054675e3441a43aa4565ffffffff01a0bb0d0000000000fd1901514c78ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4c78ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2102323c909b017748294c1d1fb82648b2c2905cd941d9a863e036b3157e8aa85dd353ae00000000"); + + t1 = + Tx.fromWireDump ("0100000001106f0a9fdee8618002cdda338341cedcef81023bfde09d2832d8895c443c9bc4000000004b004930460221008bcf597d5d6a7b2ccf3913983a2d978953e745fa6431331539ded8dc5cbd9f1402210093a4375f9d880fe506e3abcec15a919718e5946b21f823c6d35b539a2386b82701ffffffff0100350c00000000001976a914c676ea066cc2a6b41b0ace8e287fd38030f0b97c88ac00000000"); + + assertTrue (new ScriptEvaluation (t1, 0, t2.getOutputs ().get (0)).evaluate (true)); } } From 044bc3d7d1214e8ccb1433457a63c4f9ee727760 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 11 Jul 2013 16:49:15 +0200 Subject: [PATCH 112/299] do not hardcode protobuf compiler path. use java 7 --- api/pom.xml | 4 +--- pom.xml | 2 +- server/pom.xml | 8 +++----- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 4b319cba..49cbdd0a 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -18,9 +18,7 @@ com.google.protobuf.tools maven-protoc-plugin - - /usr/local/bin/protoc - + 0.1.10 diff --git a/pom.xml b/pom.xml index 7d83e93b..ab7acd9c 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ 1.16-bop-patch 1.0 1.6.6 - 1.6 + 1.7 1.48 1.1.2.RELEASE 2.9.0 diff --git a/server/pom.xml b/server/pom.xml index cc262a80..3f894c8b 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -211,8 +211,8 @@ maven-compiler-plugin 2.3.2 - 1.6 - 1.6 + 1.7 + 1.7 com.mysema.query.apt.jpa.JPAAnnotationProcessor @@ -229,9 +229,7 @@ com.google.protobuf.tools maven-protoc-plugin - - /usr/local/bin/protoc - + 0.1.10 From 8af5379fec07f71a1e9bae19b6e4182476589c4f Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 11 Jul 2013 18:15:04 +0200 Subject: [PATCH 113/299] use oracle7 in travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a8a6873d..044d1cca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,4 +4,4 @@ before_install: - sudo apt-get install protobuf-compiler jdk: - - openjdk6 + - oraclejdk7 From d85e19286af9c383d5697e3e501c88765876608f Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 11 Jul 2013 18:18:01 +0200 Subject: [PATCH 114/299] use java 7 protobuf 2.5 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 23245932..9a1c08bc 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ _Use our ready to go hosted BOP Server instance! Get an evaluation access, attra Build the Community Server -------------------------- -Make sure you have Maven3, JDK 1.6 or 7 and Google protobuf compiler 2.4.1 installed. +Make sure you have Maven3, JDK 7 and Google protobuf compiler 2.5 installed. git clone https://github.com/bitsofproof/supernode From 5edc6af11fa8476b6fe532e9877e1918001745e8 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 11 Jul 2013 18:24:53 +0200 Subject: [PATCH 115/299] Revert "do not hardcode protobuf compiler path. use java 7" This reverts commit 044bc3d7d1214e8ccb1433457a63c4f9ee727760. --- api/pom.xml | 4 +++- pom.xml | 2 +- server/pom.xml | 8 +++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 49cbdd0a..4b319cba 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -18,7 +18,9 @@ com.google.protobuf.tools maven-protoc-plugin - 0.1.10 + + /usr/local/bin/protoc + diff --git a/pom.xml b/pom.xml index ab7acd9c..7d83e93b 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ 1.16-bop-patch 1.0 1.6.6 - 1.7 + 1.6 1.48 1.1.2.RELEASE 2.9.0 diff --git a/server/pom.xml b/server/pom.xml index 3f894c8b..cc262a80 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -211,8 +211,8 @@ maven-compiler-plugin 2.3.2 - 1.7 - 1.7 + 1.6 + 1.6 com.mysema.query.apt.jpa.JPAAnnotationProcessor @@ -229,7 +229,9 @@ com.google.protobuf.tools maven-protoc-plugin - 0.1.10 + + /usr/local/bin/protoc + From d818cc6ffbb2bc2565e06694764aca59f0308222 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 11 Jul 2013 18:30:01 +0200 Subject: [PATCH 116/299] Revert "Revert "do not hardcode protobuf compiler path. use java 7"" This reverts commit 5edc6af11fa8476b6fe532e9877e1918001745e8. --- api/pom.xml | 4 +--- pom.xml | 2 +- server/pom.xml | 8 +++----- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 4b319cba..49cbdd0a 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -18,9 +18,7 @@ com.google.protobuf.tools maven-protoc-plugin - - /usr/local/bin/protoc - + 0.1.10 diff --git a/pom.xml b/pom.xml index 7d83e93b..ab7acd9c 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ 1.16-bop-patch 1.0 1.6.6 - 1.6 + 1.7 1.48 1.1.2.RELEASE 2.9.0 diff --git a/server/pom.xml b/server/pom.xml index cc262a80..3f894c8b 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -211,8 +211,8 @@ maven-compiler-plugin 2.3.2 - 1.6 - 1.6 + 1.7 + 1.7 com.mysema.query.apt.jpa.JPAAnnotationProcessor @@ -229,9 +229,7 @@ com.google.protobuf.tools maven-protoc-plugin - - /usr/local/bin/protoc - + 0.1.10 From 84db53779543a8efc51ca4edaea69d7a80ba08d8 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 11 Jul 2013 18:56:39 +0200 Subject: [PATCH 117/299] protobuf 2.5 is not yet available as ubuntu package therefore does not work in travis. reverting to 2.4.1 --- README.md | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9a1c08bc..432f0f8d 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ _Use our ready to go hosted BOP Server instance! Get an evaluation access, attra Build the Community Server -------------------------- -Make sure you have Maven3, JDK 7 and Google protobuf compiler 2.5 installed. +Make sure you have Maven3, JDK 7 and Google protobuf compiler 2.4.1 installed. git clone https://github.com/bitsofproof/supernode diff --git a/pom.xml b/pom.xml index ab7acd9c..f8ed7029 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 3.6.9.Final 9.1-901.jdbc4 1.6.1 - 2.5.0 + 2.4.1 4.3.0.Final 20090211 1.2 From ab8484d4edbc98ae8e498eb2aaba899c0f9ac7d1 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 13 Jul 2013 14:58:08 +0200 Subject: [PATCH 118/299] remove Bloom filtered listener --- .../com/bitsofproof/supernode/api/BCSAPI.java | 17 ---- .../supernode/api/JMSServerConnector.java | 88 ------------------- 2 files changed, 105 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java index 598f3e76..bd342573 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java @@ -155,21 +155,4 @@ public interface BCSAPI * @throws BCSAPIException */ public void scanTransactions (ExtendedKey master, int lookAhead, long after, TransactionListener listener) throws BCSAPIException; - - /** - * register listener with a Bloom filter - * - * @param filter - * @param listener - * @throws BCSAPIException - */ - public void registerFilteredListener (BloomFilter filter, TransactionListener listener) throws BCSAPIException; - - /** - * remove the listener of a Bloom filter - * - * @param filter - * @param listener - */ - public void removeFilteredListener (BloomFilter filter, TransactionListener listener); } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java index d23484e5..d36ccc9d 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java @@ -66,7 +66,6 @@ public class JMSServerConnector implements BCSAPI private MessageProducer transactionRequestProducer; private MessageProducer colorProducer; private MessageProducer colorRequestProducer; - private MessageProducer filterRequestProducer; private MessageProducer scanRequestProducer; private MessageProducer scanAccountProducer; private MessageProducer exactMatchProducer; @@ -248,7 +247,6 @@ public void init () blockRequestProducer = session.createProducer (session.createQueue ("blockRequest")); blockHeaderRequestProducer = session.createProducer (session.createQueue ("headerRequest")); transactionRequestProducer = session.createProducer (session.createQueue ("transactionRequest")); - filterRequestProducer = session.createProducer (session.createQueue ("filterRequest")); scanRequestProducer = session.createProducer (session.createQueue ("scanRequest")); exactMatchProducer = session.createProducer (session.createQueue ("matchRequest")); scanAccountProducer = session.createProducer (session.createQueue ("accountRequest")); @@ -568,92 +566,6 @@ public void onMessage (Message message) } - @Override - public void registerFilteredListener (final BloomFilter filter, final TransactionListener listener) throws BCSAPIException - { - try - { - BytesMessage m = session.createBytesMessage (); - BCSAPIMessage.FilterRequest.Builder builder = BCSAPIMessage.FilterRequest.newBuilder (); - builder.setBcsapiversion (1); - builder.setFilter (ByteString.copyFrom (filter.getFilter ())); - builder.setHashFunctions ((int) filter.getHashFunctions ()); - builder.setTweak ((int) filter.getTweak ()); - builder.setMode (filter.getUpdateMode ().ordinal ()); - - m.writeBytes (builder.build ().toByteArray ()); - synchronized ( messageDispatcher ) - { - MessageDispatcher dispatcher = messageDispatcher.get (filter.toString ()); - if ( dispatcher == null ) - { - TemporaryQueue answerQueue = session.createTemporaryQueue (); - MessageConsumer consumer = session.createConsumer (answerQueue); - dispatcher = new MessageDispatcher (consumer); - dispatcher.setTemporaryQueue (answerQueue); - messageDispatcher.put (filter.toString (), dispatcher); - dispatcher.addListener (listener, new ByteArrayMessageListener () - { - @Override - public void onMessage (byte[] body) - { - try - { - if ( body != null ) - { - Transaction t = Transaction.fromProtobuf (BCSAPIMessage.Transaction.parseFrom (body)); - t.computeHash (); - listener.process (t); - } - else - { - listener.process (null); - } - } - catch ( InvalidProtocolBufferException e ) - { - log.error ("Malformed message received for filter", e); - } - } - }); - } - m.setJMSCorrelationID (listener.toString ()); - m.setJMSReplyTo (dispatcher.getTemporaryQueue ()); - } - - filterRequestProducer.send (m); - } - catch ( JMSException e ) - { - throw new BCSAPIException (e); - } - } - - @Override - public void removeFilteredListener (BloomFilter filter, TransactionListener listener) - { - synchronized ( messageDispatcher ) - { - MessageDispatcher dispatcher = messageDispatcher.get (filter.toString ()); - if ( dispatcher != null ) - { - dispatcher.removeListener (listener); - if ( !dispatcher.isListened () ) - { - messageDispatcher.remove (filter.toString ()); - try - { - dispatcher.getConsumer ().close (); - dispatcher.getTemporaryQueue ().delete (); - } - catch ( JMSException e ) - { - } - } - } - } - } - @Override public void registerTransactionListener (final TransactionListener listener) throws BCSAPIException { From f259fc1995cecb7f8054fc5c1633c2eca599a8cb Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 13 Jul 2013 15:01:30 +0200 Subject: [PATCH 119/299] remove color related api --- .../supernode/api/JMSServerConnector.java | 59 ------------------- 1 file changed, 59 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java index d36ccc9d..15a2e4c4 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java @@ -64,8 +64,6 @@ public class JMSServerConnector implements BCSAPI private MessageProducer blockRequestProducer; private MessageProducer blockHeaderRequestProducer; private MessageProducer transactionRequestProducer; - private MessageProducer colorProducer; - private MessageProducer colorRequestProducer; private MessageProducer scanRequestProducer; private MessageProducer scanAccountProducer; private MessageProducer exactMatchProducer; @@ -909,61 +907,4 @@ public void sendBlock (Block block) throws BCSAPIException } } - public void issueColor (Color color) throws BCSAPIException - { - try - { - log.trace ("issue color " + color.getTerms ()); - BytesMessage m = session.createBytesMessage (); - m.writeBytes (color.toProtobuf ().toByteArray ()); - byte[] reply = synchronousRequest (colorProducer, m); - if ( reply != null ) - { - try - { - BCSAPIMessage.ExceptionMessage em = BCSAPIMessage.ExceptionMessage.parseFrom (reply); - throw new BCSAPIException (em.getMessage (0)); - } - catch ( InvalidProtocolBufferException e ) - { - throw new BCSAPIException ("Invalid response", e); - } - } - } - catch ( JMSException e ) - { - throw new BCSAPIException (e); - } - } - - public Color getColor (String digest) throws BCSAPIException - { - log.trace ("get color " + digest); - BytesMessage m; - try - { - m = session.createBytesMessage (); - BCSAPIMessage.Hash.Builder builder = BCSAPIMessage.Hash.newBuilder (); - builder.setBcsapiversion (1); - builder.addHash (ByteString.copyFrom (new Hash (digest).toByteArray ())); - m.writeBytes (builder.build ().toByteArray ()); - byte[] response = synchronousRequest (colorRequestProducer, m); - if ( response != null ) - { - Color c; - c = Color.fromProtobuf (BCSAPIMessage.Color.parseFrom (response)); - return c; - } - } - catch ( JMSException e ) - { - throw new BCSAPIException (e); - } - catch ( InvalidProtocolBufferException e ) - { - throw new BCSAPIException (e); - } - return null; - } - } From 5cf4d8c110747b4274398816d4654f5ad5620353 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 13 Jul 2013 17:50:38 +0200 Subject: [PATCH 120/299] refactored JMS to follow spec. --- .../supernode/api/JMSServerConnector.java | 283 +++++++------- .../common/InMemoryBusConnectionFactory.java | 127 ++++--- .../supernode/core/ImplementBCSAPI.java | 355 +++++++----------- 3 files changed, 349 insertions(+), 416 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java index 15a2e4c4..a206ba5a 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java @@ -21,10 +21,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; -import java.util.concurrent.Exchanger; import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import javax.jms.BytesMessage; import javax.jms.Connection; @@ -35,7 +32,6 @@ import javax.jms.MessageConsumer; import javax.jms.MessageListener; import javax.jms.MessageProducer; -import javax.jms.Queue; import javax.jms.Session; import javax.jms.TemporaryQueue; @@ -54,20 +50,9 @@ public class JMSServerConnector implements BCSAPI private ConnectionFactory connectionFactory; private Connection connection; - private Session session; private String clientId = UUID.randomUUID ().toString (); - private MessageProducer pingProducer; - private MessageProducer transactionProducer; - private MessageProducer blockProducer; - private MessageProducer blockRequestProducer; - private MessageProducer blockHeaderRequestProducer; - private MessageProducer transactionRequestProducer; - private MessageProducer scanRequestProducer; - private MessageProducer scanAccountProducer; - private MessageProducer exactMatchProducer; - private Boolean production = null; private final Map messageDispatcher = new HashMap (); @@ -89,7 +74,6 @@ private class MessageDispatcher private final Map wrapperMap = new HashMap (); private final MessageConsumer consumer; - private TemporaryQueue temporaryQueue; public MessageDispatcher (MessageConsumer consumer) { @@ -168,17 +152,6 @@ public MessageConsumer getConsumer () { return consumer; } - - public TemporaryQueue getTemporaryQueue () - { - return temporaryQueue; - } - - public void setTemporaryQueue (TemporaryQueue temporaryQueue) - { - this.temporaryQueue = temporaryQueue; - } - } public void setClientId (String clientId) @@ -198,6 +171,7 @@ private void addTopicListener (String topic, Object inner, ByteArrayMessageListe MessageDispatcher dispatcher = messageDispatcher.get (topic); if ( dispatcher == null ) { + Session session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); Destination destination = session.createTopic (topic); MessageConsumer consumer = session.createConsumer (destination); messageDispatcher.put (topic, dispatcher = new MessageDispatcher (consumer)); @@ -237,17 +211,6 @@ public void init () connection = connectionFactory.createConnection (); connection.setClientID (clientId); connection.start (); - session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); - - transactionProducer = session.createProducer (session.createTopic ("newTransaction")); - blockProducer = session.createProducer (session.createTopic ("newBlock")); - pingProducer = session.createProducer (session.createQueue ("ping")); - blockRequestProducer = session.createProducer (session.createQueue ("blockRequest")); - blockHeaderRequestProducer = session.createProducer (session.createQueue ("headerRequest")); - transactionRequestProducer = session.createProducer (session.createQueue ("transactionRequest")); - scanRequestProducer = session.createProducer (session.createQueue ("scanRequest")); - exactMatchProducer = session.createProducer (session.createQueue ("matchRequest")); - scanAccountProducer = session.createProducer (session.createQueue ("accountRequest")); } catch ( Exception e ) { @@ -259,7 +222,6 @@ public void destroy () { try { - session.close (); connection.close (); } catch ( JMSException e ) @@ -270,15 +232,19 @@ public void destroy () @Override public long ping (long nonce) throws BCSAPIException { + Session session = null; try { log.trace ("ping " + nonce); + + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); BytesMessage m = session.createBytesMessage (); BCSAPIMessage.Ping.Builder builder = BCSAPIMessage.Ping.newBuilder (); builder.setBcsapiversion (1); builder.setNonce (nonce); m.writeBytes (builder.build ().toByteArray ()); - byte[] response = synchronousRequest (pingProducer, m); + MessageProducer pingProducer = session.createProducer (session.createQueue ("ping")); + byte[] response = synchronousRequest (session, pingProducer, m); if ( response != null ) { BCSAPIMessage.Ping echo = BCSAPIMessage.Ping.parseFrom (response); @@ -297,6 +263,16 @@ public long ping (long nonce) throws BCSAPIException { throw new BCSAPIException (e); } + finally + { + try + { + session.close (); + } + catch ( JMSException e ) + { + } + } return 0; } @@ -348,10 +324,13 @@ public boolean isProduction () throws BCSAPIException @Override public void scanTransactions (Collection match, UpdateMode mode, long after, final TransactionListener listener) throws BCSAPIException { + Session session = null; try { + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); BytesMessage m = session.createBytesMessage (); + MessageProducer exactMatchProducer = session.createProducer (session.createQueue ("matchRequest")); BCSAPIMessage.ExactMatchRequest.Builder builder = BCSAPIMessage.ExactMatchRequest.newBuilder (); builder.setBcsapiversion (1); builder.setMode (mode.ordinal ()); @@ -404,26 +383,32 @@ public void onMessage (Message message) }); exactMatchProducer.send (m); - if ( ready.tryAcquire (timeout, TimeUnit.MILLISECONDS) == false ) - { - throw new BCSAPIException ("timeout"); - } + ready.acquireUninterruptibly (); } catch ( JMSException e ) { throw new BCSAPIException (e); } - catch ( InterruptedException e ) + finally { - throw new BCSAPIException (e); + try + { + session.close (); + } + catch ( JMSException e ) + { + } } } @Override public void scanTransactions (BloomFilter filter, final TransactionListener listener) throws BCSAPIException { + Session session = null; try { + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); + MessageProducer scanRequestProducer = session.createProducer (session.createQueue ("scanRequest")); BytesMessage m = session.createBytesMessage (); BCSAPIMessage.FilterRequest.Builder builder = BCSAPIMessage.FilterRequest.newBuilder (); @@ -475,18 +460,21 @@ public void onMessage (Message message) }); scanRequestProducer.send (m); - if ( ready.tryAcquire (timeout, TimeUnit.MILLISECONDS) == false ) - { - throw new BCSAPIException ("timeout"); - } + ready.acquireUninterruptibly (); } catch ( JMSException e ) { throw new BCSAPIException (e); } - catch ( InterruptedException e ) + finally { - throw new BCSAPIException (e); + try + { + session.close (); + } + catch ( JMSException e ) + { + } } } @@ -497,10 +485,13 @@ public void scanTransactions (ExtendedKey master, int lookAhead, long after, fin { master = master.getReadOnly (); } + Session session = null; try { + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); BytesMessage m = session.createBytesMessage (); + MessageProducer scanAccountProducer = session.createProducer (session.createQueue ("accountRequest")); BCSAPIMessage.AccountRequest.Builder builder = BCSAPIMessage.AccountRequest.newBuilder (); builder.setBcsapiversion (1); builder.setPublicKey (master.serialize (production)); @@ -548,20 +539,22 @@ public void onMessage (Message message) }); scanAccountProducer.send (m); - if ( ready.tryAcquire (timeout, TimeUnit.MILLISECONDS) == false ) - { - throw new BCSAPIException ("timeout"); - } + ready.acquireUninterruptibly (); } catch ( JMSException e ) { throw new BCSAPIException (e); } - catch ( InterruptedException e ) + finally { - throw new BCSAPIException (e); + try + { + session.close (); + } + catch ( JMSException e ) + { + } } - } @Override @@ -647,94 +640,26 @@ public void removeTrunkListener (TrunkListener listener) removeTopicListener ("trunk", listener); } - private byte[] synchronousRequest (MessageProducer producer, Message m) throws BCSAPIException + private byte[] synchronousRequest (Session session, MessageProducer producer, Message m) throws BCSAPIException { TemporaryQueue answerQueue = null; - try - { - try - { - answerQueue = session.createTemporaryQueue (); - } - catch ( JMSException e ) - { - return null; - } - return synchronousRequest (producer, m, answerQueue); - } - finally - { - try - { - answerQueue.delete (); - } - catch ( JMSException e ) - { - } - } - } - - private byte[] synchronousRequest (MessageProducer producer, Message m, Queue answerQueue) throws BCSAPIException - { - byte[] result = null; - final Exchanger exchanger = new Exchanger (); - MessageConsumer consumer = null; - try { + answerQueue = session.createTemporaryQueue (); m.setJMSReplyTo (answerQueue); consumer = session.createConsumer (answerQueue); - consumer.setMessageListener (new MessageListener () - { - @Override - public void onMessage (Message message) - { - BytesMessage m = (BytesMessage) message; - byte[] body; - try - { - if ( m.getBodyLength () == 0 ) - { - try - { - exchanger.exchange (null); - } - catch ( InterruptedException e ) - { - } - } - else - { - body = new byte[(int) m.getBodyLength ()]; - m.readBytes (body); - try - { - exchanger.exchange (body); - } - catch ( InterruptedException e ) - { - } - } - } - catch ( JMSException e ) - { - log.debug ("Can not parse reply", e); - } - } - }); producer.send (m); - try - { - result = exchanger.exchange (null, timeout, TimeUnit.MILLISECONDS); - consumer.close (); - } - catch ( InterruptedException e ) + BytesMessage reply = (BytesMessage) consumer.receive (timeout); + if ( reply.getBodyLength () == 0 ) { + return null; } - catch ( TimeoutException e ) + else { - throw new BCSAPIException ("timeout"); + byte[] body = new byte[(int) reply.getBodyLength ()]; + reply.readBytes (body); + return body; } } catch ( JMSException e ) @@ -749,12 +674,12 @@ public void onMessage (Message message) { consumer.close (); } + answerQueue.delete (); } catch ( JMSException e ) { } } - return result; } @Override @@ -762,14 +687,18 @@ public Transaction getTransaction (String hash) throws BCSAPIException { log.trace ("get transaction " + hash); BytesMessage m; + Session session = null; try { + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); + MessageProducer transactionRequestProducer = session.createProducer (session.createQueue ("transactionRequest")); + m = session.createBytesMessage (); BCSAPIMessage.Hash.Builder builder = BCSAPIMessage.Hash.newBuilder (); builder.setBcsapiversion (1); builder.addHash (ByteString.copyFrom (new Hash (hash).toByteArray ())); m.writeBytes (builder.build ().toByteArray ()); - byte[] response = synchronousRequest (transactionRequestProducer, m); + byte[] response = synchronousRequest (session, transactionRequestProducer, m); if ( response != null ) { Transaction t; @@ -786,21 +715,36 @@ public Transaction getTransaction (String hash) throws BCSAPIException { throw new BCSAPIException (e); } + finally + { + try + { + session.close (); + } + catch ( JMSException e ) + { + } + } return null; } @Override public Block getBlock (String hash) throws BCSAPIException { + Session session = null; try { log.trace ("get block " + hash); + + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); + MessageProducer blockRequestProducer = session.createProducer (session.createQueue ("blockRequest")); + BytesMessage m = session.createBytesMessage (); BCSAPIMessage.Hash.Builder builder = BCSAPIMessage.Hash.newBuilder (); builder.setBcsapiversion (1); builder.addHash (ByteString.copyFrom (new Hash (hash).toByteArray ())); m.writeBytes (builder.build ().toByteArray ()); - byte[] response = synchronousRequest (blockRequestProducer, m); + byte[] response = synchronousRequest (session, blockRequestProducer, m); if ( response != null ) { Block b = Block.fromProtobuf (BCSAPIMessage.Block.parseFrom (response)); @@ -816,21 +760,36 @@ public Block getBlock (String hash) throws BCSAPIException { throw new BCSAPIException (e); } + finally + { + try + { + session.close (); + } + catch ( JMSException e ) + { + } + } return null; } @Override public Block getBlockHeader (String hash) throws BCSAPIException { + Session session = null; try { log.trace ("get block header" + hash); + + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); + MessageProducer blockHeaderRequestProducer = session.createProducer (session.createQueue ("headerRequest")); + BytesMessage m = session.createBytesMessage (); BCSAPIMessage.Hash.Builder builder = BCSAPIMessage.Hash.newBuilder (); builder.setBcsapiversion (1); builder.addHash (ByteString.copyFrom (new Hash (hash).toByteArray ())); m.writeBytes (builder.build ().toByteArray ()); - byte[] response = synchronousRequest (blockHeaderRequestProducer, m); + byte[] response = synchronousRequest (session, blockHeaderRequestProducer, m); if ( response != null ) { Block b = Block.fromProtobuf (BCSAPIMessage.Block.parseFrom (response)); @@ -846,19 +805,34 @@ public Block getBlockHeader (String hash) throws BCSAPIException { throw new BCSAPIException (e); } + finally + { + try + { + session.close (); + } + catch ( JMSException e ) + { + } + } return null; } @Override public void sendTransaction (Transaction transaction) throws BCSAPIException { + Session session = null; try { transaction.computeHash (); log.trace ("send transaction " + transaction.getHash ()); + + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); + MessageProducer transactionProducer = session.createProducer (session.createTopic ("newTransaction")); + BytesMessage m = session.createBytesMessage (); m.writeBytes (transaction.toProtobuf ().toByteArray ()); - byte[] reply = synchronousRequest (transactionProducer, m); + byte[] reply = synchronousRequest (session, transactionProducer, m); if ( reply != null ) { try @@ -876,18 +850,32 @@ public void sendTransaction (Transaction transaction) throws BCSAPIException { throw new BCSAPIException (e); } + finally + { + try + { + session.close (); + } + catch ( JMSException e ) + { + } + } } @Override public void sendBlock (Block block) throws BCSAPIException { + Session session = null; try { block.computeHash (); log.trace ("send block " + block.getHash ()); + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); + MessageProducer blockProducer = session.createProducer (session.createTopic ("newBlock")); + BytesMessage m = session.createBytesMessage (); m.writeBytes (block.toProtobuf ().toByteArray ()); - byte[] reply = synchronousRequest (blockProducer, m); + byte[] reply = synchronousRequest (session, blockProducer, m); if ( reply != null ) { try @@ -905,6 +893,15 @@ public void sendBlock (Block block) throws BCSAPIException { throw new BCSAPIException (e); } + finally + { + try + { + session.close (); + } + catch ( JMSException e ) + { + } + } } - } diff --git a/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java b/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java index 92e679b8..47c66d95 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java @@ -54,8 +54,6 @@ public class InMemoryBusConnectionFactory implements ConnectionFactory { - private final MockSession singleSession = new MockSession (); - private static class MockBytesMessage implements BytesMessage { Destination destination; @@ -528,8 +526,6 @@ public void delete () throws JMSException } private static final Map> consumer = new HashMap> (); - private static Executor consumerExecutor = Executors.newFixedThreadPool (4); - private static final Map producer = new HashMap (); private static class MockProducer implements MessageProducer { @@ -609,64 +605,49 @@ public void close () throws JMSException @Override public void send (Message message) throws JMSException { - List cl = consumer.get (name); - if ( cl != null ) - { - for ( MockConsumer c : cl ) - { - c.getQueue ().add (message); - consumerExecutor.execute (c); - } - } + sendToConsumer (message); } @Override public void send (Message message, int deliveryMode, int priority, long timeToLive) throws JMSException { - List cl = consumer.get (name); - if ( cl != null ) - { - for ( MockConsumer c : cl ) - { - c.getQueue ().add (message); - consumerExecutor.execute (c); - } - } + sendToConsumer (message); } @Override public void send (Destination destination, Message message) throws JMSException { - List cl = consumer.get (name); - if ( cl != null ) - { - for ( MockConsumer c : cl ) - { - c.getQueue ().add (message); - consumerExecutor.execute (c); - } - } + sendToConsumer (message); } @Override public void send (Destination destination, Message message, int deliveryMode, int priority, long timeToLive) throws JMSException { - List cl = consumer.get (name); - if ( cl != null ) + sendToConsumer (message); + } + + private void sendToConsumer (Message message) + { + synchronized ( consumer ) { - for ( MockConsumer c : cl ) + List cl = consumer.get (name); + if ( cl != null ) { - c.getQueue ().add (message); - consumerExecutor.execute (c); + for ( MockConsumer c : cl ) + { + c.getQueue ().add (message); + } } } } } + private static Executor consumerExecutor = Executors.newCachedThreadPool (); + private static class MockConsumer implements MessageConsumer, Runnable { private final LinkedBlockingQueue queue = new LinkedBlockingQueue (); - private MessageListener listener = null; + private boolean open = true; public LinkedBlockingQueue getQueue () { @@ -686,9 +667,26 @@ public MessageListener getMessageListener () throws JMSException } @Override - public void setMessageListener (MessageListener listener) throws JMSException + public void setMessageListener (final MessageListener listener) throws JMSException { - this.listener = listener; + consumerExecutor.execute (new Runnable () + { + @Override + public void run () + { + while ( open ) + { + try + { + Message m = receive (); + listener.onMessage (m); + } + catch ( JMSException e ) + { + } + } + } + }); } @Override @@ -726,24 +724,20 @@ public Message receiveNoWait () throws JMSException @Override public void close () throws JMSException { + open = false; } @Override public void run () { - try - { - Message m = receive (); - listener.onMessage (m); - } - catch ( JMSException e ) - { - } + } } private static class MockSession implements Session { + private final List sessionConsumer = new ArrayList (); + @Override public BytesMessage createBytesMessage () throws JMSException { @@ -817,6 +811,10 @@ public void rollback () throws JMSException @Override public void close () throws JMSException { + for ( MessageConsumer consumer : sessionConsumer ) + { + consumer.close (); + } } @Override @@ -857,14 +855,7 @@ public MessageProducer createProducer (Destination destination) throws JMSExcept name = ((MockTemporaryQueue) destination).getQueueName (); } - if ( producer.containsKey (name) ) - { - return producer.get (name); - } - - MockProducer p = new MockProducer (name); - producer.put (name, p); - return p; + return new MockProducer (name); } @Override @@ -883,14 +874,18 @@ public MessageConsumer createConsumer (Destination destination) throws JMSExcept { name = ((MockTemporaryQueue) destination).getQueueName (); } - MockConsumer c = new MockConsumer (); - ArrayList cl = consumer.get (name); - if ( cl == null ) + final MockConsumer c = new MockConsumer (); + sessionConsumer.add (c); + synchronized ( consumer ) { - cl = new ArrayList (); - consumer.put (name, cl); + ArrayList cl = consumer.get (name); + if ( cl == null ) + { + cl = new ArrayList (); + consumer.put (name, cl); + } + cl.add (c); } - cl.add (c); return c; } @@ -962,10 +957,14 @@ public void unsubscribe (String name) throws JMSException private class MockConnection implements Connection { + private final List sessions = new ArrayList (); + @Override public Session createSession (boolean transacted, int acknowledgeMode) throws JMSException { - return singleSession; + Session session = new MockSession (); + sessions.add (session); + return session; } @Override @@ -1009,6 +1008,10 @@ public void stop () throws JMSException @Override public void close () throws JMSException { + for ( Session session : sessions ) + { + session.close (); + } } @Override diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index 10a78f96..a6dc8d0c 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -41,12 +41,10 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; -import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import com.bitsofproof.supernode.api.BCSAPIMessage; import com.bitsofproof.supernode.api.Block; -import com.bitsofproof.supernode.api.Color; import com.bitsofproof.supernode.api.ExtendedKey; import com.bitsofproof.supernode.api.Transaction; import com.bitsofproof.supernode.api.TrunkUpdateMessage; @@ -59,7 +57,6 @@ import com.bitsofproof.supernode.core.BlockStore.TransactionProcessor; import com.bitsofproof.supernode.messages.BlockMessage; import com.bitsofproof.supernode.model.Blk; -import com.bitsofproof.supernode.model.StoredColor; import com.bitsofproof.supernode.model.Tx; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; @@ -77,10 +74,7 @@ public class ImplementBCSAPI implements TrunkListener, TxListener private ConnectionFactory connectionFactory; private Connection connection; - private Session session; - private MessageProducer transactionProducer; - private MessageProducer trunkProducer; private final Map correlationProducer = new HashMap (); private final Map correlationBloomFilter = Collections.synchronizedMap (new HashMap ()); @@ -106,15 +100,58 @@ public void setConnectionFactory (ConnectionFactory connectionFactory) this.connectionFactory = connectionFactory; } - private void addTopicListener (String topic, MessageListener listener) throws JMSException + private abstract static class SessionMessageListener implements MessageListener { + private Session session; + + protected void reply (Destination destination, byte[] msg) + { + MessageProducer replier = null; + try + { + replier = session.createProducer (destination); + BytesMessage m = session.createBytesMessage (); + if ( msg != null ) + { + m.writeBytes (msg); + } + replier.send (m); + } + catch ( JMSException e ) + { + log.trace ("can not reply", e); + } + finally + { + try + { + replier.close (); + } + catch ( JMSException e ) + { + } + } + } + + public void setSession (Session session) + { + this.session = session; + } + } + + private void addTopicListener (String topic, SessionMessageListener listener) throws JMSException + { + Session session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); + listener.setSession (session); Destination destination = session.createTopic (topic); MessageConsumer consumer = session.createConsumer (destination); consumer.setMessageListener (listener); } - private void addQueueListener (String queue, MessageListener listener) throws JMSException + private void addQueueListener (String queue, SessionMessageListener listener) throws JMSException { + Session session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); + listener.setSession (session); Destination destination = session.createQueue (queue); MessageConsumer consumer = session.createConsumer (destination); consumer.setMessageListener (listener); @@ -127,15 +164,11 @@ public void init () connection = connectionFactory.createConnection (); connection.setClientID ("bitsofproof supernode"); connection.start (); - session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); - transactionProducer = session.createProducer (session.createTopic ("transaction")); - trunkProducer = session.createProducer (session.createTopic ("trunk")); addNewTransactionListener (); addNewBlockListener (); addBlockrequestListener (); addBlockHeaderRequestListener (); addTransactionRequestListener (); - addBloomFilterListener (); addBloomScanListener (); addMatchScanListener (); addPingListener (); @@ -147,56 +180,30 @@ public void init () } } - private void addBloomFilterListener () throws JMSException + private void closeSession (Session session) { - addQueueListener ("filterRequest", new MessageListener () + if ( session != null ) { - @Override - public void onMessage (Message msg) + try { - BytesMessage o = (BytesMessage) msg; - byte[] body; - try - { - body = new byte[(int) o.getBodyLength ()]; - o.readBytes (body); - BCSAPIMessage.FilterRequest request = BCSAPIMessage.FilterRequest.parseFrom (body); - byte[] data = request.getFilter ().toByteArray (); - long hashFunctions = request.getHashFunctions (); - long tweak = request.getTweak (); - UpdateMode updateMode = UpdateMode.values ()[request.getMode ()]; - BloomFilter filter = new BloomFilter (data, hashFunctions, tweak, updateMode); - synchronized ( correlationBloomFilter ) - { - if ( !correlationProducer.containsKey (o.getJMSCorrelationID ()) ) - { - MessageProducer producer = session.createProducer (msg.getJMSReplyTo ()); - correlationProducer.put (o.getJMSCorrelationID (), producer); - } - correlationBloomFilter.put (o.getJMSCorrelationID (), filter); - } - } - catch ( JMSException e ) - { - log.error ("invalid filter request", e); - } - catch ( InvalidProtocolBufferException e ) - { - log.error ("invalid filter request", e); - } + session.close (); } - }); + catch ( JMSException e ) + { + } + } } private void addBloomScanListener () throws JMSException { - addQueueListener ("scanRequest", new MessageListener () + addQueueListener ("scanRequest", new SessionMessageListener () { @Override public void onMessage (Message msg) { BytesMessage o = (BytesMessage) msg; byte[] body; + Session session = null; try { body = new byte[(int) o.getBodyLength ()]; @@ -207,6 +214,7 @@ public void onMessage (Message msg) long tweak = request.getTweak (); UpdateMode updateMode = UpdateMode.values ()[request.getMode ()]; final BloomFilter filter = new BloomFilter (data, hashFunctions, tweak, updateMode); + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); final MessageProducer producer = session.createProducer (msg.getJMSReplyTo ()); requestProcessor.execute (new Runnable () { @@ -220,31 +228,32 @@ public void run () @Override public void process (Tx tx) { - if ( tx != null ) + Session session = null; + try { - Transaction transaction = toBCSAPITransaction (tx, false); - BytesMessage m; - try + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); + + if ( tx != null ) { + Transaction transaction = toBCSAPITransaction (tx, false); + BytesMessage m; m = session.createBytesMessage (); m.writeBytes (transaction.toProtobuf ().toByteArray ()); producer.send (m); } - catch ( JMSException e ) - { - } - } - else - { - try + else { BytesMessage m = session.createBytesMessage (); producer.send (m); // indicate EOF producer.close (); } - catch ( JMSException e ) - { - } + } + catch ( JMSException e ) + { + } + finally + { + closeSession (session); } } }); @@ -264,19 +273,24 @@ public void process (Tx tx) { log.error ("invalid filter request", e); } + finally + { + closeSession (session); + } } }); } private void addMatchScanListener () throws JMSException { - addQueueListener ("matchRequest", new MessageListener () + addQueueListener ("matchRequest", new SessionMessageListener () { @Override public void onMessage (Message msg) { BytesMessage o = (BytesMessage) msg; byte[] body; + Session session = null; try { body = new byte[(int) o.getBodyLength ()]; @@ -288,6 +302,7 @@ public void onMessage (Message msg) match.add (new ByteVector (bs.toByteArray ())); } final UpdateMode mode = UpdateMode.values ()[request.getMode ()]; + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); final MessageProducer producer = session.createProducer (msg.getJMSReplyTo ()); final long after = request.hasAfter () ? request.getAfter () : 0; requestProcessor.execute (new Runnable () @@ -306,8 +321,10 @@ public void process (Tx tx) { Transaction transaction = toBCSAPITransaction (tx, false); BytesMessage m; + Session session = null; try { + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); m = session.createBytesMessage (); m.writeBytes (transaction.toProtobuf ().toByteArray ()); producer.send (m); @@ -315,14 +332,20 @@ public void process (Tx tx) catch ( JMSException e ) { } + finally + { + closeSession (session); + } } } }; store.filterTransactions (match, mode, after, processor); txhandler.scanUnconfirmedPool (match, mode, processor); + Session session = null; try { + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); BytesMessage m = session.createBytesMessage (); producer.send (m); // indicate EOF producer.close (); @@ -330,6 +353,10 @@ public void process (Tx tx) catch ( JMSException e ) { } + finally + { + closeSession (session); + } } catch ( ValidationException e ) { @@ -346,23 +373,29 @@ public void process (Tx tx) { log.error ("invalid filter request", e); } + finally + { + closeSession (session); + } } }); } private void addAccountScanListener () throws JMSException { - addQueueListener ("accountRequest", new MessageListener () + addQueueListener ("accountRequest", new SessionMessageListener () { @Override public void onMessage (Message msg) { BytesMessage o = (BytesMessage) msg; byte[] body; + Session session = null; try { body = new byte[(int) o.getBodyLength ()]; o.readBytes (body); + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); BCSAPIMessage.AccountRequest request = BCSAPIMessage.AccountRequest.parseFrom (body); final ExtendedKey ek = ExtendedKey.parse (request.getPublicKey ()); final int lookAhead = request.getLookAhead (); @@ -386,8 +419,10 @@ public void process (Tx tx) { Transaction transaction = toBCSAPITransaction (tx, false); BytesMessage m; + Session session = null; try { + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); m = session.createBytesMessage (); m.writeBytes (transaction.toProtobuf ().toByteArray ()); producer.send (m); @@ -395,14 +430,20 @@ public void process (Tx tx) catch ( JMSException e ) { } + finally + { + closeSession (session); + } } } }; store.filterTransactions (match, ek, lookAhead, after, processor); txhandler.scanUnconfirmedPool (match, mode, processor); + Session session = null; try { + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); BytesMessage m = session.createBytesMessage (); producer.send (m); // indicate EOF producer.close (); @@ -410,6 +451,10 @@ public void process (Tx tx) catch ( JMSException e ) { } + finally + { + closeSession (session); + } } catch ( ValidationException e ) { @@ -430,78 +475,9 @@ public void process (Tx tx) { log.error ("Invalid scan account request", e); } - } - }); - } - - private void addNewColorListener () throws JMSException - { - addTopicListener ("newColor", new MessageListener () - { - @Override - public void onMessage (Message arg0) - { - BytesMessage o = (BytesMessage) arg0; - try + finally { - byte[] body = new byte[(int) o.getBodyLength ()]; - o.readBytes (body); - Color color = Color.fromProtobuf (BCSAPIMessage.Color.parseFrom (body)); - StoredColor sc = new StoredColor (); - sc.setFungibleName (color.getFungibleName ()); - sc.setExpiryHeight (color.getExpiryHeight ()); - sc.setPubkey (color.getPubkey ()); - sc.setSignature (color.getSignature ()); - sc.setTerms (color.getTerms ()); - sc.setUnit (color.getUnit ()); - sc.setTxHash (color.getTransaction ()); - store.issueColor (sc); - reply (o.getJMSReplyTo (), null); - } - catch ( Exception e ) - { - BCSAPIMessage.ExceptionMessage.Builder builder = BCSAPIMessage.ExceptionMessage.newBuilder (); - builder.setBcsapiversion (1); - builder.addMessage (e.getMessage ()); - try - { - reply (o.getJMSReplyTo (), builder.build ().toByteArray ()); - } - catch ( JMSException e1 ) - { - log.error ("Can not send reply ", e1); - } - } - } - }); - } - - private void addColorRequestListener () throws JMSException - { - addTopicListener ("colorRequest", new MessageListener () - { - @Override - public void onMessage (Message message) - { - BytesMessage o = (BytesMessage) message; - try - { - byte[] body = new byte[(int) o.getBodyLength ()]; - o.readBytes (body); - String hash = new Hash (BCSAPIMessage.Hash.parseFrom (body).getHash (0).toByteArray ()).toString (); - Color c = getColor (hash); - if ( c != null ) - { - reply (o.getJMSReplyTo (), c.toProtobuf ().toByteArray ()); - } - else - { - reply (o.getJMSReplyTo (), null); - } - } - catch ( Exception e ) - { - log.trace ("Rejected invalid color request ", e); + closeSession (session); } } }); @@ -509,7 +485,7 @@ public void onMessage (Message message) private void addTransactionRequestListener () throws JMSException { - addQueueListener ("transactionRequest", new MessageListener () + addQueueListener ("transactionRequest", new SessionMessageListener () { @Override public void onMessage (Message arg0) @@ -540,7 +516,7 @@ public void onMessage (Message arg0) private void addBlockrequestListener () throws JMSException { - addQueueListener ("blockRequest", new MessageListener () + addQueueListener ("blockRequest", new SessionMessageListener () { @Override public void onMessage (Message arg0) @@ -572,7 +548,7 @@ public void onMessage (Message arg0) private void addBlockHeaderRequestListener () throws JMSException { - addQueueListener ("headerRequest", new MessageListener () + addQueueListener ("headerRequest", new SessionMessageListener () { @Override public void onMessage (Message arg0) @@ -604,7 +580,7 @@ public void onMessage (Message arg0) private void addNewBlockListener () throws JMSException { - addTopicListener ("newBlock", new MessageListener () + addTopicListener ("newBlock", new SessionMessageListener () { @Override public void onMessage (Message arg0) @@ -639,7 +615,7 @@ public void onMessage (Message arg0) private void addPingListener () throws JMSException { - addQueueListener ("ping", new MessageListener () + addQueueListener ("ping", new SessionMessageListener () { @Override public void onMessage (Message arg0) @@ -661,7 +637,7 @@ public void onMessage (Message arg0) private void addNewTransactionListener () throws JMSException { - addTopicListener ("newTransaction", new MessageListener () + addTopicListener ("newTransaction", new SessionMessageListener () { @Override public void onMessage (Message arg0) @@ -694,40 +670,10 @@ public void onMessage (Message arg0) }); } - private void reply (Destination destination, byte[] msg) - { - MessageProducer replier = null; - try - { - replier = session.createProducer (destination); - BytesMessage m = session.createBytesMessage (); - if ( msg != null ) - { - m.writeBytes (msg); - } - replier.send (m); - } - catch ( JMSException e ) - { - log.trace ("can not reply", e); - } - finally - { - try - { - replier.close (); - } - catch ( JMSException e ) - { - } - } - } - public void destroy () { try { - session.close (); connection.close (); } catch ( JMSException e ) @@ -842,46 +788,6 @@ public Block doInTransaction (TransactionStatus status) return block; } - private Color getColor (final String hash) - { - log.trace ("get color " + hash); - final Color color = new Color (); - new TransactionTemplate (transactionManager).execute (new TransactionCallbackWithoutResult () - { - @Override - protected void doInTransactionWithoutResult (TransactionStatus status) - { - status.setRollbackOnly (); - StoredColor c; - try - { - c = ((ColorStore) store).findColor (hash); - if ( c != null ) - { - color.setExpiryHeight (c.getExpiryHeight ()); - color.setSignature (c.getSignature ()); - color.setTerms (c.getTerms ()); - color.setUnit (c.getUnit ()); - color.setPubkey (c.getPubkey ()); - color.setTransaction (c.getTxHash ()); - } - } - catch ( ValidationException e ) - { - log.error ("can not get color " + hash, e); - } - - } - }); - if ( color.getTerms () != null ) - { - log.trace ("get color returned " + hash); - return color; - } - log.trace ("get color failed "); - return null; - } - private void sendTransaction (Transaction transaction) throws ValidationException { log.trace ("send transaction " + transaction.getHash ()); @@ -911,11 +817,14 @@ private void sendBlock (Block block) throws ValidationException @Override public void process (Tx tx, boolean doubleSpend) { + Session session = null; try { Transaction transaction = toBCSAPITransaction (tx, doubleSpend); + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); BytesMessage m = session.createBytesMessage (); m.writeBytes (transaction.toProtobuf ().toByteArray ()); + MessageProducer transactionProducer = session.createProducer (session.createTopic ("transaction")); transactionProducer.send (m); synchronized ( correlationBloomFilter ) @@ -933,6 +842,16 @@ public void process (Tx tx, boolean doubleSpend) { log.error ("Can not send JMS message ", e); } + finally + { + try + { + session.close (); + } + catch ( JMSException e ) + { + } + } } private Transaction toBCSAPITransaction (Tx tx, boolean doubleSpend) @@ -964,8 +883,12 @@ private Block toBCSAPIBlock (Blk b) @Override public void trunkUpdate (final List removed, final List extended) { + Session session = null; try { + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); + MessageProducer trunkProducer = session.createProducer (session.createTopic ("trunk")); + List r = new ArrayList (); List a = new ArrayList (); for ( Blk blk : removed ) @@ -985,6 +908,16 @@ public void trunkUpdate (final List removed, final List extended) { log.error ("Can not send JMS message ", e); } + finally + { + try + { + session.close (); + } + catch ( JMSException e ) + { + } + } } } From f23c600c54be4ee81cac534953f248372e8f4c3b Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 13 Jul 2013 18:18:53 +0200 Subject: [PATCH 121/299] 1.1.4 --- api/pom.xml | 2 +- pom.xml | 2 +- server/bop-service.sh | 6 +++--- server/pom.xml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 49cbdd0a..cfca9823 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.4-SNAPSHOT + 1.1.4 ../ diff --git a/pom.xml b/pom.xml index f8ed7029..69fb6bd0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.1.4-SNAPSHOT + 1.1.4 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/bop-service.sh b/server/bop-service.sh index 381f6c2d..865fe349 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -19,7 +19,7 @@ #BOP_USER="test" service=`basename "$0"` -export JVM_FLAGS="-server -Xmx2g" +export JVM_FLAGS="-server -Xmx4g" export BOP_CONTEXTS="leveldb production" # @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.3.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.4.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.3.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.4.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" diff --git a/server/pom.xml b/server/pom.xml index 3f894c8b..9e7bf0d1 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.4-SNAPSHOT + 1.1.4 ../ From 4846aca4ffe74440598244877298a3870b316f2a Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 13 Jul 2013 21:03:56 +0200 Subject: [PATCH 122/299] used wrong session in scans --- .../supernode/core/ImplementBCSAPI.java | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index a6dc8d0c..34293bd0 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -203,7 +203,6 @@ public void onMessage (Message msg) { BytesMessage o = (BytesMessage) msg; byte[] body; - Session session = null; try { body = new byte[(int) o.getBodyLength ()]; @@ -214,8 +213,7 @@ public void onMessage (Message msg) long tweak = request.getTweak (); UpdateMode updateMode = UpdateMode.values ()[request.getMode ()]; final BloomFilter filter = new BloomFilter (data, hashFunctions, tweak, updateMode); - session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); - final MessageProducer producer = session.createProducer (msg.getJMSReplyTo ()); + final Destination destination = msg.getJMSReplyTo (); requestProcessor.execute (new Runnable () { @Override @@ -232,6 +230,7 @@ public void process (Tx tx) try { session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); + MessageProducer producer = session.createProducer (destination); if ( tx != null ) { @@ -273,10 +272,6 @@ public void process (Tx tx) { log.error ("invalid filter request", e); } - finally - { - closeSession (session); - } } }); } @@ -290,7 +285,6 @@ public void onMessage (Message msg) { BytesMessage o = (BytesMessage) msg; byte[] body; - Session session = null; try { body = new byte[(int) o.getBodyLength ()]; @@ -302,8 +296,7 @@ public void onMessage (Message msg) match.add (new ByteVector (bs.toByteArray ())); } final UpdateMode mode = UpdateMode.values ()[request.getMode ()]; - session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); - final MessageProducer producer = session.createProducer (msg.getJMSReplyTo ()); + final Destination destination = msg.getJMSReplyTo (); final long after = request.hasAfter () ? request.getAfter () : 0; requestProcessor.execute (new Runnable () { @@ -325,6 +318,8 @@ public void process (Tx tx) try { session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); + MessageProducer producer = session.createProducer (destination); + m = session.createBytesMessage (); m.writeBytes (transaction.toProtobuf ().toByteArray ()); producer.send (m); @@ -347,6 +342,7 @@ public void process (Tx tx) { session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); BytesMessage m = session.createBytesMessage (); + MessageProducer producer = session.createProducer (destination); producer.send (m); // indicate EOF producer.close (); } @@ -373,10 +369,6 @@ public void process (Tx tx) { log.error ("invalid filter request", e); } - finally - { - closeSession (session); - } } }); } @@ -390,19 +382,17 @@ public void onMessage (Message msg) { BytesMessage o = (BytesMessage) msg; byte[] body; - Session session = null; try { body = new byte[(int) o.getBodyLength ()]; o.readBytes (body); - session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); BCSAPIMessage.AccountRequest request = BCSAPIMessage.AccountRequest.parseFrom (body); final ExtendedKey ek = ExtendedKey.parse (request.getPublicKey ()); final int lookAhead = request.getLookAhead (); final long after = request.getAfter (); final Set match = new HashSet (); final UpdateMode mode = UpdateMode.all; - final MessageProducer producer = session.createProducer (msg.getJMSReplyTo ()); + final Destination destination = msg.getJMSDestination (); requestProcessor.execute (new Runnable () { @Override @@ -423,6 +413,8 @@ public void process (Tx tx) try { session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); + MessageProducer producer = session.createProducer (destination); + m = session.createBytesMessage (); m.writeBytes (transaction.toProtobuf ().toByteArray ()); producer.send (m); @@ -444,6 +436,8 @@ public void process (Tx tx) try { session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); + MessageProducer producer = session.createProducer (destination); + BytesMessage m = session.createBytesMessage (); producer.send (m); // indicate EOF producer.close (); @@ -475,10 +469,6 @@ public void process (Tx tx) { log.error ("Invalid scan account request", e); } - finally - { - closeSession (session); - } } }); } From ba9f0fab61c2526d687273fb5a03accb34b3d572 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 13 Jul 2013 21:25:12 +0200 Subject: [PATCH 123/299] typo --- .../java/com/bitsofproof/supernode/core/ImplementBCSAPI.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index 34293bd0..67c5fa7b 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -392,7 +392,7 @@ public void onMessage (Message msg) final long after = request.getAfter (); final Set match = new HashSet (); final UpdateMode mode = UpdateMode.all; - final Destination destination = msg.getJMSDestination (); + final Destination destination = msg.getJMSReplyTo (); requestProcessor.execute (new Runnable () { @Override From 92453a8eabc698e955685698de48c59411e2a734 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 13 Jul 2013 21:54:26 +0200 Subject: [PATCH 124/299] less sessions --- .../supernode/core/ImplementBCSAPI.java | 86 ++++++++----------- 1 file changed, 38 insertions(+), 48 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index 67c5fa7b..175abcb5 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -219,30 +219,32 @@ public void onMessage (Message msg) @Override public void run () { + Session session = null; try { + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); + final MessageProducer producer = session.createProducer (destination); + final Session passInSession = session; + store.scan (filter, new TransactionProcessor () { @Override public void process (Tx tx) { - Session session = null; try { - session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); - MessageProducer producer = session.createProducer (destination); if ( tx != null ) { Transaction transaction = toBCSAPITransaction (tx, false); BytesMessage m; - m = session.createBytesMessage (); + m = passInSession.createBytesMessage (); m.writeBytes (transaction.toProtobuf ().toByteArray ()); producer.send (m); } else { - BytesMessage m = session.createBytesMessage (); + BytesMessage m = passInSession.createBytesMessage (); producer.send (m); // indicate EOF producer.close (); } @@ -250,27 +252,27 @@ public void process (Tx tx) catch ( JMSException e ) { } - finally - { - closeSession (session); - } } }); } - catch ( ValidationException e ) + catch ( ValidationException | JMSException e ) { log.error ("Error while scanning", e); } + finally + { + closeSession (session); + } } }); } catch ( JMSException e ) { - log.error ("invalid filter request", e); + log.error ("invalid scanRequest request", e); } catch ( InvalidProtocolBufferException e ) { - log.error ("invalid filter request", e); + log.error ("invalid scanRequest request", e); } } }); @@ -303,8 +305,13 @@ public void onMessage (Message msg) @Override public void run () { + Session session = null; try { + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); + final MessageProducer producer = session.createProducer (destination); + final Session passInSession = session; + TransactionProcessor processor = new TransactionProcessor () { @Override @@ -314,60 +321,50 @@ public void process (Tx tx) { Transaction transaction = toBCSAPITransaction (tx, false); BytesMessage m; - Session session = null; try { - session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); - MessageProducer producer = session.createProducer (destination); - m = session.createBytesMessage (); + m = passInSession.createBytesMessage (); m.writeBytes (transaction.toProtobuf ().toByteArray ()); producer.send (m); } catch ( JMSException e ) { } - finally - { - closeSession (session); - } } } }; store.filterTransactions (match, mode, after, processor); txhandler.scanUnconfirmedPool (match, mode, processor); - Session session = null; try { - session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); BytesMessage m = session.createBytesMessage (); - MessageProducer producer = session.createProducer (destination); producer.send (m); // indicate EOF producer.close (); } catch ( JMSException e ) { } - finally - { - closeSession (session); - } } - catch ( ValidationException e ) + catch ( ValidationException | JMSException e ) { log.error ("Error while scanning", e); } + finally + { + closeSession (session); + } } }); } catch ( JMSException e ) { - log.error ("invalid filter request", e); + log.error ("invalid matchRequest request", e); } catch ( InvalidProtocolBufferException e ) { - log.error ("invalid filter request", e); + log.error ("invalid matchRequest request", e); } } }); @@ -398,8 +395,12 @@ public void onMessage (Message msg) @Override public void run () { + Session session = null; try { + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); + final MessageProducer producer = session.createProducer (destination); + final Session passInSession = session; TransactionProcessor processor = new TransactionProcessor () { @Override @@ -409,35 +410,24 @@ public void process (Tx tx) { Transaction transaction = toBCSAPITransaction (tx, false); BytesMessage m; - Session session = null; + try { - session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); - MessageProducer producer = session.createProducer (destination); - - m = session.createBytesMessage (); + m = passInSession.createBytesMessage (); m.writeBytes (transaction.toProtobuf ().toByteArray ()); producer.send (m); } catch ( JMSException e ) { } - finally - { - closeSession (session); - } } } }; store.filterTransactions (match, ek, lookAhead, after, processor); txhandler.scanUnconfirmedPool (match, mode, processor); - Session session = null; try { - session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); - MessageProducer producer = session.createProducer (destination); - BytesMessage m = session.createBytesMessage (); producer.send (m); // indicate EOF producer.close (); @@ -445,15 +435,15 @@ public void process (Tx tx) catch ( JMSException e ) { } - finally - { - closeSession (session); - } } - catch ( ValidationException e ) + catch ( ValidationException | JMSException e ) { log.error ("Error while scanning", e); } + finally + { + closeSession (session); + } } }); } From 82dfd0268386d214191c910f3f8cfef6977be799 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Mon, 15 Jul 2013 20:34:10 +0200 Subject: [PATCH 125/299] 1.1.5-SNAPSHOT --- api/pom.xml | 2 +- pom.xml | 2 +- server/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index cfca9823..649cfc5c 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.4 + 1.1.5-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index 69fb6bd0..bd900ed0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.1.4 + 1.1.5-SNAPSHOT supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/pom.xml b/server/pom.xml index 9e7bf0d1..48936583 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.4 + 1.1.5-SNAPSHOT ../ From b4a9670bfa994db71e51cffd49b719350fe2846b Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Mon, 15 Jul 2013 21:12:36 +0200 Subject: [PATCH 126/299] remove bloom filter scan from API --- .../com/bitsofproof/supernode/api/BCSAPI.java | 11 -- .../supernode/api/JMSServerConnector.java | 78 ------------- .../supernode/core/ImplementBCSAPI.java | 103 ------------------ 3 files changed, 192 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java index bd342573..ada6df22 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java @@ -17,7 +17,6 @@ import java.util.Collection; -import com.bitsofproof.supernode.common.BloomFilter; import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; /** @@ -136,16 +135,6 @@ public interface BCSAPI */ public void scanTransactions (Collection match, UpdateMode mode, long after, TransactionListener listener) throws BCSAPIException; - /** - * scan transactions matching the filter - * - * @param filter - * @param after - * @param listener - * @throws BCSAPIException - */ - public void scanTransactions (BloomFilter filter, TransactionListener listener) throws BCSAPIException; - /** * scan transactions for an account * diff --git a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java index a206ba5a..bdc28de9 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java @@ -38,7 +38,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.bitsofproof.supernode.common.BloomFilter; import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.common.Hash; import com.google.protobuf.ByteString; @@ -401,83 +400,6 @@ public void onMessage (Message message) } } - @Override - public void scanTransactions (BloomFilter filter, final TransactionListener listener) throws BCSAPIException - { - Session session = null; - try - { - session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); - MessageProducer scanRequestProducer = session.createProducer (session.createQueue ("scanRequest")); - BytesMessage m = session.createBytesMessage (); - - BCSAPIMessage.FilterRequest.Builder builder = BCSAPIMessage.FilterRequest.newBuilder (); - builder.setBcsapiversion (1); - builder.setFilter (ByteString.copyFrom (filter.getFilter ())); - builder.setHashFunctions ((int) filter.getHashFunctions ()); - builder.setTweak ((int) filter.getTweak ()); - builder.setMode (filter.getUpdateMode ().ordinal ()); - - m.writeBytes (builder.build ().toByteArray ()); - - final TemporaryQueue answerQueue = session.createTemporaryQueue (); - final MessageConsumer consumer = session.createConsumer (answerQueue); - m.setJMSReplyTo (answerQueue); - final Semaphore ready = new Semaphore (0); - consumer.setMessageListener (new MessageListener () - { - @Override - public void onMessage (Message message) - { - BytesMessage m = (BytesMessage) message; - byte[] body; - try - { - if ( m.getBodyLength () > 0 ) - { - body = new byte[(int) m.getBodyLength ()]; - m.readBytes (body); - Transaction t = Transaction.fromProtobuf (BCSAPIMessage.Transaction.parseFrom (body)); - t.computeHash (); - listener.process (t); - } - else - { - consumer.close (); - answerQueue.delete (); - ready.release (); - } - } - catch ( JMSException e ) - { - log.error ("Malformed message received for filter scan transactions", e); - } - catch ( InvalidProtocolBufferException e ) - { - log.error ("Malformed message received for filter scan transactions", e); - } - } - }); - - scanRequestProducer.send (m); - ready.acquireUninterruptibly (); - } - catch ( JMSException e ) - { - throw new BCSAPIException (e); - } - finally - { - try - { - session.close (); - } - catch ( JMSException e ) - { - } - } - } - @Override public void scanTransactions (ExtendedKey master, int lookAhead, long after, final TransactionListener listener) throws BCSAPIException { diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index 175abcb5..4ed103cd 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -16,11 +16,8 @@ package com.bitsofproof.supernode.core; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -48,7 +45,6 @@ import com.bitsofproof.supernode.api.ExtendedKey; import com.bitsofproof.supernode.api.Transaction; import com.bitsofproof.supernode.api.TrunkUpdateMessage; -import com.bitsofproof.supernode.common.BloomFilter; import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.common.ByteVector; import com.bitsofproof.supernode.common.Hash; @@ -75,9 +71,6 @@ public class ImplementBCSAPI implements TrunkListener, TxListener private Connection connection; - private final Map correlationProducer = new HashMap (); - private final Map correlationBloomFilter = Collections.synchronizedMap (new HashMap ()); - private final ExecutorService requestProcessor = Executors.newFixedThreadPool (Runtime.getRuntime ().availableProcessors ()); public ImplementBCSAPI (BitcoinNetwork network, TxHandler txHandler) @@ -169,7 +162,6 @@ public void init () addBlockrequestListener (); addBlockHeaderRequestListener (); addTransactionRequestListener (); - addBloomScanListener (); addMatchScanListener (); addPingListener (); addAccountScanListener (); @@ -194,90 +186,6 @@ private void closeSession (Session session) } } - private void addBloomScanListener () throws JMSException - { - addQueueListener ("scanRequest", new SessionMessageListener () - { - @Override - public void onMessage (Message msg) - { - BytesMessage o = (BytesMessage) msg; - byte[] body; - try - { - body = new byte[(int) o.getBodyLength ()]; - o.readBytes (body); - BCSAPIMessage.FilterRequest request = BCSAPIMessage.FilterRequest.parseFrom (body); - byte[] data = request.getFilter ().toByteArray (); - long hashFunctions = request.getHashFunctions (); - long tweak = request.getTweak (); - UpdateMode updateMode = UpdateMode.values ()[request.getMode ()]; - final BloomFilter filter = new BloomFilter (data, hashFunctions, tweak, updateMode); - final Destination destination = msg.getJMSReplyTo (); - requestProcessor.execute (new Runnable () - { - @Override - public void run () - { - Session session = null; - try - { - session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); - final MessageProducer producer = session.createProducer (destination); - final Session passInSession = session; - - store.scan (filter, new TransactionProcessor () - { - @Override - public void process (Tx tx) - { - try - { - - if ( tx != null ) - { - Transaction transaction = toBCSAPITransaction (tx, false); - BytesMessage m; - m = passInSession.createBytesMessage (); - m.writeBytes (transaction.toProtobuf ().toByteArray ()); - producer.send (m); - } - else - { - BytesMessage m = passInSession.createBytesMessage (); - producer.send (m); // indicate EOF - producer.close (); - } - } - catch ( JMSException e ) - { - } - } - }); - } - catch ( ValidationException | JMSException e ) - { - log.error ("Error while scanning", e); - } - finally - { - closeSession (session); - } - } - }); - } - catch ( JMSException e ) - { - log.error ("invalid scanRequest request", e); - } - catch ( InvalidProtocolBufferException e ) - { - log.error ("invalid scanRequest request", e); - } - } - }); - } - private void addMatchScanListener () throws JMSException { addQueueListener ("matchRequest", new SessionMessageListener () @@ -806,17 +714,6 @@ public void process (Tx tx, boolean doubleSpend) m.writeBytes (transaction.toProtobuf ().toByteArray ()); MessageProducer transactionProducer = session.createProducer (session.createTopic ("transaction")); transactionProducer.send (m); - - synchronized ( correlationBloomFilter ) - { - for ( Map.Entry e : correlationBloomFilter.entrySet () ) - { - if ( tx.passesFilter (e.getValue ()) ) - { - correlationProducer.get (e.getKey ()).send (m); - } - } - } } catch ( Exception e ) { From a9096123c7ff316ac25a934ab2fc12ed2f885de3 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Mon, 15 Jul 2013 21:18:18 +0200 Subject: [PATCH 127/299] remove match set limits --- .../main/java/com/bitsofproof/supernode/api/BCSAPI.java | 2 +- .../com/bitsofproof/supernode/core/CachedBlockStore.java | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java index ada6df22..c5c05fcd 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java @@ -127,7 +127,7 @@ public interface BCSAPI public void removeTrunkListener (TrunkListener listener); /** - * Match transactions using and address or outpoint in match. This returns only exact matches but limited in result set. + * Match transactions using and address or outpoint in match. * * @param match * @param listener diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 26bc4988..81ff250e 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -413,15 +413,13 @@ else if ( blockChain.getLast () != p ) blockChain.addAll (appendList); } - private static final int MAX_MATCH_SET = 1000; - @Transactional (propagation = Propagation.REQUIRED, rollbackFor = { Exception.class }, readOnly = true) @Override public void filterTransactions (Set matchSet, ExtendedKey ek, int lookAhead, long after, TransactionProcessor processor) throws ValidationException { Map addressSet = new HashMap (); - lookAhead = Math.min (Math.max (10, lookAhead), MAX_MATCH_SET); + lookAhead = Math.min (Math.max (10, lookAhead), 1000); for ( int i = 0; i < lookAhead; ++i ) { ByteVector address = new ByteVector (ek.getKey (i).getAddress ()); @@ -511,10 +509,6 @@ public void filterTransactions (Set matchSet, ExtendedKey ek, int lo @Override public void filterTransactions (Set matchSet, UpdateMode update, long after, TransactionProcessor processor) throws ValidationException { - if ( matchSet.size () > MAX_MATCH_SET ) - { - throw new ValidationException ("Match set too big for filterTransactions. Use scan instead"); - } try { Map> hashes = new HashMap> (); From fb043f76b4a6b62754630b4176343a99ace62276 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Mon, 15 Jul 2013 22:23:49 +0200 Subject: [PATCH 128/299] simplified --- .../api/ExtendedKeyAccountManager.java | 85 +++++++++---------- 1 file changed, 40 insertions(+), 45 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java index 9eba0e7d..b0a2d24d 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java @@ -20,15 +20,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.bitsofproof.supernode.common.ByteVector; import com.bitsofproof.supernode.common.Key; -import com.bitsofproof.supernode.common.ScriptFormat; -import com.bitsofproof.supernode.common.ScriptFormat.Token; import com.bitsofproof.supernode.common.ValidationException; public class ExtendedKeyAccountManager extends BaseAccountManager implements TransactionListener @@ -38,6 +35,7 @@ public class ExtendedKeyAccountManager extends BaseAccountManager implements Tra private final Map keyIDForAddress = new HashMap (); private ExtendedKey master; private int nextSequence; + private int lookAhead = 100; public ExtendedKey getMaster () { @@ -52,16 +50,37 @@ public void setMaster (ExtendedKey master) @Override public int getNumberOfKeys () { - return nextSequence; + return keyIDForAddress.size (); } - public void setNextSequence (int nextSequence) throws ValidationException + @Override + protected void notifyListener (Transaction t) { - while ( this.nextSequence < nextSequence ) + for ( TransactionOutput o : t.getOutputs () ) { - getNextKey (); + if ( isOwnAddress (o.getOutputAddress ()) ) + { + int keyId = keyIDForAddress.get (new ByteVector (o.getOutputAddress ())); + ensureLookAhead (keyId); + } + } + super.notifyListener (t); + } + + private void ensureLookAhead (int from) + { + while ( keyIDForAddress.size () < (from + lookAhead) ) + { + Key key = null; + try + { + key = master.getKey (keyIDForAddress.size ()); + } + catch ( ValidationException e ) + { + } + keyIDForAddress.put (new ByteVector (key.getAddress ()), keyIDForAddress.size ()); } - this.nextSequence = nextSequence; } public Key getKey (int i) throws ValidationException @@ -69,13 +88,16 @@ public Key getKey (int i) throws ValidationException return master.getKey (i); } + public void setNextKey (int i) + { + nextSequence = i; + ensureLookAhead (nextSequence); + } + @Override public Key getNextKey () throws ValidationException { - Key key = master.getKey (nextSequence); - keyIDForAddress.put (new ByteVector (key.getAddress ()), nextSequence); - ++nextSequence; - return key; + return getKey (nextSequence++); } public Integer getKeyIDForAddress (byte[] address) @@ -119,11 +141,8 @@ public ExtendedKeyAccountManager (String name, long created) public void sync (BCSAPI api, final int lookAhead) throws BCSAPIException, ValidationException { - final AtomicInteger lastUsedKey = new AtomicInteger (-1); - for ( int i = 0; i < lookAhead; ++i ) - { - getNextKey (); - } + this.lookAhead = lookAhead; + ensureLookAhead (0); log.trace ("Sync " + getName () + " nkeys: " + getNumberOfKeys ()); api.scanTransactions (getMaster (), lookAhead, getCreated (), new TransactionListener () { @@ -132,40 +151,16 @@ public void process (Transaction t) { for ( TransactionOutput o : t.getOutputs () ) { - try - { - for ( Token token : ScriptFormat.parse (o.getScript ()) ) - { - if ( token.data != null ) - { - Integer thisKey = getKeyIDForAddress (token.data); - if ( thisKey != null ) - { - lastUsedKey.set (Math.max (thisKey, lastUsedKey.get ())); - } - else - { - while ( thisKey == null && (getNumberOfKeys () - lastUsedKey.get ()) < lookAhead ) - { - getNextKey (); - } - thisKey = getKeyIDForAddress (token.data); - if ( thisKey != null ) - { - lastUsedKey.set (Math.max (thisKey, lastUsedKey.get ())); - } - } - } - } - } - catch ( ValidationException e ) + Integer thisKey = getKeyIDForAddress (o.getOutputAddress ()); + if ( thisKey != null ) { + ensureLookAhead (thisKey); + nextSequence = Math.max (nextSequence, thisKey + 1); } } updateWithTransaction (t); } }); - setNextSequence (Math.min (lastUsedKey.get () + 1, getNumberOfKeys ())); log.trace ("Sync " + getName () + " finished with nkeys: " + getNumberOfKeys ()); } } From bc864d32b15ea6ad82312760f20ae6b12fdeeb18 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Mon, 15 Jul 2013 22:34:06 +0200 Subject: [PATCH 129/299] ensure lookahead from any key asked --- .../com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java index b0a2d24d..18c533bc 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java @@ -85,6 +85,7 @@ private void ensureLookAhead (int from) public Key getKey (int i) throws ValidationException { + ensureLookAhead (i); return master.getKey (i); } From 8f2a3626ff87446d4076607c704cb5f0415ce781 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 24 Jul 2013 11:35:31 +0200 Subject: [PATCH 130/299] return used number of keys (not includig lookup) --- .../bitsofproof/supernode/api/ExtendedKeyAccountManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java index 18c533bc..d06a1806 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java @@ -50,7 +50,7 @@ public void setMaster (ExtendedKey master) @Override public int getNumberOfKeys () { - return keyIDForAddress.size (); + return nextSequence; } @Override From 8bb8cefa473e5df9e0d91550d6d8f9f9e3eda964 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 24 Jul 2013 15:22:59 +0200 Subject: [PATCH 131/299] report back used keys from scan --- .../supernode/api/ExtendedKeyAccountManager.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java index d06a1806..c444ee75 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java @@ -18,8 +18,10 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -140,11 +142,12 @@ public ExtendedKeyAccountManager (String name, long created) super (name, created); } - public void sync (BCSAPI api, final int lookAhead) throws BCSAPIException, ValidationException + public Set sync (BCSAPI api, final int lookAhead) throws BCSAPIException, ValidationException { this.lookAhead = lookAhead; + final Set usedIndices = new HashSet (); ensureLookAhead (0); - log.trace ("Sync " + getName () + " nkeys: " + getNumberOfKeys ()); + log.trace ("Sync start " + getName ()); api.scanTransactions (getMaster (), lookAhead, getCreated (), new TransactionListener () { @Override @@ -155,6 +158,7 @@ public void process (Transaction t) Integer thisKey = getKeyIDForAddress (o.getOutputAddress ()); if ( thisKey != null ) { + usedIndices.add (thisKey); ensureLookAhead (thisKey); nextSequence = Math.max (nextSequence, thisKey + 1); } @@ -162,6 +166,7 @@ public void process (Transaction t) updateWithTransaction (t); } }); - log.trace ("Sync " + getName () + " finished with nkeys: " + getNumberOfKeys ()); + log.trace ("Sync finish " + getName () + " used: " + usedIndices.size () + " keys of " + getNumberOfKeys () + " look ahead:" + lookAhead); + return usedIndices; } } From 65ba07f9ac271c260aa7f66dbd5a4b15c1a118a1 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 24 Jul 2013 15:55:47 +0200 Subject: [PATCH 132/299] Revert "report back used keys from scan" This reverts commit 8bb8cefa473e5df9e0d91550d6d8f9f9e3eda964. --- .../supernode/api/ExtendedKeyAccountManager.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java index c444ee75..d06a1806 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java @@ -18,10 +18,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -142,12 +140,11 @@ public ExtendedKeyAccountManager (String name, long created) super (name, created); } - public Set sync (BCSAPI api, final int lookAhead) throws BCSAPIException, ValidationException + public void sync (BCSAPI api, final int lookAhead) throws BCSAPIException, ValidationException { this.lookAhead = lookAhead; - final Set usedIndices = new HashSet (); ensureLookAhead (0); - log.trace ("Sync start " + getName ()); + log.trace ("Sync " + getName () + " nkeys: " + getNumberOfKeys ()); api.scanTransactions (getMaster (), lookAhead, getCreated (), new TransactionListener () { @Override @@ -158,7 +155,6 @@ public void process (Transaction t) Integer thisKey = getKeyIDForAddress (o.getOutputAddress ()); if ( thisKey != null ) { - usedIndices.add (thisKey); ensureLookAhead (thisKey); nextSequence = Math.max (nextSequence, thisKey + 1); } @@ -166,7 +162,6 @@ public void process (Transaction t) updateWithTransaction (t); } }); - log.trace ("Sync finish " + getName () + " used: " + usedIndices.size () + " keys of " + getNumberOfKeys () + " look ahead:" + lookAhead); - return usedIndices; + log.trace ("Sync " + getName () + " finished with nkeys: " + getNumberOfKeys ()); } } From cb184771c36ba6eebf09d238dad76bc753d98827 Mon Sep 17 00:00:00 2001 From: Barnabas Debreczeni Date: Sat, 27 Jul 2013 21:49:52 +0200 Subject: [PATCH 133/299] add JCE requirement to README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 432f0f8d..5c97da60 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ _Use our ready to go hosted BOP Server instance! Get an evaluation access, attra Build the Community Server -------------------------- -Make sure you have Maven3, JDK 7 and Google protobuf compiler 2.4.1 installed. +Make sure you have Maven3, JDK 7 (with JCE Unlimited Strength Policy Jurisdiction) and Google protobuf compiler 2.4.1 installed. git clone https://github.com/bitsofproof/supernode From 016c098eb5676533cf8751d522071c2af8d81a6f Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Mon, 5 Aug 2013 20:27:20 +0200 Subject: [PATCH 134/299] do not rebroadcast own transactions. avoid deadlock on unconfirmed. --- .../bitsofproof/supernode/core/TxHandler.java | 118 ++++-------------- 1 file changed, 27 insertions(+), 91 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index 7fa57d3c..2be5dee8 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -16,15 +16,14 @@ package com.bitsofproof.supernode.core; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; -import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,9 +53,7 @@ public class TxHandler implements TrunkListener private final BitcoinNetwork network; - private final Set heard = new HashSet (); - private final Map unconfirmed = new HashMap (); - private final Set own = new HashSet (); + private final Map unconfirmed = Collections.synchronizedMap (new HashMap ()); private ImplementTxOutCacheDelta availableOutput = null; private PlatformTransactionManager transactionManager; @@ -100,36 +97,6 @@ public TxHandler (final BitcoinNetwork network) this.network = network; final BlockStore store = network.getStore (); - network.scheduleJobWithFixedDelay (new Runnable () - { - @Override - public void run () - { - // give retransmits of previously failed tx a chance - synchronized ( heard ) - { - heard.clear (); - } - // re-transmit own until not in a block - synchronized ( own ) - { - if ( !own.isEmpty () ) - { - for ( BitcoinPeer peer : network.getConnectPeers () ) - { - InvMessage tm = (InvMessage) peer.createMessage ("inv"); - for ( String t : own ) - { - log.debug ("Re-broadcast " + t); - tm.getTransactionHashes ().add (new Hash (t).toByteArray ()); - } - peer.send (tm); - } - } - } - } - }, 10, 10, TimeUnit.MINUTES); - store.addTrunkListener (this); network.getStore ().runInCacheContext (new BlockStore.CacheContextRunnable () { @@ -149,20 +116,10 @@ public void process (InvMessage im, BitcoinPeer peer) for ( byte[] h : im.getTransactionHashes () ) { String hash = new Hash (h).toString (); - synchronized ( unconfirmed ) + if ( !unconfirmed.containsKey (hash) ) { - synchronized ( heard ) - { - if ( !heard.contains (hash) ) - { - heard.add (hash); - if ( !unconfirmed.containsKey (hash) ) - { - log.trace ("heard about new transaction " + hash + " from " + peer.getAddress ()); - get.getTransactions ().add (h); - } - } - } + log.trace ("heard about transaction " + hash + " from " + peer.getAddress ()); + get.getTransactions ().add (h); } } if ( get.getTransactions ().size () > 0 ) @@ -215,45 +172,35 @@ public void validateCacheAndSend (final Tx t, final BitcoinPeer peer) throws Val @Override public void run (TxOutCache cache) throws ValidationException { - synchronized ( unconfirmed ) + if ( !unconfirmed.containsKey (t.getHash ()) ) { - if ( !unconfirmed.containsKey (t.getHash ()) ) + ValidationException exception = new TransactionTemplate (transactionManager).execute (new TransactionCallback () { - ValidationException exception = new TransactionTemplate (transactionManager).execute (new TransactionCallback () + @Override + public ValidationException doInTransaction (TransactionStatus status) { - @Override - public ValidationException doInTransaction (TransactionStatus status) - { - status.setRollbackOnly (); + status.setRollbackOnly (); - try - { - if ( network.getStore ().getTransaction (t.getHash ()) == null ) - { - network.getStore ().validateTransaction (t, availableOutput); - sendTransaction (t, peer); - cacheTransaction (t); - notifyListener (t, false); - if ( peer == null ) - { - synchronized ( own ) - { - own.add (t.getHash ()); - } - } - } - return null; - } - catch ( ValidationException e ) + try + { + if ( network.getStore ().getTransaction (t.getHash ()) == null ) { - return e; + network.getStore ().validateTransaction (t, availableOutput); + sendTransaction (t, peer); + cacheTransaction (t); + notifyListener (t, false); } + return null; + } + catch ( ValidationException e ) + { + return e; } - }); - if ( exception != null ) - { - throw exception; } + }); + if ( exception != null ) + { + throw exception; } } } @@ -267,10 +214,7 @@ public ValidationException doInTransaction (TransactionStatus status) public Tx getTransaction (String hash) { - synchronized ( unconfirmed ) - { - return unconfirmed.get (hash); - } + return unconfirmed.get (hash); } private void cacheTransaction (Tx tx) @@ -365,10 +309,6 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo if ( unconfirmed.containsKey (tx.getHash ()) ) { unconfirmed.remove (tx.getHash ()); - synchronized ( own ) - { - own.remove (tx.getHash ()); - } dependencyOrderedSet.remove (tx); notifyListener (tx, false); } @@ -400,10 +340,6 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo } catch ( ValidationException e ) { - synchronized ( own ) - { - own.remove (tx.getHash ()); - } unconfirmed.remove (tx.getHash ()); txi.remove (); notifyListener (tx, true); From 512725e3665ce7fc57983cc4435b2ff5b78a4f7b Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Tue, 6 Aug 2013 10:13:20 +0200 Subject: [PATCH 135/299] use 1.1.5-SNAPSHOT target --- server/bop-service.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/bop-service.sh b/server/bop-service.sh index 865fe349..5855ce82 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.4.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.5-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.4.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.5-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" From c0e07f7851e2584bf67dffa739c7757d4d6b05df Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 7 Aug 2013 16:26:57 +0200 Subject: [PATCH 136/299] API now expects after in milis since unix epoch. --- .../supernode/api/ExtendedKeySetWallet.java | 42 +++++++++++++++++++ .../bitsofproof/supernode/api/FileWallet.java | 9 ++-- .../com/bitsofproof/supernode/api/Wallet.java | 6 --- .../supernode/api/WalletFactory.java | 6 --- .../supernode/core/CachedBlockStore.java | 4 +- .../bitsofproof/supernode/test/APITest.java | 6 +-- 6 files changed, 49 insertions(+), 24 deletions(-) create mode 100644 api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeySetWallet.java delete mode 100644 api/src/main/java/com/bitsofproof/supernode/api/WalletFactory.java diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeySetWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeySetWallet.java new file mode 100644 index 00000000..6b926aa9 --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeySetWallet.java @@ -0,0 +1,42 @@ +package com.bitsofproof.supernode.api; + +import java.util.HashMap; +import java.util.Map; + +import com.bitsofproof.supernode.common.ValidationException; + +public class ExtendedKeySetWallet implements Wallet +{ + private final Map accountManager = new HashMap<> (); + + public void addKey (String name, ExtendedKey key, long created) throws ValidationException + { + if ( accountManager.containsKey (name) ) + { + throw new ValidationException (name + " account manager exsists"); + } + ExtendedKeyAccountManager manager; + accountManager.put (name, manager = new ExtendedKeyAccountManager (name, created)); + manager.setMaster (key); + } + + @Override + public AccountManager getAccountManager (String name) + { + return accountManager.get (name); + } + + @Override + public AccountManager createAccountManager (String name) throws ValidationException + { + if ( accountManager.containsKey (name) ) + { + throw new ValidationException (name + " account manager exsists"); + } + ExtendedKeyAccountManager manager; + accountManager.put (name, manager = new ExtendedKeyAccountManager (name, System.currentTimeMillis ())); + manager.setMaster (ExtendedKey.createNew ()); + return manager; + } + +} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java index 0674f649..a167b6f4 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java @@ -32,7 +32,6 @@ public boolean exists () return new File (fileName).exists (); } - @Override public void init (String passphrase) { master = null; @@ -47,7 +46,6 @@ public void init (String passphrase) } } - @Override public void unlock (String passphrase) throws ValidationException { master = ExtendedKey.createFromPassphrase (passphrase, encryptedSeed); @@ -62,7 +60,6 @@ public void unlock (String passphrase) throws ValidationException } } - @Override public void lock () { master = null; @@ -89,7 +86,7 @@ public static FileWallet read (String fileName) throws IOException, ValidationEx wallet.signature = walletMessage.getSignature ().toByteArray (); for ( BCSAPIMessage.Wallet.Account account : walletMessage.getAccountsList () ) { - ExtendedKeyAccountManager am = new ExtendedKeyAccountManager (account.getName (), account.getCreated ()); + ExtendedKeyAccountManager am = new ExtendedKeyAccountManager (account.getName (), account.getCreated () * 1000); wallet.accounts.put (account.getName (), am); am.setMaster (ExtendedKey.parse (account.getPublicKey ())); } @@ -128,7 +125,7 @@ public synchronized AccountManager createAccountManager (String name) throws Val { throw new ValidationException ("The wallet is locked"); } - ExtendedKeyAccountManager account = new ExtendedKeyAccountManager (name, System.currentTimeMillis () / 1000); + ExtendedKeyAccountManager account = new ExtendedKeyAccountManager (name, System.currentTimeMillis ()); account.setMaster (master.getChild (accounts.size () | 0x80000000)); accounts.put (name, account); return account; @@ -148,7 +145,7 @@ public void persist () throws IOException { BCSAPIMessage.Wallet.Account.Builder ab = BCSAPIMessage.Wallet.Account.newBuilder (); ab.setName (am.getName ()); - ab.setCreated (am.getCreated ()); + ab.setCreated (am.getCreated () / 1000); ab.setPublicKey (am.getMaster ().getReadOnly ().serialize (true)); builder.addAccounts (ab.build ()); } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java b/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java index 8589966f..0c4d9513 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java @@ -4,12 +4,6 @@ public interface Wallet { - public void init (String passphrase); - - public void unlock (String passphrase) throws ValidationException; - - public void lock (); - public AccountManager getAccountManager (String name); public AccountManager createAccountManager (String name) throws ValidationException; diff --git a/api/src/main/java/com/bitsofproof/supernode/api/WalletFactory.java b/api/src/main/java/com/bitsofproof/supernode/api/WalletFactory.java deleted file mode 100644 index 4bcb7013..00000000 --- a/api/src/main/java/com/bitsofproof/supernode/api/WalletFactory.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.bitsofproof.supernode.api; - -public interface WalletFactory -{ - public Wallet getWallet (String fileName, String passphrase) throws BCSAPIException; -} diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 81ff250e..3dc7b695 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -433,7 +433,7 @@ public void filterTransactions (Set matchSet, ExtendedKey ek, int lo for ( CachedBlock cb : blockChain ) { - if ( after > 1231006505 && cb.time <= after || after < 1231006505 && cb.height <= after ) + if ( after > 1231006505000L && cb.time * 1000 <= after || after < 1231006505000L && cb.height <= after ) { continue; } @@ -517,7 +517,7 @@ public void filterTransactions (Set matchSet, UpdateMode update, lon for ( CachedBlock cb : blockChain ) { - if ( after > 1231006505 && cb.time <= after || after < 1231006505 && cb.height <= after ) + if ( after > 1231006505000L && cb.time * 1000 <= after || after < 1231006505000L && cb.height <= after ) { continue; } diff --git a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java index 85e04db1..63dca950 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java @@ -41,7 +41,7 @@ import com.bitsofproof.supernode.api.BCSAPI; import com.bitsofproof.supernode.api.BCSAPIException; import com.bitsofproof.supernode.api.Block; -import com.bitsofproof.supernode.api.FileWallet; +import com.bitsofproof.supernode.api.ExtendedKeySetWallet; import com.bitsofproof.supernode.api.Transaction; import com.bitsofproof.supernode.api.TransactionListener; import com.bitsofproof.supernode.api.TrunkListener; @@ -175,9 +175,7 @@ public void test () throws BCSAPIException, ValidationException, IOException { store.resetStore (chain); store.cache (chain, 0); - wallet = new FileWallet ("test.wallet"); - wallet.init ("passphrase"); - wallet.unlock ("passphrase"); + wallet = new ExtendedKeySetWallet (); alice = wallet.createAccountManager ("Alice"); bob = wallet.createAccountManager ("Bob"); From 96a9ca19f7e087a8117253f664e13f32fe02e8c9 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 7 Aug 2013 16:33:56 +0200 Subject: [PATCH 137/299] do not ask for mempool at startup, it is not dependency ordered, leads to rejects for most cases. --- .../java/com/bitsofproof/supernode/core/BitcoinPeer.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/BitcoinPeer.java b/server/src/main/java/com/bitsofproof/supernode/core/BitcoinPeer.java index ac39963f..6d04e0c1 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/BitcoinPeer.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/BitcoinPeer.java @@ -326,12 +326,6 @@ public void process (BitcoinPeer.Message m, BitcoinPeer peer) + getNetwork ().getNumberOfConnections ()); network.addPeer (peer); peer.send (peer.createMessage ("getaddr")); - if ( peer.getVersion () > 60001 ) - { - MempoolMessage mp = (MempoolMessage) peer.createMessage ("mempool"); - - peer.send (mp); - } } }); } From 7cc8775c8a83668d924acf60d2001e8b6c67e821 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 7 Aug 2013 16:48:11 +0200 Subject: [PATCH 138/299] apply filter for inv to bloom filtered peers --- .../src/main/java/com/bitsofproof/supernode/core/TxHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index 2be5dee8..33748e7f 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -243,7 +243,7 @@ private void sendTransaction (Tx tx, BitcoinPeer peer) { if ( p != peer ) { - if ( peer == null || peer.isRelay () || peer.getFilter () == null || tx.passesFilter (peer.getFilter ()) ) + if ( p.isRelay () || p.getFilter () == null || tx.passesFilter (p.getFilter ()) ) { InvMessage tm = (InvMessage) p.createMessage ("inv"); tm.getTransactionHashes ().add (new Hash (tx.getHash ()).toByteArray ()); From 87722a010536288d9b6ae5684eed9939ba08149b Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 7 Aug 2013 18:25:32 +0200 Subject: [PATCH 139/299] use confirmed and change for payments. prepare for UTXO not in memory --- .../supernode/api/AccountManager.java | 15 ++- .../api/AddressListAccountManager.java | 13 +- .../supernode/api/BaseAccountManager.java | 117 +++++++++++++----- .../api/ExtendedKeyAccountManager.java | 13 +- .../supernode/api/ExtendedKeySetWallet.java | 4 +- .../bitsofproof/supernode/api/FileWallet.java | 35 +++++- .../supernode/api/InMemoryUTXO.java | 60 +-------- .../supernode/api/KeyListAccountManager.java | 13 +- .../bitsofproof/supernode/test/APITest.java | 31 +++-- 9 files changed, 162 insertions(+), 139 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java index 8c78bde4..6fd1b772 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java @@ -23,9 +23,20 @@ public interface AccountManager extends TransactionListener { - public String getName (); + public interface UTXO + { - public long getCreated (); + public void add (TransactionOutput out); + + public Collection getUTXO (); + + public TransactionOutput get (String tx, long ix); + + public TransactionOutput remove (String tx, long ix); + + public long getTotal (); + + } public int getNumberOfKeys (); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java index ae388232..646b3c52 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java @@ -20,11 +20,6 @@ public class AddressListAccountManager extends BaseAccountManager private final Set addresses = new HashSet (); - public AddressListAccountManager (String name, long created) - { - super (name, created); - } - @Override public int getNumberOfKeys () { @@ -65,10 +60,10 @@ public void addAddress (byte[] address) addresses.add (new ByteVector (address)); } - public void sync (BCSAPI api) throws BCSAPIException, ValidationException + public void sync (BCSAPI api, long created) throws BCSAPIException, ValidationException { - log.trace ("Sync " + getName () + " naddr: " + addresses.size ()); - api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () + log.trace ("Sync naddr: " + addresses.size ()); + api.scanTransactions (getAddresses (), UpdateMode.all, created, new TransactionListener () { @Override public void process (Transaction t) @@ -76,6 +71,6 @@ public void process (Transaction t) updateWithTransaction (t); } }); - log.trace ("Sync " + getName () + " finished naddr: " + addresses.size ()); + log.trace ("Sync finished naddr: " + addresses.size ()); } } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java index 64aae485..b51a3af7 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java @@ -34,13 +34,10 @@ public abstract class BaseAccountManager implements AccountManager private static final long MINIMUM_FEE = 10000; private static final long MAXIMUM_FEE = 1000000; - private final InMemoryUTXO confirmed = new InMemoryUTXO (); - private final InMemoryUTXO change = new InMemoryUTXO (); - private final InMemoryUTXO receiving = new InMemoryUTXO (); - private final InMemoryUTXO sending = new InMemoryUTXO (); - - private final long created; - private final String name; + private final UTXO confirmed = createConfirmedUTXO (); + private final UTXO change = createChangeUTXO (); + private final UTXO receiving = createReceivingUTXO (); + private final UTXO sending = createSendingUTXO (); private final List accountListener = Collections.synchronizedList (new ArrayList ()); private final Map transactions = new HashMap (); @@ -50,27 +47,29 @@ public abstract class BaseAccountManager implements AccountManager @Override public abstract Key getNextKey () throws ValidationException; - public BaseAccountManager (String name, long created) + protected UTXO createConfirmedUTXO () { - this.name = name; - this.created = created; + return new InMemoryUTXO (); } - public boolean isOwnAddress (byte[] address) + protected UTXO createChangeUTXO () { - return getKeyForAddress (address) != null; + return new InMemoryUTXO (); } - @Override - public String getName () + protected UTXO createSendingUTXO () { - return name; + return new InMemoryUTXO (); } - @Override - public long getCreated () + protected UTXO createReceivingUTXO () + { + return new InMemoryUTXO (); + } + + public boolean isOwnAddress (byte[] address) { - return created; + return getKeyForAddress (address) != null; } protected static class TransactionSink @@ -96,14 +95,6 @@ public long getValue () } } - public List getSources (long amount, long fee, String color) - { - synchronized ( confirmed ) - { - return confirmed.getSufficientSources (amount, fee, null); - } - } - public static long estimateFee (Transaction t) { WireFormat.Writer writer = new WireFormat.Writer (); @@ -111,7 +102,7 @@ public static long estimateFee (Transaction t) return Math.min (MAXIMUM_FEE, Math.max (MINIMUM_FEE, writer.toByteArray ().length / 1000 * MINIMUM_FEE)); } - public Transaction createSpend (List sources, List sinks) throws ValidationException + protected Transaction createSpend (List sources, List sinks) throws ValidationException { long fee = MINIMUM_FEE; long prevfee = MINIMUM_FEE; @@ -124,7 +115,7 @@ public Transaction createSpend (List sources, List sources, List sinks, long fee) throws ValidationException + protected Transaction createSpend (List sources, List sinks, long fee) throws ValidationException { if ( fee < 0 || fee > MAXIMUM_FEE ) { @@ -293,13 +284,77 @@ else if ( (hashType & 0x1f) == ScriptFormat.SIGHASH_SINGLE ) return hash; } + private List getSufficientSources (long amount, long fee, String color) + { + List candidates = new ArrayList (); + candidates.addAll (confirmed.getUTXO ()); + // prefer confirmed + Collections.sort (candidates, new Comparator () + { + // prefer aggregation of UTXO + @Override + public int compare (TransactionOutput o1, TransactionOutput o2) + { + return (int) (o1.getValue () - o2.getValue ()); + } + }); + List changelist = new ArrayList (); + changelist.addAll (change.getUTXO ()); + // ... then change + Collections.sort (changelist, new Comparator () + { + // prefer aggregation of UTXO + @Override + public int compare (TransactionOutput o1, TransactionOutput o2) + { + return (int) (o1.getValue () - o2.getValue ()); + } + }); + candidates.addAll (changelist); + + List result = new ArrayList (); + long sum = 0; + for ( TransactionOutput o : candidates ) + { + if ( color == null ) + { + if ( o.getColor () == null ) + { + sum += o.getValue (); + result.add (o); + if ( sum >= (amount + fee) ) + { + return result; + } + } + } + else + { + if ( o.getColor ().equals (color) ) + { + sum += o.getValue (); + result.add (o); + if ( sum >= amount ) + { + if ( fee > 0 ) + { + result.addAll (getSufficientSources (0, fee, null)); + } + return result; + } + } + } + } + return null; + } + @Override public Transaction pay (byte[] receiver, long amount, long fee) throws ValidationException { synchronized ( confirmed ) { log.trace ("pay to " + ByteUtils.toHex (receiver) + " " + amount + " + " + fee); - List sources = confirmed.getSufficientSources (amount, fee, null); + List sources = getSufficientSources (amount, fee, null); if ( sources == null ) { throw new ValidationException ("Insufficient funds to pay " + (amount + fee)); @@ -348,7 +403,7 @@ public Transaction split (long[] amounts, long fee) throws ValidationException { amount += a; } - List sources = confirmed.getSufficientSources (amount, fee, null); + List sources = getSufficientSources (amount, fee, null); if ( sources == null ) { throw new ValidationException ("Insufficient funds to pay " + (amount + fee)); @@ -661,7 +716,7 @@ public void dumpKeys (PrintStream print) } } - private void dumpUTXO (PrintStream print, InMemoryUTXO set) + private void dumpUTXO (PrintStream print, UTXO set) { for ( TransactionOutput o : set.getUTXO () ) { diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java index d06a1806..1e2cc750 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java @@ -135,17 +135,12 @@ public Collection getAddresses () return addresses; } - public ExtendedKeyAccountManager (String name, long created) - { - super (name, created); - } - - public void sync (BCSAPI api, final int lookAhead) throws BCSAPIException, ValidationException + public void sync (BCSAPI api, final int lookAhead, long created) throws BCSAPIException, ValidationException { this.lookAhead = lookAhead; ensureLookAhead (0); - log.trace ("Sync " + getName () + " nkeys: " + getNumberOfKeys ()); - api.scanTransactions (getMaster (), lookAhead, getCreated (), new TransactionListener () + log.trace ("Sync nkeys: " + getNumberOfKeys ()); + api.scanTransactions (getMaster (), lookAhead, created, new TransactionListener () { @Override public void process (Transaction t) @@ -162,6 +157,6 @@ public void process (Transaction t) updateWithTransaction (t); } }); - log.trace ("Sync " + getName () + " finished with nkeys: " + getNumberOfKeys ()); + log.trace ("Sync finished with nkeys: " + getNumberOfKeys ()); } } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeySetWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeySetWallet.java index 6b926aa9..3993513f 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeySetWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeySetWallet.java @@ -16,7 +16,7 @@ public void addKey (String name, ExtendedKey key, long created) throws Validatio throw new ValidationException (name + " account manager exsists"); } ExtendedKeyAccountManager manager; - accountManager.put (name, manager = new ExtendedKeyAccountManager (name, created)); + accountManager.put (name, manager = new ExtendedKeyAccountManager ()); manager.setMaster (key); } @@ -34,7 +34,7 @@ public AccountManager createAccountManager (String name) throws ValidationExcept throw new ValidationException (name + " account manager exsists"); } ExtendedKeyAccountManager manager; - accountManager.put (name, manager = new ExtendedKeyAccountManager (name, System.currentTimeMillis ())); + accountManager.put (name, manager = new ExtendedKeyAccountManager ()); manager.setMaster (ExtendedKey.createNew ()); return manager; } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java index a167b6f4..a3634dab 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java @@ -20,7 +20,30 @@ public class FileWallet implements Wallet private byte[] signature; private String fileName; - private final Map accounts = new HashMap (); + private static class NCExtendedKeyAccountManager extends ExtendedKeyAccountManager + { + private final String name; + private final long created; + + public NCExtendedKeyAccountManager (String name, long created) + { + super (); + this.name = name; + this.created = created; + } + + public String getName () + { + return name; + } + + public long getCreated () + { + return created; + } + } + + private final Map accounts = new HashMap (); public FileWallet (String fileName) { @@ -86,7 +109,7 @@ public static FileWallet read (String fileName) throws IOException, ValidationEx wallet.signature = walletMessage.getSignature ().toByteArray (); for ( BCSAPIMessage.Wallet.Account account : walletMessage.getAccountsList () ) { - ExtendedKeyAccountManager am = new ExtendedKeyAccountManager (account.getName (), account.getCreated () * 1000); + NCExtendedKeyAccountManager am = new NCExtendedKeyAccountManager (account.getName (), account.getCreated () * 1000); wallet.accounts.put (account.getName (), am); am.setMaster (ExtendedKey.parse (account.getPublicKey ())); } @@ -100,9 +123,9 @@ public static FileWallet read (String fileName) throws IOException, ValidationEx public void sync (BCSAPI api, int lookAhead) throws BCSAPIException, ValidationException { - for ( ExtendedKeyAccountManager account : accounts.values () ) + for ( NCExtendedKeyAccountManager account : accounts.values () ) { - account.sync (api, lookAhead); + account.sync (api, lookAhead, account.getCreated ()); } } @@ -125,7 +148,7 @@ public synchronized AccountManager createAccountManager (String name) throws Val { throw new ValidationException ("The wallet is locked"); } - ExtendedKeyAccountManager account = new ExtendedKeyAccountManager (name, System.currentTimeMillis ()); + NCExtendedKeyAccountManager account = new NCExtendedKeyAccountManager (name, System.currentTimeMillis ()); account.setMaster (master.getChild (accounts.size () | 0x80000000)); accounts.put (name, account); return account; @@ -141,7 +164,7 @@ public void persist () throws IOException builder.setBcsapiversion (1); builder.setEncryptedSeed (ByteString.copyFrom (encryptedSeed)); builder.setSignature (ByteString.copyFrom (signature)); - for ( ExtendedKeyAccountManager am : accounts.values () ) + for ( NCExtendedKeyAccountManager am : accounts.values () ) { BCSAPIMessage.Wallet.Account.Builder ab = BCSAPIMessage.Wallet.Account.newBuilder (); ab.setName (am.getName ()); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java index 0e789ec6..b29271aa 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java @@ -15,15 +15,12 @@ */ package com.bitsofproof.supernode.api; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; -import java.util.List; import java.util.Map; -public class InMemoryUTXO +public class InMemoryUTXO implements AccountManager.UTXO { private static class TxOutKey { @@ -53,26 +50,31 @@ public int hashCode () private final Map utxo = new HashMap (); + @Override public void add (TransactionOutput out) { utxo.put (new TxOutKey (out.getTxHash (), out.getIx ()), out); } + @Override public Collection getUTXO () { return Collections.unmodifiableCollection (utxo.values ()); } + @Override public TransactionOutput get (String tx, long ix) { return utxo.get (new TxOutKey (tx, ix)); } + @Override public TransactionOutput remove (String tx, long ix) { return utxo.remove (new TxOutKey (tx, ix)); } + @Override public long getTotal () { long s = 0; @@ -82,54 +84,4 @@ public long getTotal () } return s; } - - public List getSufficientSources (long amount, long fee, String color) - { - List candidates = new ArrayList (); - candidates.addAll (utxo.values ()); - Collections.sort (candidates, new Comparator () - { - // prefer aggregation of UTXO - @Override - public int compare (TransactionOutput o1, TransactionOutput o2) - { - return (int) (o1.getValue () - o2.getValue ()); - } - }); - - List result = new ArrayList (); - long sum = 0; - for ( TransactionOutput o : candidates ) - { - if ( color == null ) - { - if ( o.getColor () == null ) - { - sum += o.getValue (); - result.add (o); - if ( sum >= (amount + fee) ) - { - return result; - } - } - } - else - { - if ( o.getColor ().equals (color) ) - { - sum += o.getValue (); - result.add (o); - if ( sum >= amount ) - { - if ( fee > 0 ) - { - result.addAll (getSufficientSources (0, fee, null)); - } - return result; - } - } - } - } - return null; - } } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java index e31eac3c..eb1524de 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java @@ -24,11 +24,6 @@ public class KeyListAccountManager extends BaseAccountManager private final List keys = new ArrayList (); private static final SecureRandom rnd = new SecureRandom (); - public KeyListAccountManager (String name, long created) - { - super (name, created); - } - @Override public int getNumberOfKeys () { @@ -64,10 +59,10 @@ public void addKey (ECKeyPair key) keyByAddress.put (new ByteVector (key.getAddress ()), key); } - public void sync (BCSAPI api) throws BCSAPIException, ValidationException + public void sync (BCSAPI api, long created) throws BCSAPIException, ValidationException { - log.trace ("Sync " + getName () + " nkeys: " + keys.size ()); - api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () + log.trace ("Sync nkeys: " + keys.size ()); + api.scanTransactions (getAddresses (), UpdateMode.all, created, new TransactionListener () { @Override public void process (Transaction t) @@ -75,6 +70,6 @@ public void process (Transaction t) updateWithTransaction (t); } }); - log.trace ("Sync " + getName () + " finished nkeys: " + keys.size ()); + log.trace ("Sync finished nkeys: " + keys.size ()); } } diff --git a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java index 63dca950..40d0153f 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java @@ -67,26 +67,21 @@ public class APITest private static final long COIN = 100000000L; private static final long FEE = COIN / 1000L; - private static Map blocks = new HashMap (); - - private static Wallet wallet; - private static AccountManager alice; - private static AccountManager bob; private static class AccountMonitor implements AccountListener { private final Semaphore ready = new Semaphore (0); - private final String name; + private final AccountManager account; - public AccountMonitor (String name) + public AccountMonitor (AccountManager account) { - this.name = name; + this.account = account; } @Override public void accountChanged (AccountManager account, Transaction t) { - assertTrue (account.getName ().equals (name)); + assertTrue (this.account == account); ready.release (); } @@ -159,11 +154,6 @@ public void process (Transaction t) } } - private static final AccountMonitor bobMonitor = new AccountMonitor ("Bob"); - private static final AccountMonitor aliceMonitor = new AccountMonitor ("Alice"); - - private static final ValidationMonitor validationMonitor = new ValidationMonitor (); - @BeforeClass public static void provider () { @@ -173,12 +163,19 @@ public static void provider () @Test public void test () throws BCSAPIException, ValidationException, IOException { + Map blocks = new HashMap (); + + Wallet wallet = new ExtendedKeySetWallet (); + AccountManager alice = wallet.createAccountManager ("Alice"); + AccountManager bob = wallet.createAccountManager ("Bob"); + + AccountMonitor bobMonitor = new AccountMonitor (bob); + AccountMonitor aliceMonitor = new AccountMonitor (alice); + ValidationMonitor validationMonitor = new ValidationMonitor (); + store.resetStore (chain); store.cache (chain, 0); - wallet = new ExtendedKeySetWallet (); - alice = wallet.createAccountManager ("Alice"); - bob = wallet.createAccountManager ("Bob"); api.registerTransactionListener (alice); api.registerTransactionListener (bob); From 20bbff0325fa32e5b7ae74acdfdacc745ee5c0a0 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 7 Aug 2013 18:31:13 +0200 Subject: [PATCH 140/299] that's too strict --- .../java/com/bitsofproof/supernode/api/BaseAccountManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java index b51a3af7..02716b3a 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java @@ -284,7 +284,7 @@ else if ( (hashType & 0x1f) == ScriptFormat.SIGHASH_SINGLE ) return hash; } - private List getSufficientSources (long amount, long fee, String color) + protected List getSufficientSources (long amount, long fee, String color) { List candidates = new ArrayList (); candidates.addAll (confirmed.getUTXO ()); From 8b4e6585ff5eb7c7169f070c210e7db15a78b917 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 7 Aug 2013 18:36:59 +0200 Subject: [PATCH 141/299] marking it 1.1.5 --- api/pom.xml | 2 +- pom.xml | 2 +- server/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 649cfc5c..d88d0af1 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.5-SNAPSHOT + 1.1.5 ../ diff --git a/pom.xml b/pom.xml index bd900ed0..4ea989ff 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.1.5-SNAPSHOT + 1.1.5 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/pom.xml b/server/pom.xml index 48936583..23afc17e 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.5-SNAPSHOT + 1.1.5 ../ From 073c73460bc2fb4ea0f09f8b729862d604cbc321 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 7 Aug 2013 19:47:38 +0200 Subject: [PATCH 142/299] update version in target --- server/bop-service.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/bop-service.sh b/server/bop-service.sh index 5855ce82..8cfb33c3 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.5-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.5.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.5-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.5.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" From 1c3b7fc426c38e07a70eb156b7c4898cdaedded1 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 8 Aug 2013 18:13:57 +0200 Subject: [PATCH 143/299] file wallet can now store encryped master in place of seed. --- api/pom.xml | 2 +- .../supernode/api/ExtendedKey.java | 49 +++++++++++++++-- .../api/ExtendedKeyAccountManager.java | 5 +- .../bitsofproof/supernode/api/FileWallet.java | 54 ++++++++++++++----- pom.xml | 2 +- server/pom.xml | 2 +- .../bitsofproof/supernode/core/TxHandler.java | 2 +- 7 files changed, 95 insertions(+), 21 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index d88d0af1..418ecb7b 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.5 + 1.1.6-SNAPSHOT ../ diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java index 84012516..eecbc0bf 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; @@ -30,6 +31,7 @@ import javax.crypto.Mac; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.asn1.sec.SECNamedCurves; @@ -60,15 +62,28 @@ public class ExtendedKey private static final byte[] BITCOIN_SEED = "Bitcoin seed".getBytes (); - public static ExtendedKey createFromPassphrase (String passphrase, byte[] encryptedSeed) throws ValidationException + public static ExtendedKey createFromPassphrase (String passphrase, byte[] encrypted) throws ValidationException { try { byte[] key = SCrypt.generate (passphrase.getBytes ("UTF-8"), BITCOIN_SEED, 16384, 8, 8, 32); - Cipher cipher = Cipher.getInstance ("AES/ECB/NoPadding", "BC"); SecretKeySpec keyspec = new SecretKeySpec (key, "AES"); - cipher.init (Cipher.DECRYPT_MODE, keyspec); - return create (cipher.doFinal (encryptedSeed)); + if ( encrypted.length == 32 ) + { + // asssume encrypted is seed + Cipher cipher = Cipher.getInstance ("AES/ECB/NoPadding", "BC"); + cipher.init (Cipher.DECRYPT_MODE, keyspec); + return create (cipher.doFinal (encrypted)); + } + else + { + // assume encrypted serialization of a key + Cipher cipher = Cipher.getInstance ("AES/CBC/PKCS5Padding", "BC"); + byte[] iv = Arrays.copyOfRange (encrypted, 0, 16); + byte[] data = Arrays.copyOfRange (encrypted, 16, encrypted.length); + cipher.init (Cipher.DECRYPT_MODE, keyspec, new IvParameterSpec (iv)); + return ExtendedKey.parse (new String (cipher.doFinal (data))); + } } catch ( UnsupportedEncodingException e ) { @@ -98,6 +113,32 @@ public static ExtendedKey createFromPassphrase (String passphrase, byte[] encryp { throw new ValidationException (e); } + catch ( InvalidAlgorithmParameterException e ) + { + throw new ValidationException (e); + } + } + + public byte[] encrypt (String passphrase, boolean production) throws ValidationException + { + try + { + byte[] key = SCrypt.generate (passphrase.getBytes ("UTF-8"), BITCOIN_SEED, 16384, 8, 8, 32); + SecretKeySpec keyspec = new SecretKeySpec (key, "AES"); + Cipher cipher = Cipher.getInstance ("AES/CBC/PKCS5Padding", "BC"); + cipher.init (Cipher.ENCRYPT_MODE, keyspec); + byte[] iv = cipher.getIV (); + byte[] c = cipher.doFinal (serialize (production).getBytes ()); + byte[] result = new byte[iv.length + c.length]; + System.arraycopy (iv, 0, result, 0, iv.length); + System.arraycopy (c, 0, result, iv.length, c.length); + return result; + } + catch ( UnsupportedEncodingException | NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException | InvalidKeyException + | IllegalBlockSizeException | BadPaddingException e ) + { + throw new ValidationException (e); + } } public static ExtendedKey create (byte[] seed) throws ValidationException diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java index 1e2cc750..1bfe8589 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java @@ -130,7 +130,10 @@ public Collection getAddresses () List addresses = new ArrayList (); for ( ByteVector v : keyIDForAddress.keySet () ) { - addresses.add (v.toByteArray ()); + if ( keyIDForAddress.get (v).intValue () < nextSequence ) + { + addresses.add (v.toByteArray ()); + } } return addresses; } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java index a3634dab..b79a2972 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java @@ -6,7 +6,9 @@ import java.io.IOException; import java.io.InputStream; import java.security.SecureRandom; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import com.bitsofproof.supernode.common.ValidationException; @@ -16,7 +18,7 @@ public class FileWallet implements Wallet { private static final SecureRandom random = new SecureRandom (); private transient ExtendedKey master; - private byte[] encryptedSeed; + private byte[] encrypted; private byte[] signature; private String fileName; @@ -58,28 +60,45 @@ public boolean exists () public void init (String passphrase) { master = null; - encryptedSeed = new byte[32]; - random.nextBytes (encryptedSeed); + encrypted = new byte[32]; + random.nextBytes (encrypted); try { - signature = ExtendedKey.createFromPassphrase (passphrase, encryptedSeed).getMaster ().sign (encryptedSeed); + signature = ExtendedKey.createFromPassphrase (passphrase, encrypted).getMaster ().sign (encrypted); } catch ( ValidationException e ) { } } + public void init (String passphrase, ExtendedKey master, boolean production) + { + this.master = master; + try + { + encrypted = master.encrypt (passphrase, production); + signature = ExtendedKey.createFromPassphrase (passphrase, encrypted).getMaster ().sign (encrypted); + } + catch ( ValidationException e ) + { + } + } + + public ExtendedKey getMaster () + { + return master; + } + public void unlock (String passphrase) throws ValidationException { - master = ExtendedKey.createFromPassphrase (passphrase, encryptedSeed); - if ( !master.getMaster ().verify (encryptedSeed, signature) ) + master = ExtendedKey.createFromPassphrase (passphrase, encrypted); + if ( !master.getMaster ().verify (encrypted, signature) ) { throw new ValidationException ("incorrect passphrase"); } - int i = 0; - for ( ExtendedKeyAccountManager account : accounts.values () ) + for ( NCExtendedKeyAccountManager account : accounts.values () ) { - account.setMaster (master.getChild (i++ | 0x80000000)); + account.setMaster (master.getChild (account.getMaster ().getSequence ())); } } @@ -92,6 +111,16 @@ public void lock () } } + public List getAccountNames () + { + List names = new ArrayList (); + for ( NCExtendedKeyAccountManager account : accounts.values () ) + { + names.add (account.getName ()); + } + return names; + } + public void setFileName (String fileName) { this.fileName = fileName; @@ -105,13 +134,14 @@ public static FileWallet read (String fileName) throws IOException, ValidationEx try { BCSAPIMessage.Wallet walletMessage = BCSAPIMessage.Wallet.parseFrom (in); - wallet.encryptedSeed = walletMessage.getEncryptedSeed ().toByteArray (); + wallet.encrypted = walletMessage.getEncryptedSeed ().toByteArray (); wallet.signature = walletMessage.getSignature ().toByteArray (); for ( BCSAPIMessage.Wallet.Account account : walletMessage.getAccountsList () ) { + ExtendedKey pub = ExtendedKey.parse (account.getPublicKey ()); NCExtendedKeyAccountManager am = new NCExtendedKeyAccountManager (account.getName (), account.getCreated () * 1000); wallet.accounts.put (account.getName (), am); - am.setMaster (ExtendedKey.parse (account.getPublicKey ())); + am.setMaster (pub); } } finally @@ -162,7 +192,7 @@ public void persist () throws IOException { BCSAPIMessage.Wallet.Builder builder = BCSAPIMessage.Wallet.newBuilder (); builder.setBcsapiversion (1); - builder.setEncryptedSeed (ByteString.copyFrom (encryptedSeed)); + builder.setEncryptedSeed (ByteString.copyFrom (encrypted)); builder.setSignature (ByteString.copyFrom (signature)); for ( NCExtendedKeyAccountManager am : accounts.values () ) { diff --git a/pom.xml b/pom.xml index 4ea989ff..541a2d22 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.1.5 + 1.1.6-SNAPSHOT supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/pom.xml b/server/pom.xml index 23afc17e..a6954827 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.5 + 1.1.6-SNAPSHOT ../ diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index 33748e7f..a16f25c7 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -186,8 +186,8 @@ public ValidationException doInTransaction (TransactionStatus status) if ( network.getStore ().getTransaction (t.getHash ()) == null ) { network.getStore ().validateTransaction (t, availableOutput); - sendTransaction (t, peer); cacheTransaction (t); + sendTransaction (t, peer); notifyListener (t, false); } return null; From 01be8e24197d0ee44b716bfe2f9f9d460200027a Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 8 Aug 2013 18:18:56 +0200 Subject: [PATCH 144/299] use 1.1.6-SNAPSHOT --- server/bop-service.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/bop-service.sh b/server/bop-service.sh index 8cfb33c3..c0e36f91 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.5.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.6-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.5.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.6-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" From b263791cfb8fa96013b23a7b0a23f9ecd2b11dae Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 8 Aug 2013 21:43:24 +0200 Subject: [PATCH 145/299] created is always needed --- .../supernode/api/AccountManager.java | 2 ++ .../supernode/api/AddressListAccountManager.java | 4 ++-- .../supernode/api/BaseAccountManager.java | 13 +++++++++++++ .../supernode/api/ExtendedKeyAccountManager.java | 4 ++-- .../bitsofproof/supernode/api/FileWallet.java | 16 ++++++---------- .../supernode/api/KeyListAccountManager.java | 4 ++-- 6 files changed, 27 insertions(+), 16 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java index 6fd1b772..5755c955 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java @@ -38,6 +38,8 @@ public interface UTXO } + public long getCreated (); + public int getNumberOfKeys (); public Key getNextKey () throws ValidationException; diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java index 646b3c52..6b2b852e 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java @@ -60,10 +60,10 @@ public void addAddress (byte[] address) addresses.add (new ByteVector (address)); } - public void sync (BCSAPI api, long created) throws BCSAPIException, ValidationException + public void sync (BCSAPI api) throws BCSAPIException, ValidationException { log.trace ("Sync naddr: " + addresses.size ()); - api.scanTransactions (getAddresses (), UpdateMode.all, created, new TransactionListener () + api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () { @Override public void process (Transaction t) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java index 02716b3a..69a60841 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java @@ -39,6 +39,19 @@ public abstract class BaseAccountManager implements AccountManager private final UTXO receiving = createReceivingUTXO (); private final UTXO sending = createSendingUTXO (); + private long created; + + @Override + public long getCreated () + { + return created; + } + + public void setCreated (long created) + { + this.created = created; + } + private final List accountListener = Collections.synchronizedList (new ArrayList ()); private final Map transactions = new HashMap (); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java index 1bfe8589..a049af39 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java @@ -138,12 +138,12 @@ public Collection getAddresses () return addresses; } - public void sync (BCSAPI api, final int lookAhead, long created) throws BCSAPIException, ValidationException + public void sync (BCSAPI api, final int lookAhead) throws BCSAPIException, ValidationException { this.lookAhead = lookAhead; ensureLookAhead (0); log.trace ("Sync nkeys: " + getNumberOfKeys ()); - api.scanTransactions (getMaster (), lookAhead, created, new TransactionListener () + api.scanTransactions (getMaster (), lookAhead, getCreated (), new TransactionListener () { @Override public void process (Transaction t) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java index b79a2972..236c1721 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java @@ -21,28 +21,23 @@ public class FileWallet implements Wallet private byte[] encrypted; private byte[] signature; private String fileName; + private long since; private static class NCExtendedKeyAccountManager extends ExtendedKeyAccountManager { private final String name; - private final long created; public NCExtendedKeyAccountManager (String name, long created) { super (); this.name = name; - this.created = created; + setCreated (created); } public String getName () { return name; } - - public long getCreated () - { - return created; - } } private final Map accounts = new HashMap (); @@ -71,9 +66,10 @@ public void init (String passphrase) } } - public void init (String passphrase, ExtendedKey master, boolean production) + public void init (String passphrase, ExtendedKey master, boolean production, long since) { this.master = master; + this.since = since; try { encrypted = master.encrypt (passphrase, production); @@ -155,7 +151,7 @@ public void sync (BCSAPI api, int lookAhead) throws BCSAPIException, ValidationE { for ( NCExtendedKeyAccountManager account : accounts.values () ) { - account.sync (api, lookAhead, account.getCreated ()); + account.sync (api, lookAhead); } } @@ -178,7 +174,7 @@ public synchronized AccountManager createAccountManager (String name) throws Val { throw new ValidationException ("The wallet is locked"); } - NCExtendedKeyAccountManager account = new NCExtendedKeyAccountManager (name, System.currentTimeMillis ()); + NCExtendedKeyAccountManager account = new NCExtendedKeyAccountManager (name, Math.min (System.currentTimeMillis (), since)); account.setMaster (master.getChild (accounts.size () | 0x80000000)); accounts.put (name, account); return account; diff --git a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java index eb1524de..a556589e 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java @@ -59,10 +59,10 @@ public void addKey (ECKeyPair key) keyByAddress.put (new ByteVector (key.getAddress ()), key); } - public void sync (BCSAPI api, long created) throws BCSAPIException, ValidationException + public void sync (BCSAPI api) throws BCSAPIException, ValidationException { log.trace ("Sync nkeys: " + keys.size ()); - api.scanTransactions (getAddresses (), UpdateMode.all, created, new TransactionListener () + api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () { @Override public void process (Transaction t) From 91cfb899868b9d206a8bbd3e69ea0706458f55b6 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 8 Aug 2013 22:02:18 +0200 Subject: [PATCH 146/299] 1.1.6 --- api/pom.xml | 2 +- pom.xml | 2 +- server/bop-service.sh | 4 ++-- server/pom.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 418ecb7b..9c86d7c8 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.6-SNAPSHOT + 1.1.6 ../ diff --git a/pom.xml b/pom.xml index 541a2d22..7ce7eea0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.1.6-SNAPSHOT + 1.1.6 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/bop-service.sh b/server/bop-service.sh index c0e36f91..f6ee6170 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.6-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.6.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.6-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.6.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" diff --git a/server/pom.xml b/server/pom.xml index a6954827..f4ab2d3a 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.6-SNAPSHOT + 1.1.6 ../ From 4d7756565786cb861fc2123ae237d7e7793c88ee Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Mon, 12 Aug 2013 17:33:41 +0200 Subject: [PATCH 147/299] reset use at trunk update, since resolveInputs sets use, this disallows connecting to inputs with transactions remained in the unconfirmed pool after block came in. --- api/pom.xml | 2 +- pom.xml | 2 +- server/pom.xml | 2 +- .../bitsofproof/supernode/core/ImplementTxOutCacheDelta.java | 5 +++++ .../main/java/com/bitsofproof/supernode/core/TxHandler.java | 1 + 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 9c86d7c8..8392706a 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.6 + 1.1.7-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index 7ce7eea0..25d622d9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.1.6 + 1.1.7-SNAPSHOT supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/pom.xml b/server/pom.xml index f4ab2d3a..23a4dd9b 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.6 + 1.1.7-SNAPSHOT ../ diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementTxOutCacheDelta.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementTxOutCacheDelta.java index 91ffd89c..bb7c9e77 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementTxOutCacheDelta.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementTxOutCacheDelta.java @@ -41,6 +41,11 @@ public void reset () added.clear (); } + public void resetUse () + { + used.clear (); + } + @Override public TxOut get (String hash, Long ix) { diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index a16f25c7..bc55cc1d 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -345,6 +345,7 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo notifyListener (tx, true); } } + availableOutput.resetUse (); for ( Tx tx : firstSeenInBlock ) { notifyListener (tx, false); From ff99fb4e01eaccb7ebe40a38323199c9ff1f489c Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Mon, 12 Aug 2013 17:36:20 +0200 Subject: [PATCH 148/299] lets bump version as this is an important fix --- api/pom.xml | 2 +- pom.xml | 2 +- server/bop-service.sh | 4 ++-- server/pom.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 8392706a..f6775c56 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.7-SNAPSHOT + 1.1.7 ../ diff --git a/pom.xml b/pom.xml index 25d622d9..f22347c4 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.1.7-SNAPSHOT + 1.1.7 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/bop-service.sh b/server/bop-service.sh index f6ee6170..398cecce 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.6.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.7.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.6.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.7.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" diff --git a/server/pom.xml b/server/pom.xml index 23a4dd9b..bf703991 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.7-SNAPSHOT + 1.1.7 ../ From c9d111b6382a10d86392758ccc57ca8493eff72e Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 14 Aug 2013 20:04:55 +0200 Subject: [PATCH 149/299] temporary hack around a deadlock --- api/pom.xml | 2 +- pom.xml | 2 +- server/pom.xml | 2 +- .../bitsofproof/supernode/core/TxHandler.java | 68 +++++++++++-------- 4 files changed, 43 insertions(+), 31 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index f6775c56..77f547bf 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.7 + 1.1.8-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index f22347c4..623eb902 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.1.7 + 1.1.8-SNAPSHOT supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/pom.xml b/server/pom.xml index bf703991..a2d7e731 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.7 + 1.1.8-SNAPSHOT ../ diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index bc55cc1d..42f86a80 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.Semaphore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -165,50 +166,61 @@ public void process (final MempoolMessage m, final BitcoinPeer peer) } + private final Semaphore serializedValidation = new Semaphore (1); + public void validateCacheAndSend (final Tx t, final BitcoinPeer peer) throws ValidationException { - ValidationException exception = network.getStore ().runInCacheContext (new BlockStore.CacheContextRunnable () + try { - @Override - public void run (TxOutCache cache) throws ValidationException + serializedValidation.acquireUninterruptibly (); + + ValidationException exception = network.getStore ().runInCacheContext (new BlockStore.CacheContextRunnable () { - if ( !unconfirmed.containsKey (t.getHash ()) ) + @Override + public void run (TxOutCache cache) throws ValidationException { - ValidationException exception = new TransactionTemplate (transactionManager).execute (new TransactionCallback () + if ( !unconfirmed.containsKey (t.getHash ()) ) { - @Override - public ValidationException doInTransaction (TransactionStatus status) + ValidationException exception = new TransactionTemplate (transactionManager).execute (new TransactionCallback () { - status.setRollbackOnly (); - - try + @Override + public ValidationException doInTransaction (TransactionStatus status) { - if ( network.getStore ().getTransaction (t.getHash ()) == null ) + status.setRollbackOnly (); + + try { - network.getStore ().validateTransaction (t, availableOutput); - cacheTransaction (t); - sendTransaction (t, peer); - notifyListener (t, false); + if ( network.getStore ().getTransaction (t.getHash ()) == null ) + { + network.getStore ().validateTransaction (t, availableOutput); + cacheTransaction (t); + sendTransaction (t, peer); + notifyListener (t, false); + } + return null; + } + catch ( ValidationException e ) + { + return e; } - return null; - } - catch ( ValidationException e ) - { - return e; } + }); + if ( exception != null ) + { + throw exception; } - }); - if ( exception != null ) - { - throw exception; } } + }); + if ( exception != null ) + { + log.debug ("REJECTING transaction " + t.getHash ()); + throw exception; } - }); - if ( exception != null ) + } + finally { - log.debug ("REJECTING transaction " + t.getHash ()); - throw exception; + serializedValidation.release (); } } From 2b5c9000847a4fab61e1899837976de209ed6106 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 14 Aug 2013 20:07:17 +0200 Subject: [PATCH 150/299] bump version --- server/bop-service.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/bop-service.sh b/server/bop-service.sh index 398cecce..c724a898 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.7.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.8-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.7.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.8-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" From e1bc367c284bcbc7f82985fa80ca0f98fe634b92 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 15 Aug 2013 13:20:14 +0200 Subject: [PATCH 151/299] removing relational database support and peer store. --- server/bop-service.sh | 4 +- server/pom.xml | 117 +---- server/src/main/assembly/supernode.xml | 35 -- .../supernode/core/AddressHandler.java | 20 - .../supernode/core/BitcoinNetwork.java | 11 - .../supernode/core/BitcoinPeer.java | 66 --- .../supernode/core/BlockStore.java | 2 +- .../supernode/core/CachedBlockStore.java | 141 +----- .../supernode/core/ColorStore.java | 28 -- .../supernode/core/GetDataHandler.java | 127 ++---- .../supernode/core/GetHeadersHandler.java | 42 +- .../supernode/core/HeartbeatHandler.java | 18 - .../supernode/core/ImplementBCSAPI.java | 117 ++--- .../bitsofproof/supernode/core/PeerStore.java | 32 -- .../bitsofproof/supernode/core/TxHandler.java | 41 +- .../com/bitsofproof/supernode/model/Blk.java | 21 - .../com/bitsofproof/supernode/model/Head.java | 12 +- .../bitsofproof/supernode/model/JpaStore.java | 430 ------------------ .../supernode/model/KnownPeer.java | 14 - .../bitsofproof/supernode/model/LvlStore.java | 155 +------ .../supernode/model/LvlTransaction.java | 71 --- .../supernode/model/StoredColor.java | 187 -------- .../com/bitsofproof/supernode/model/Tx.java | 24 +- .../com/bitsofproof/supernode/model/TxIn.java | 21 - .../bitsofproof/supernode/model/TxOut.java | 21 - .../main/resources/context/derby-profile.xml | 47 -- .../main/resources/context/memdb-profile.xml | 42 -- .../main/resources/context/persistence.xml | 7 - .../resources/context/postgresql-profile.xml | 47 -- .../resources/context/relational-profile.xml | 49 -- .../main/resources/context/remote-profile.xml | 49 -- server/src/main/resources/context/server.xml | 3 - .../test/resources/context/EmbeddedBCSAPI.xml | 2 - .../test/resources/context/leveldb-store.xml | 8 - .../test/resources/context/memory-store.xml | 7 - .../test/resources/context/persistence.xml | 18 - server/src/test/resources/context/store1.xml | 49 -- server/src/test/resources/context/store2.xml | 49 -- server/src/test/resources/context/store3.xml | 49 -- 39 files changed, 129 insertions(+), 2054 deletions(-) delete mode 100644 server/src/main/assembly/supernode.xml delete mode 100644 server/src/main/java/com/bitsofproof/supernode/core/ColorStore.java delete mode 100644 server/src/main/java/com/bitsofproof/supernode/core/PeerStore.java delete mode 100644 server/src/main/java/com/bitsofproof/supernode/model/JpaStore.java delete mode 100644 server/src/main/java/com/bitsofproof/supernode/model/LvlTransaction.java delete mode 100644 server/src/main/java/com/bitsofproof/supernode/model/StoredColor.java delete mode 100644 server/src/main/resources/context/derby-profile.xml delete mode 100644 server/src/main/resources/context/memdb-profile.xml delete mode 100644 server/src/main/resources/context/persistence.xml delete mode 100644 server/src/main/resources/context/postgresql-profile.xml delete mode 100644 server/src/main/resources/context/relational-profile.xml delete mode 100644 server/src/main/resources/context/remote-profile.xml delete mode 100644 server/src/test/resources/context/persistence.xml delete mode 100644 server/src/test/resources/context/store1.xml delete mode 100644 server/src/test/resources/context/store2.xml delete mode 100644 server/src/test/resources/context/store3.xml diff --git a/server/bop-service.sh b/server/bop-service.sh index c724a898..6ecd9008 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.8-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.1.8-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" diff --git a/server/pom.xml b/server/pom.xml index a2d7e731..5512f57e 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.8-SNAPSHOT + 1.2-SNAPSHOT ../ @@ -47,112 +47,16 @@ protobuf-java ${protobuf.version} - - org.apache.derby - derbyclient - ${derby.version} - - - org.apache.derby - derby - ${derby.version} - - - postgresql - postgresql - ${postgresql.version} - org.springframework spring-context ${springframework.version} - - org.springframework - spring-web - ${springframework.version} - org.springframework spring-test ${springframework.version} - - org.springframework - spring-webmvc - ${springframework.version} - - - org.springframework - spring-orm - ${springframework.version} - - - org.springframework.data - spring-data-jpa - ${spring-data.version} - - - org.springframework - spring-beans - - - org.springframework - spring-orm - - - org.springframework - spring-context - - - org.springframework - spring-aop - - - org.springframework - spring-tx - - - org.springframework - spring-core - - - - - javax.validation - validation-api - 1.0.0.GA - - - org.eclipse.persistence - javax.persistence - 2.0.0 - - - com.mysema.querydsl - querydsl-jpa - ${querydsl.version} - - - com.mysema.querydsl - querydsl-apt - ${querydsl.version} - - - org.hibernate - hibernate-entitymanager - ${hibernate.version} - - - org.hibernate - hibernate-core - ${hibernate.version} - - - org.hibernate - hibernate-validator - ${hibernate-validator.version} - org.apache.geronimo.specs geronimo-jms_1.1_spec @@ -207,25 +111,6 @@ - - maven-compiler-plugin - 2.3.2 - - 1.7 - 1.7 - - com.mysema.query.apt.jpa.JPAAnnotationProcessor - - - - - org.apache.maven.plugins - maven-assembly-plugin - 2.4 - - src/main/assembly/supernode.xml - - com.google.protobuf.tools maven-protoc-plugin diff --git a/server/src/main/assembly/supernode.xml b/server/src/main/assembly/supernode.xml deleted file mode 100644 index e13994f7..00000000 --- a/server/src/main/assembly/supernode.xml +++ /dev/null @@ -1,35 +0,0 @@ - - dist - - tar.gz - tar.bz2 - zip - - - - ${project.basedir} - / - - README* - LICENSE* - NOTICE* - - - - ${project.build.directory} - / - - *.jar - - - - ${project.build.directory}/site - docs - - - ${project.build.directory}/lib - lib - - - diff --git a/server/src/main/java/com/bitsofproof/supernode/core/AddressHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/AddressHandler.java index 3433f37a..53c014d4 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/AddressHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/AddressHandler.java @@ -20,13 +20,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.common.WireFormat; import com.bitsofproof.supernode.common.WireFormat.Address; import com.bitsofproof.supernode.messages.AddrMessage; import com.bitsofproof.supernode.messages.BitcoinMessageListener; import com.bitsofproof.supernode.messages.GetAddrMessage; -import com.bitsofproof.supernode.model.KnownPeer; public class AddressHandler implements BitcoinMessageListener { @@ -66,24 +64,6 @@ public void process (AddrMessage am, BitcoinPeer peer) { log.trace ("received address " + a.address + " from " + peer.getAddress ().getAddress ()); peer.getNetwork ().addPeer (peer.getAddress ()); - if ( network.getPeerStore () != null ) - { - KnownPeer p; - try - { - p = network.getPeerStore ().findPeer (a.address); - if ( p == null ) - { - p = new KnownPeer (); - p.setAddress (a.address.getHostAddress () + ":" + a.port); - p.setResponseTime (Integer.MAX_VALUE); - network.getPeerStore ().store (p); - } - } - catch ( ValidationException e ) - { - } - } } } } diff --git a/server/src/main/java/com/bitsofproof/supernode/core/BitcoinNetwork.java b/server/src/main/java/com/bitsofproof/supernode/core/BitcoinNetwork.java index a4fac209..d9a52423 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/BitcoinNetwork.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/BitcoinNetwork.java @@ -44,7 +44,6 @@ public BitcoinNetwork (int connections) throws IOException private Chain chain; private BlockStore store; private Discovery discovery; - private PeerStore peerStore; private long protocolVersion = 70001; private final long versionNonce = new SecureRandom ().nextLong (); @@ -285,16 +284,6 @@ public void setDiscovery (Discovery discovery) this.discovery = discovery; } - public PeerStore getPeerStore () - { - return peerStore; - } - - public void setPeerStore (PeerStore peerStore) - { - this.peerStore = peerStore; - } - public long getProtocolVersion () { return protocolVersion; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/BitcoinPeer.java b/server/src/main/java/com/bitsofproof/supernode/core/BitcoinPeer.java index 6d04e0c1..ad94f0f7 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/BitcoinPeer.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/BitcoinPeer.java @@ -55,7 +55,6 @@ import com.bitsofproof.supernode.messages.PongMessage; import com.bitsofproof.supernode.messages.TxMessage; import com.bitsofproof.supernode.messages.VersionMessage; -import com.bitsofproof.supernode.model.KnownPeer; public class BitcoinPeer extends P2P.Peer { @@ -271,42 +270,6 @@ public void process (VersionMessage v, BitcoinPeer peer) peerVersion = Math.min (peerVersion, v.getVersion ()); peerServices = v.getServices (); relay = v.isRelay (); - if ( network.getPeerStore () != null ) - { - KnownPeer p; - try - { - p = network.getPeerStore ().findPeer (getAddress ().getAddress ()); - if ( p == null ) - { - p = new KnownPeer (); - } - trafficIn = p.getTrafficIn (); - trafficOut = p.getTrafficOut (); - if ( p.getBanned () > System.currentTimeMillis () / 1000 ) - { - log.trace ("Disconnecting banned peer " + address); - peer.disconnect (); - } - else - { - p.setAddress (address.getAddress ().getHostAddress ()); - p.setName (address.toString ()); - p.setVersion (peerVersion); - p.setHeight (height); - p.setAgent (agent); - p.setServices (peerServices); - p.setResponseTime (Integer.MAX_VALUE); - p.setConnected (System.currentTimeMillis () / 1000); - network.getPeerStore ().store (p); - } - } - catch ( ValidationException e ) - { - log.error ("Can not read peer", e); - } - - } if ( !outgoing ) { onConnect (); @@ -374,35 +337,6 @@ protected void onDisconnect (long timeout, long bannedForSeconds, String reason) { network.notifyPeerRemoved (this); log.info ("Disconnected '" + getAgent () + "' at " + getAddress ()); - if ( network.getPeerStore () != null ) - { - KnownPeer p; - try - { - p = network.getPeerStore ().findPeer (getAddress ().getAddress ()); - if ( p != null ) - { - p.setTrafficIn (trafficIn); - p.setTrafficOut (trafficOut); - p.setDisconnected (System.currentTimeMillis () / 1000); - if ( timeout > 0 ) - { - p.setResponseTime (timeout * 1000 + 1); - } - if ( bannedForSeconds > 0 ) - { - p.setBanned (System.currentTimeMillis () / 1000 + bannedForSeconds); - p.setBanReason (reason); - } - network.getPeerStore ().store (p); - } - } - catch ( ValidationException e ) - { - log.error ("Can not read peer", e); - } - - } } private static final int MAX_BLOCK_SIZE = 1000000 + 80; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java index d5eb2865..f58eeb82 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java @@ -26,7 +26,7 @@ import com.bitsofproof.supernode.model.Blk; import com.bitsofproof.supernode.model.Tx; -public interface BlockStore extends ColorStore +public interface BlockStore { public interface CacheContextRunnable { diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 3dc7b695..ba1019bd 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -17,7 +17,6 @@ import java.math.BigInteger; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -35,13 +34,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.support.TransactionCallback; -import org.springframework.transaction.support.TransactionTemplate; import com.bitsofproof.supernode.api.ColorRules; import com.bitsofproof.supernode.api.ColorRules.ColoredCoin; @@ -57,7 +49,6 @@ import com.bitsofproof.supernode.common.WireFormat; import com.bitsofproof.supernode.model.Blk; import com.bitsofproof.supernode.model.Head; -import com.bitsofproof.supernode.model.StoredColor; import com.bitsofproof.supernode.model.Tx; import com.bitsofproof.supernode.model.TxIn; import com.bitsofproof.supernode.model.TxOut; @@ -95,9 +86,6 @@ public abstract class CachedBlockStore implements BlockStore protected Chain chain; - @Autowired - PlatformTransactionManager transactionManager; - private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock (); private boolean enforceV2Block = false; @@ -105,7 +93,6 @@ public abstract class CachedBlockStore implements BlockStore protected CachedHead currentHead = null; protected final Map cachedBlocks = new HashMap (); protected final Map cachedHeads = new HashMap (); - protected final Map cachedColors = new HashMap (); private final LinkedList blockChain = new LinkedList (); @@ -351,7 +338,6 @@ public boolean equals (Object o) } @Override - @Transactional (propagation = Propagation.REQUIRED, readOnly = true) public void cache (Chain chain, int size) throws ValidationException { this.chain = chain; @@ -413,7 +399,6 @@ else if ( blockChain.getLast () != p ) blockChain.addAll (appendList); } - @Transactional (propagation = Propagation.REQUIRED, rollbackFor = { Exception.class }, readOnly = true) @Override public void filterTransactions (Set matchSet, ExtendedKey ek, int lookAhead, long after, TransactionProcessor processor) throws ValidationException @@ -505,7 +490,6 @@ public void filterTransactions (Set matchSet, ExtendedKey ek, int lo log.trace ("Done with Match request"); } - @Transactional (propagation = Propagation.REQUIRED, rollbackFor = { Exception.class }, readOnly = true) @Override public void filterTransactions (Set matchSet, UpdateMode update, long after, TransactionProcessor processor) throws ValidationException { @@ -762,42 +746,24 @@ public void storeBlock (final Blk b) throws ValidationException { try { - // have to lock before transaction starts and unlock after finished. - // otherwise updates to transient vs. persistent structures are out of sync for a - // concurrent tx. This does not apply to methods using MANDATORY transaction annotation - // context since they must have been invoked within a transaction. lock.writeLock ().lock (); - ValidationException e = new TransactionTemplate (transactionManager).execute (new TransactionCallback () + try { - @Override - public ValidationException doInTransaction (TransactionStatus status) - { - try - { - startBatch (); - lockedStoreBlock (b); - endBatch (); - } - catch ( ValidationException e ) - { - cancelBatch (); - status.setRollbackOnly (); - return e; - } - catch ( Exception e ) - { - cancelBatch (); - status.setRollbackOnly (); - return new ValidationException (e); - } - return null; - } - }); - if ( e != null ) + startBatch (); + lockedStoreBlock (b); + endBatch (); + } + catch ( ValidationException e ) { + cancelBatch (); throw e; } + catch ( Exception e ) + { + cancelBatch (); + throw new ValidationException (e); + } } finally { @@ -1598,7 +1564,6 @@ public String getHeadHash () } } - @Transactional (propagation = Propagation.REQUIRED, rollbackFor = { Exception.class }) @Override public void resetStore (Chain chain) throws ValidationException { @@ -1617,7 +1582,6 @@ public void resetStore (Chain chain) throws ValidationException insertBlock (genesis); } - @Transactional (propagation = Propagation.MANDATORY, readOnly = true) @Override public Blk getBlock (String hash) throws ValidationException { @@ -1639,7 +1603,6 @@ public Blk getBlock (String hash) throws ValidationException return retrieveBlock (cached); } - @Transactional (propagation = Propagation.MANDATORY, readOnly = true) @Override public Blk getBlockHeader (String hash) throws ValidationException { @@ -1661,7 +1624,6 @@ public Blk getBlockHeader (String hash) throws ValidationException return retrieveBlockHeader (cached); } - @Transactional (propagation = Propagation.MANDATORY, readOnly = true) @Override public void resolveTransactionInputs (Tx t, TxOutCache resolvedInputs) throws ValidationException { @@ -1677,7 +1639,6 @@ public void resolveTransactionInputs (Tx t, TxOutCache resolvedInputs) throws Va } } - @Transactional (propagation = Propagation.MANDATORY, readOnly = true) @Override public boolean validateTransaction (Tx t, TxOutCache resolvedInputs) throws ValidationException { @@ -1709,82 +1670,4 @@ public boolean validateTransaction (Tx t, TxOutCache resolvedInputs) throws Vali lock.readLock ().unlock (); } } - - @Override - public void issueColor (final StoredColor color) throws ValidationException - { - try - { - // have to lock before transaction starts and unlock after finished. - // otherwise updates to transient vs. persistent structures are out of sync for a - // concurrent tx. This does not apply to methods using MANDATORY transaction annotation - // context since they must have been invoked within a transaction. - lock.writeLock ().lock (); - - ValidationException e = new TransactionTemplate (transactionManager).execute (new TransactionCallback () - { - @Override - public ValidationException doInTransaction (TransactionStatus status) - { - try - { - startBatch (); - - Tx tx = getTransaction (color.getTxHash ()); - if ( tx == null ) - { - throw new ValidationException ("Unknown transaction for new color"); - } - TxOut out = tx.getOutputs ().get (0); - if ( !out.isAvailable () ) - { - throw new ValidationException ("The color genesis was already spent."); - } - if ( !ScriptFormat.isPayToAddress (out.getScript ()) ) - { - throw new ValidationException ("Color output should pay to address"); - } - List tokens = ScriptFormat.parse (out.getScript ()); - if ( !Arrays.equals (tokens.get (2).data, Hash.keyHash (color.getPubkey ())) ) - { - throw new ValidationException ("Color key does not match output address"); - } - if ( !color.verify () ) - { - storeColor (color); - } - else - { - throw new ValidationException ("Color is not valid"); - } - - updateColor (out, color.getFungibleName ()); - - endBatch (); - } - catch ( ValidationException e ) - { - cancelBatch (); - status.setRollbackOnly (); - return e; - } - catch ( Exception e ) - { - cancelBatch (); - status.setRollbackOnly (); - return new ValidationException (e); - } - return null; - } - }); - if ( e != null ) - { - throw e; - } - } - finally - { - lock.writeLock ().unlock (); - } - } } diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ColorStore.java b/server/src/main/java/com/bitsofproof/supernode/core/ColorStore.java deleted file mode 100644 index b02fc578..00000000 --- a/server/src/main/java/com/bitsofproof/supernode/core/ColorStore.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2013 bits of proof zrt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.bitsofproof.supernode.core; - -import com.bitsofproof.supernode.common.ValidationException; -import com.bitsofproof.supernode.model.StoredColor; - -public interface ColorStore -{ - public void issueColor (StoredColor color) throws ValidationException; - - public void storeColor (StoredColor color) throws ValidationException; - - public StoredColor findColor (String hash) throws ValidationException; -} \ No newline at end of file diff --git a/server/src/main/java/com/bitsofproof/supernode/core/GetDataHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/GetDataHandler.java index 6c729b9d..bd44a03b 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/GetDataHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/GetDataHandler.java @@ -17,10 +17,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.support.TransactionCallbackWithoutResult; -import org.springframework.transaction.support.TransactionTemplate; import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.ValidationException; @@ -39,12 +35,6 @@ public class GetDataHandler implements BitcoinMessageListener private final BlockStore store; private TxHandler txHandler; - private PlatformTransactionManager transactionManager; - - public void setTransactionManager (PlatformTransactionManager transactionManager) - { - this.transactionManager = transactionManager; - } public GetDataHandler (BitcoinNetwork network) { @@ -76,87 +66,70 @@ public void process (final GetDataMessage m, final BitcoinPeer peer) if ( m.getBlocks ().size () > 0 ) { - new TransactionTemplate (transactionManager).execute (new TransactionCallbackWithoutResult () + for ( final byte[] h : m.getBlocks () ) { - @Override - protected void doInTransactionWithoutResult (TransactionStatus status) + Blk b; + try { - status.setRollbackOnly (); - - for ( final byte[] h : m.getBlocks () ) + b = store.getBlock (new Hash (h).toString ()); + if ( b != null ) { - Blk b; - try - { - b = store.getBlock (new Hash (h).toString ()); - if ( b != null ) - { - final BlockMessage bm = (BlockMessage) peer.createMessage ("block"); + final BlockMessage bm = (BlockMessage) peer.createMessage ("block"); - bm.setBlock (b); - peer.send (bm); - log.trace ("sent block " + b.getHash () + " to " + peer.getAddress ()); - } - } - catch ( ValidationException e ) - { - } - - } - if ( m.getBlocks ().size () > 1 ) - { - log.debug ("sent " + m.getBlocks ().size () + " blocks to " + peer.getAddress ()); + bm.setBlock (b); + peer.send (bm); + log.trace ("sent block " + b.getHash () + " to " + peer.getAddress ()); } - else - { - peer.send (peer.createMessage ("notfound")); - } - InvMessage inv = (InvMessage) peer.createMessage ("inv"); - inv.getBlockHashes ().add (new Hash (store.getHeadHash ()).toByteArray ()); - peer.send (inv); } - }); + catch ( ValidationException e ) + { + } + + } + if ( m.getBlocks ().size () > 1 ) + { + log.debug ("sent " + m.getBlocks ().size () + " blocks to " + peer.getAddress ()); + } + else + { + peer.send (peer.createMessage ("notfound")); + } + InvMessage inv = (InvMessage) peer.createMessage ("inv"); + inv.getBlockHashes ().add (new Hash (store.getHeadHash ()).toByteArray ()); + peer.send (inv); } if ( m.getFilteredBlocks ().size () > 0 && peer.getFilter () != null ) { - new TransactionTemplate (transactionManager).execute (new TransactionCallbackWithoutResult () + for ( final byte[] h : m.getBlocks () ) { - @Override - protected void doInTransactionWithoutResult (TransactionStatus status) + Blk b; + try { - status.setRollbackOnly (); - for ( final byte[] h : m.getBlocks () ) - { - Blk b; - try - { - b = store.getBlock (new Hash (h).toString ()); - if ( b != null ) - { - final MerkleBlockMessage bm = (MerkleBlockMessage) peer.createMessage ("merkleblock"); - bm.setBlock (b); - bm.filter (peer.getFilter ()); - peer.send (bm); - log.trace ("sent block " + b.getHash () + " to " + peer.getAddress ()); - } - } - catch ( ValidationException e ) - { - } - } - if ( m.getBlocks ().size () > 1 ) - { - log.debug ("sent " + m.getBlocks ().size () + " merkleblocks to " + peer.getAddress ()); - } - else + b = store.getBlock (new Hash (h).toString ()); + if ( b != null ) { - peer.send (peer.createMessage ("notfound")); + final MerkleBlockMessage bm = (MerkleBlockMessage) peer.createMessage ("merkleblock"); + bm.setBlock (b); + bm.filter (peer.getFilter ()); + peer.send (bm); + log.trace ("sent block " + b.getHash () + " to " + peer.getAddress ()); } - InvMessage inv = (InvMessage) peer.createMessage ("inv"); - inv.getBlockHashes ().add (new Hash (store.getHeadHash ()).toByteArray ()); - peer.send (inv); } - }); + catch ( ValidationException e ) + { + } + } + if ( m.getBlocks ().size () > 1 ) + { + log.debug ("sent " + m.getBlocks ().size () + " merkleblocks to " + peer.getAddress ()); + } + else + { + peer.send (peer.createMessage ("notfound")); + } + InvMessage inv = (InvMessage) peer.createMessage ("inv"); + inv.getBlockHashes ().add (new Hash (store.getHeadHash ()).toByteArray ()); + peer.send (inv); } } diff --git a/server/src/main/java/com/bitsofproof/supernode/core/GetHeadersHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/GetHeadersHandler.java index e2ee4edc..a6b9fc7c 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/GetHeadersHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/GetHeadersHandler.java @@ -20,10 +20,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.support.TransactionCallbackWithoutResult; -import org.springframework.transaction.support.TransactionTemplate; import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.ValidationException; @@ -37,7 +33,6 @@ public class GetHeadersHandler implements BitcoinMessageListener 0 ) { diff --git a/server/src/main/java/com/bitsofproof/supernode/core/HeartbeatHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/HeartbeatHandler.java index b2e68a08..afa9f509 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/HeartbeatHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/HeartbeatHandler.java @@ -23,11 +23,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.messages.BitcoinMessageListener; import com.bitsofproof.supernode.messages.PingMessage; import com.bitsofproof.supernode.messages.PongMessage; -import com.bitsofproof.supernode.model.KnownPeer; public class HeartbeatHandler implements BitcoinMessageListener, Runnable, BitcoinPeerListener { @@ -60,22 +58,6 @@ public void process (PongMessage m, BitcoinPeer peer) if ( n != null && n.nonce == m.getNonce () ) { sentNonces.remove (peer); - if ( network.getPeerStore () != null ) - { - KnownPeer p; - try - { - p = network.getPeerStore ().findPeer (peer.getAddress ().getAddress ()); - if ( p != null ) - { - p.setResponseTime (System.currentTimeMillis () - n.sent); - network.getPeerStore ().store (p); - } - } - catch ( ValidationException e ) - { - } - } } } diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index 4ed103cd..d45298b8 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -35,10 +35,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.support.TransactionCallback; -import org.springframework.transaction.support.TransactionTemplate; import com.bitsofproof.supernode.api.BCSAPIMessage; import com.bitsofproof.supernode.api.Block; @@ -65,8 +61,6 @@ public class ImplementBCSAPI implements TrunkListener, TxListener private final BlockStore store; private final TxHandler txhandler; - private PlatformTransactionManager transactionManager; - private ConnectionFactory connectionFactory; private Connection connection; @@ -83,11 +77,6 @@ public ImplementBCSAPI (BitcoinNetwork network, TxHandler txHandler) txHandler.addTransactionListener (this); } - public void setTransactionManager (PlatformTransactionManager transactionManager) - { - this.transactionManager = transactionManager; - } - public void setConnectionFactory (ConnectionFactory connectionFactory) { this.connectionFactory = connectionFactory; @@ -577,27 +566,19 @@ public Transaction getTransaction (final String hash) Tx tx = txhandler.getTransaction (hash); if ( tx == null ) { - return new TransactionTemplate (transactionManager).execute (new TransactionCallback () + Tx t; + try { - @Override - public Transaction doInTransaction (TransactionStatus status) + t = store.getTransaction (hash); + if ( t != null ) { - status.setRollbackOnly (); - Tx t; - try - { - t = store.getTransaction (hash); - if ( t != null ) - { - return toBCSAPITransaction (t, false); - } - } - catch ( ValidationException e ) - { - } - return null; + return toBCSAPITransaction (t, false); } - }); + } + catch ( ValidationException e ) + { + } + return null; } else { @@ -613,32 +594,25 @@ public Transaction doInTransaction (TransactionStatus status) public Block getBlock (final String hash) { log.trace ("get block " + hash); - Block block = new TransactionTemplate (transactionManager).execute (new TransactionCallback () + Block block = null; + String h = hash; + if ( h.equals (Hash.ZERO_HASH_STRING) ) { - @Override - public Block doInTransaction (TransactionStatus status) + h = store.getHeadHash (); + } + Blk b; + try + { + b = store.getBlock (h); + if ( b != null ) { - status.setRollbackOnly (); - String h = hash; - if ( h.equals (Hash.ZERO_HASH_STRING) ) - { - h = store.getHeadHash (); - } - Blk b; - try - { - b = store.getBlock (h); - if ( b != null ) - { - return toBCSAPIBlock (b); - } - } - catch ( ValidationException e ) - { - } - return null; + block = toBCSAPIBlock (b); } - }); + } + catch ( ValidationException e ) + { + } + log.trace ("get block returned " + block != null ? hash : "null"); return block; } @@ -646,32 +620,25 @@ public Block doInTransaction (TransactionStatus status) public Block getBlockHeader (final String hash) { log.trace ("get block header " + hash); - Block block = new TransactionTemplate (transactionManager).execute (new TransactionCallback () + Block block = null; + String h = hash; + if ( h.equals (Hash.ZERO_HASH_STRING) ) { - @Override - public Block doInTransaction (TransactionStatus status) + h = store.getHeadHash (); + } + Blk b; + try + { + b = store.getBlockHeader (h); + if ( b != null ) { - status.setRollbackOnly (); - String h = hash; - if ( h.equals (Hash.ZERO_HASH_STRING) ) - { - h = store.getHeadHash (); - } - Blk b; - try - { - b = store.getBlockHeader (h); - if ( b != null ) - { - return toBCSAPIBlock (b); - } - } - catch ( ValidationException e ) - { - } - return null; + block = toBCSAPIBlock (b); } - }); + } + catch ( ValidationException e ) + { + } + log.trace ("get block header returned " + block != null ? hash : "null"); return block; } diff --git a/server/src/main/java/com/bitsofproof/supernode/core/PeerStore.java b/server/src/main/java/com/bitsofproof/supernode/core/PeerStore.java deleted file mode 100644 index f042f6e2..00000000 --- a/server/src/main/java/com/bitsofproof/supernode/core/PeerStore.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2013 bits of proof zrt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.bitsofproof.supernode.core; - -import java.net.InetAddress; -import java.util.Collection; - -import com.bitsofproof.supernode.common.ValidationException; -import com.bitsofproof.supernode.model.KnownPeer; - -public interface PeerStore -{ - - public Collection getConnectablePeers (); - - public void store (KnownPeer peer); - - public KnownPeer findPeer (InetAddress address) throws ValidationException; -} \ No newline at end of file diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index 42f86a80..2e37537c 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -28,10 +28,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.support.TransactionCallback; -import org.springframework.transaction.support.TransactionTemplate; import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.common.ByteVector; @@ -56,7 +52,6 @@ public class TxHandler implements TrunkListener private final Map unconfirmed = Collections.synchronizedMap (new HashMap ()); private ImplementTxOutCacheDelta availableOutput = null; - private PlatformTransactionManager transactionManager; private final Set dependencyOrderedSet = new TreeSet (new Comparator () { @@ -88,11 +83,6 @@ public void addTransactionListener (TxListener listener) transactionListener.add (listener); } - public void setTransactionManager (PlatformTransactionManager transactionManager) - { - this.transactionManager = transactionManager; - } - public TxHandler (final BitcoinNetwork network) { this.network = network; @@ -181,33 +171,12 @@ public void run (TxOutCache cache) throws ValidationException { if ( !unconfirmed.containsKey (t.getHash ()) ) { - ValidationException exception = new TransactionTemplate (transactionManager).execute (new TransactionCallback () - { - @Override - public ValidationException doInTransaction (TransactionStatus status) - { - status.setRollbackOnly (); - - try - { - if ( network.getStore ().getTransaction (t.getHash ()) == null ) - { - network.getStore ().validateTransaction (t, availableOutput); - cacheTransaction (t); - sendTransaction (t, peer); - notifyListener (t, false); - } - return null; - } - catch ( ValidationException e ) - { - return e; - } - } - }); - if ( exception != null ) + if ( network.getStore ().getTransaction (t.getHash ()) == null ) { - throw exception; + network.getStore ().validateTransaction (t, availableOutput); + cacheTransaction (t); + sendTransaction (t, peer); + notifyListener (t, false); } } } diff --git a/server/src/main/java/com/bitsofproof/supernode/model/Blk.java b/server/src/main/java/com/bitsofproof/supernode/model/Blk.java index 4d9fffc4..0cf36e39 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/Blk.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/Blk.java @@ -21,17 +21,6 @@ import java.util.ArrayList; import java.util.List; -import javax.persistence.Basic; -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.Lob; -import javax.persistence.OneToMany; -import javax.persistence.Table; - import org.json.JSONException; import org.json.JSONObject; @@ -43,27 +32,20 @@ import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -@Entity -@Table (name = "blk") public class Blk implements Serializable { private static final long serialVersionUID = 1L; - @Id - @GeneratedValue private Long id; - @Column (length = 64, nullable = false, unique = true) private String hash; long version; - @Column (length = 64, nullable = false) private String previousHash; transient private byte[] wireTransactions; - @Column (length = 64, nullable = false) private String merkleRoot; private long createTime; @@ -72,14 +54,11 @@ public class Blk implements Serializable private int height; private long nonce; - @OneToMany (fetch = FetchType.LAZY, cascade = CascadeType.ALL) private List transactions; private transient ArrayList txHashes; private Long headId; - @Lob - @Basic (fetch = FetchType.EAGER) private byte[] filterMap; private int filterFunctions; diff --git a/server/src/main/java/com/bitsofproof/supernode/model/Head.java b/server/src/main/java/com/bitsofproof/supernode/model/Head.java index 8755503b..8d8f67a8 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/Head.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/Head.java @@ -17,24 +17,15 @@ import java.io.Serializable; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.Table; - import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.ValidationException; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -@Entity -@Table (name = "head") public class Head implements Serializable { private static final long serialVersionUID = 1L; - @Id - @GeneratedValue + private Long id; private long chainWork; private int height; @@ -42,7 +33,6 @@ public class Head implements Serializable private Long previousId; private int previousHeight; - @Column (length = 64, nullable = false) private String leaf; public static Head fromLevelDB (byte[] data) throws ValidationException diff --git a/server/src/main/java/com/bitsofproof/supernode/model/JpaStore.java b/server/src/main/java/com/bitsofproof/supernode/model/JpaStore.java deleted file mode 100644 index 142a2592..00000000 --- a/server/src/main/java/com/bitsofproof/supernode/model/JpaStore.java +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Copyright 2013 bits of proof zrt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.bitsofproof.supernode.model; - -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; - -import org.hibernate.exception.ConstraintViolationException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; - -import com.bitsofproof.supernode.common.BloomFilter; -import com.bitsofproof.supernode.common.Hash; -import com.bitsofproof.supernode.common.ValidationException; -import com.bitsofproof.supernode.core.CachedBlockStore; -import com.bitsofproof.supernode.core.ColorStore; -import com.bitsofproof.supernode.core.Discovery; -import com.bitsofproof.supernode.core.PeerStore; -import com.bitsofproof.supernode.core.TxOutCache; -import com.mysema.query.jpa.impl.JPAQuery; - -public class JpaStore extends CachedBlockStore implements Discovery, PeerStore, ColorStore -{ - private static final Logger log = LoggerFactory.getLogger (JpaStore.class); - - @PersistenceContext - private EntityManager entityManager; - - @Autowired - private PlatformTransactionManager transactionManager; - - @Override - protected void clearStore () - { - } - - @Override - protected void startBatch () - { - } - - @Override - protected void endBatch () - { - } - - @Override - protected void cancelBatch () - { - } - - @Override - public void scan (BloomFilter filter, TransactionProcessor processor) - { - log.error ("Bloom scan not yet implemented in JpaStore"); - processor.process (null); - } - - @Override - protected void cacheUTXO (int lookback, TxOutCache cache) - { - long after = Math.max (currentHead.getHeight () - lookback, 0L); - QTxOut txout = QTxOut.txOut; - JPAQuery q = new JPAQuery (entityManager); - for ( TxOut o : q.from (txout).where (txout.available.eq (true).and (txout.height.gt (after))).list (txout) ) - { - cache.add (o.flatCopy (null)); - entityManager.detach (o); - } - } - - @Override - protected void cacheChain () - { - JPAQuery q; - QBlk block = QBlk.blk; - q = new JPAQuery (entityManager); - for ( Blk b : q.from (block).orderBy (block.id.asc ()).list (block) ) - { - CachedBlock cb = null; - if ( !b.getPreviousHash ().equals (Hash.ZERO_HASH_STRING) ) - { - cb = - new CachedBlock (b.getHash (), b.getId (), cachedBlocks.get (b.getPreviousHash ()), b.getCreateTime (), b.getHeight (), - (int) b.getVersion (), b.getFilterMap (), b.getFilterFunctions ()); - } - else - { - cb = - new CachedBlock (b.getHash (), b.getId (), null, b.getCreateTime (), b.getHeight (), (int) b.getVersion (), b.getFilterMap (), - b.getFilterFunctions ()); - } - cachedBlocks.put (b.getHash (), cb); - CachedHead h = cachedHeads.get (b.getHeadId ()); - h.getBlocks ().add (cb); - h.setLast (cb); - } - } - - @Override - protected void cacheHeads () - { - QHead head = QHead.head; - JPAQuery q = new JPAQuery (entityManager); - for ( Head h : q.from (head).orderBy (head.id.asc ()).list (head) ) - { - CachedHead sh = new CachedHead (); - sh.setId (h.getId ()); - sh.setChainWork (h.getChainWork ()); - sh.setHeight (h.getHeight ()); - if ( h.getPreviousId () != null ) - { - sh.setPrevious (cachedHeads.get (h.getPreviousId ())); - sh.setPreviousHeight (h.getPreviousHeight ()); - } - cachedHeads.put (h.getId (), sh); - if ( currentHead == null || currentHead.getChainWork () < sh.getChainWork () ) - { - currentHead = sh; - } - } - } - - @Override - protected void cacheColors () - { - JPAQuery q = new JPAQuery (entityManager); - QStoredColor color = QStoredColor.storedColor; - for ( StoredColor c : q.from (color).list (color) ) - { - entityManager.detach (c); - cachedColors.put (c.getTxHash (), c); - } - } - - @Override - protected void backwardCache (Blk b, TxOutCache cache, boolean modify) - { - List txs = new ArrayList (); - txs.addAll (b.getTransactions ()); - Collections.reverse (txs); - for ( Tx t : txs ) - { - for ( TxOut out : t.getOutputs () ) - { - if ( modify ) - { - out.setAvailable (false); - } - cache.remove (t.getHash (), out.getIx ()); - } - - for ( TxIn in : t.getInputs () ) - { - if ( !in.getSourceHash ().equals (Hash.ZERO_HASH_STRING) ) - { - TxOut source = in.getSource (); - if ( modify ) - { - source.setAvailable (true); - } - cache.add (source.flatCopy (null)); - } - } - } - } - - @Override - protected void forwardCache (Blk b, TxOutCache cache, boolean modify) - { - for ( Tx t : b.getTransactions () ) - { - for ( TxOut out : t.getOutputs () ) - { - if ( modify ) - { - out.setAvailable (true); - } - cache.add (out.flatCopy (null)); - } - - for ( TxIn in : t.getInputs () ) - { - if ( !in.getSourceHash ().equals (Hash.ZERO_HASH_STRING) ) - { - if ( modify ) - { - in.getSource ().setAvailable (false); - } - cache.remove (in.getSourceHash (), in.getIx ()); - } - } - } - } - - @Override - protected List findTxOuts (Map> need) - { - List fromDB = new ArrayList (); - QTxOut txout = QTxOut.txOut; - JPAQuery q = new JPAQuery (entityManager); - for ( TxOut o : q.from (txout).where (txout.txHash.in (need.keySet ())).list (txout) ) - { - if ( o.isAvailable () && need.get (o.getTxHash ()).contains (o.getIx ()) ) - { - fromDB.add (o); - } - } - return fromDB; - } - - @Override - protected void checkBIP30Compliance (Set txs, int untilHeight) throws ValidationException - { - QTxOut txout = QTxOut.txOut; - JPAQuery q = new JPAQuery (entityManager); - for ( TxOut o : q.from (txout).where (txout.txHash.in (txs)).list (txout) ) - { - if ( o.isAvailable () && o.getTransaction ().getBlock ().getHeight () <= untilHeight ) - { - throw new ValidationException ("BIP30 violation block contains unspent tx " + o.getTxHash ()); - } - } - } - - @Override - protected Blk retrieveBlock (CachedBlock cached) - { - return entityManager.find (Blk.class, cached.getId ()); - } - - @Override - protected Blk retrieveBlockHeader (CachedBlock cached) - { - return entityManager.find (Blk.class, cached.getId ()); - } - - @Override - protected void insertHead (Head head) - { - entityManager.persist (head); - } - - @Override - protected Head updateHead (Head head) - { - return entityManager.merge (head); - } - - @Override - protected Head retrieveHead (CachedHead cached) - { - return entityManager.find (Head.class, cached.getId ()); - } - - @Override - protected void insertBlock (Blk b) - { - entityManager.persist (b); - } - - @Override - public boolean isEmpty () - { - QHead head = QHead.head; - JPAQuery q = new JPAQuery (entityManager); - return q.from (head).list (head).isEmpty (); - } - - @Override - protected TxOut getSourceReference (TxOut source) - { - return entityManager.getReference (TxOut.class, source.getId ()); - } - - @Override - @Transactional (propagation = Propagation.REQUIRED, readOnly = true) - public Tx getTransaction (String hash) - { - JPAQuery q = new JPAQuery (entityManager); - QTx tx = QTx.tx; - - Tx t = q.from (tx).where (tx.hash.eq (hash)).uniqueResult (tx); - if ( t != null ) - { - // trigger lazy loading - t.getInputs ().size (); - t.getOutputs ().size (); - entityManager.detach (t); - return t; - } - return null; - } - - @Override - @Transactional (propagation = Propagation.REQUIRED) - public Collection getConnectablePeers () - { - QKnownPeer kp = QKnownPeer.knownPeer; - JPAQuery q = new JPAQuery (entityManager); - List pl = - q.from (kp).where (kp.banned.lt (System.currentTimeMillis () / 1000)).orderBy (kp.preference.desc ()).orderBy (kp.height.desc ()) - .orderBy (kp.responseTime.desc ()).orderBy (kp.connected.desc ()).list (kp); - log.trace ("Retrieved " + pl.size () + " peers from store"); - return pl; - } - - @Override - @Transactional (propagation = Propagation.REQUIRED) - public void store (KnownPeer peer) - { - try - { - KnownPeer stored; - if ( (stored = findPeer (InetAddress.getByName (peer.getAddress ()))) == null ) - { - entityManager.persist (peer); - } - else - { - stored.setAgent (peer.getAgent ()); - stored.setBanned (peer.getBanned ()); - stored.setBanReason (peer.getBanReason ()); - stored.setConnected (peer.getConnected ()); - stored.setDisconnected (peer.getDisconnected ()); - stored.setHeight (peer.getHeight ()); - stored.setName (peer.getName ()); - stored.setPreference (peer.getPreference ()); - stored.setResponseTime (peer.getResponseTime ()); - stored.setServices (peer.getServices ()); - stored.setTrafficIn (peer.getTrafficIn ()); - stored.setTrafficOut (peer.getTrafficOut ()); - stored.setVersion (peer.getVersion ()); - entityManager.merge (stored); - } - } - catch ( ConstraintViolationException e ) - { - } - catch ( UnknownHostException e ) - { - } - } - - @Override - @Transactional (propagation = Propagation.REQUIRED) - public KnownPeer findPeer (InetAddress address) - { - QKnownPeer kp = QKnownPeer.knownPeer; - JPAQuery q = new JPAQuery (entityManager); - return q.from (kp).where (kp.address.eq (address.getHostAddress ())).uniqueResult (kp); - } - - @Override - public List discover () - { - log.trace ("Discovering stored peers"); - List peers = new ArrayList (); - for ( KnownPeer kp : getConnectablePeers () ) - { - try - { - String[] split = kp.getAddress ().split (":"); - if ( split.length == 2 ) - { - peers.add (InetSocketAddress.createUnresolved (split[0], Integer.valueOf (split[1]))); - } - else - { - peers.add (InetSocketAddress.createUnresolved (split[0], chain.getPort ())); - } - } - catch ( Exception e ) - { - } - } - return peers; - } - - @Override - @Transactional (propagation = Propagation.MANDATORY) - public void storeColor (StoredColor color) - { - entityManager.persist (color); - } - - @Override - @Transactional (propagation = Propagation.REQUIRED) - public StoredColor findColor (String hash) - { - QStoredColor sc = QStoredColor.storedColor; - JPAQuery q = new JPAQuery (entityManager); - return q.from (sc).where (sc.txHash.eq (hash)).uniqueResult (sc); - } - - @Override - @Transactional (propagation = Propagation.MANDATORY) - protected void updateColor (TxOut root, String fungibleName) - { - root.setColor (fungibleName); - } -} diff --git a/server/src/main/java/com/bitsofproof/supernode/model/KnownPeer.java b/server/src/main/java/com/bitsofproof/supernode/model/KnownPeer.java index e4bb5533..5573dc4c 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/KnownPeer.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/KnownPeer.java @@ -17,26 +17,15 @@ import java.io.Serializable; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.Table; - import com.bitsofproof.supernode.common.ValidationException; import com.google.protobuf.InvalidProtocolBufferException; -@Entity -@Table (name = "peer") public class KnownPeer implements Serializable { private static final long serialVersionUID = 1L; - @Id - @GeneratedValue private Long id; - @Column (length = 40, nullable = false, unique = true) private String address; private long version; @@ -45,10 +34,8 @@ public class KnownPeer implements Serializable private long height; - @Column (length = 256, nullable = true) private String name; - @Column (length = 64, nullable = true) private String agent; private long responseTime; @@ -61,7 +48,6 @@ public class KnownPeer implements Serializable private long banned; - @Column (length = 256, nullable = true) private String banReason; public byte[] toLevelDB () diff --git a/server/src/main/java/com/bitsofproof/supernode/model/LvlStore.java b/server/src/main/java/com/bitsofproof/supernode/model/LvlStore.java index 091f32c6..8a3eb9fa 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/LvlStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/LvlStore.java @@ -15,10 +15,7 @@ */ package com.bitsofproof.supernode.model; -import java.net.InetAddress; -import java.net.InetSocketAddress; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -35,16 +32,11 @@ import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.common.WireFormat; import com.bitsofproof.supernode.core.CachedBlockStore; -import com.bitsofproof.supernode.core.ColorStore; -import com.bitsofproof.supernode.core.Discovery; -import com.bitsofproof.supernode.core.PeerStore; import com.bitsofproof.supernode.core.TxOutCache; import com.bitsofproof.supernode.model.OrderedMapStore.DataProcessor; import com.bitsofproof.supernode.model.OrderedMapStoreKey.KeyType; -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -public class LvlStore extends CachedBlockStore implements Discovery, PeerStore, ColorStore +public class LvlStore extends CachedBlockStore { private static final Logger log = LoggerFactory.getLogger (LvlStore.class); @@ -267,23 +259,6 @@ public boolean process (byte[] key, byte[] data) @Override protected void cacheColors () { - store.forAll (KeyType.COLOR, new DataProcessor () - { - @Override - public boolean process (byte[] key, byte[] data) - { - WireFormat.Reader reader = new WireFormat.Reader (data); - StoredColor sc = new StoredColor (); - sc.setTxHash (reader.readHash ().toString ()); - sc.setTerms (reader.readString ()); - sc.setUnit (reader.readUint64 ()); - sc.setExpiryHeight ((int) reader.readUint32 ()); - sc.setSignature (reader.readVarBytes ()); - sc.setPubkey (reader.readVarBytes ()); - cachedColors.put (sc.getTxHash (), sc); - return true; - } - }); } @Override @@ -488,134 +463,6 @@ public boolean process (byte[] key, byte[] data) processor.process (null); } - @Override - public Collection getConnectablePeers () - { - final List peers = new ArrayList (); - store.forAll (KeyType.PEER, new DataProcessor () - { - @Override - public boolean process (byte[] key, byte[] data) - { - KnownPeer p; - try - { - p = KnownPeer.fromLevelDB (data); - if ( p.getBanned () < System.currentTimeMillis () / 1000 ) - { - peers.add (p); - } - } - catch ( ValidationException e ) - { - log.error ("Can not read peer ", e); - return false; - } - return true; - } - }); - Collections.sort (peers, new Comparator () - { - - @Override - public int compare (KnownPeer o1, KnownPeer o2) - { - if ( o1.getPreference () != o2.getPreference () ) - { - return (int) (o1.getPreference () - o2.getPreference ()); - } - if ( o1.getResponseTime () != o2.getResponseTime () ) - { - return (int) (o1.getResponseTime () - o2.getResponseTime ()); - } - return 0; - } - }); - - return peers; - } - - @Override - public void store (KnownPeer peer) - { - writePeer (peer); - } - - @Override - public KnownPeer findPeer (InetAddress address) throws ValidationException - { - return readPeer (address.getHostName ()); - } - - @Override - public List discover () - { - log.trace ("Discovering stored peers"); - List peers = new ArrayList (); - for ( KnownPeer kp : getConnectablePeers () ) - { - try - { - String[] split = kp.getAddress ().split (":"); - if ( split.length == 2 ) - { - peers.add (InetSocketAddress.createUnresolved (split[0], Integer.valueOf (split[1]))); - } - else - { - peers.add (InetSocketAddress.createUnresolved (split[0], chain.getPort ())); - } - } - catch ( Exception e ) - { - } - } - return peers; - } - - @Override - public void storeColor (StoredColor color) - { - LevelDBStore.COLOR.Builder builder = LevelDBStore.COLOR.newBuilder (); - builder.setStoreVersion (1); - builder.setTxHash (ByteString.copyFrom (new Hash (color.getTxHash ()).toByteArray ())); - builder.setFungibleName (color.getFungibleName ()); - builder.setTerms (color.getTerms ()); - builder.setUnit (color.getUnit ()); - builder.setExpiryHeight (color.getExpiryHeight ()); - builder.setSignature (ByteString.copyFrom (color.getSignature ())); - builder.setPubkey (ByteString.copyFrom (color.getPubkey ())); - store.put (OrderedMapStoreKey.createKey (KeyType.COLOR, new Hash (color.getFungibleName ()).toByteArray ()), builder.build ().toByteArray ()); - } - - @Override - public StoredColor findColor (String hash) throws ValidationException - { - byte[] data = store.get (OrderedMapStoreKey.createKey (KeyType.COLOR, new Hash (hash).toByteArray ())); - if ( data == null ) - { - return null; - } - LevelDBStore.COLOR p; - try - { - p = LevelDBStore.COLOR.parseFrom (data); - StoredColor sc = new StoredColor (); - sc.setTxHash (new Hash (p.getTxHash ().toByteArray ()).toString ()); - sc.setFungibleName (p.getFungibleName ()); - sc.setTerms (p.getTerms ()); - sc.setUnit (p.getUnit ()); - sc.setExpiryHeight (p.getExpiryHeight ()); - sc.setSignature (p.getSignature ().toByteArray ()); - sc.setPubkey (p.getPubkey ().toByteArray ()); - return sc; - } - catch ( InvalidProtocolBufferException e ) - { - throw new ValidationException (e); - } - } - @Override public boolean isEmpty () { diff --git a/server/src/main/java/com/bitsofproof/supernode/model/LvlTransaction.java b/server/src/main/java/com/bitsofproof/supernode/model/LvlTransaction.java deleted file mode 100644 index 280f6a14..00000000 --- a/server/src/main/java/com/bitsofproof/supernode/model/LvlTransaction.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2013 bits of proof zrt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.bitsofproof.supernode.model; - -import javax.transaction.HeuristicMixedException; -import javax.transaction.HeuristicRollbackException; -import javax.transaction.NotSupportedException; -import javax.transaction.RollbackException; -import javax.transaction.SystemException; -import javax.transaction.UserTransaction; - -public class LvlTransaction implements UserTransaction -{ - - @Override - public void begin () throws NotSupportedException, SystemException - { - // TODO Auto-generated method stub - - } - - @Override - public void commit () throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, - SystemException - { - // TODO Auto-generated method stub - - } - - @Override - public int getStatus () throws SystemException - { - // TODO Auto-generated method stub - return 0; - } - - @Override - public void rollback () throws IllegalStateException, SecurityException, SystemException - { - // TODO Auto-generated method stub - - } - - @Override - public void setRollbackOnly () throws IllegalStateException, SystemException - { - // TODO Auto-generated method stub - - } - - @Override - public void setTransactionTimeout (int arg0) throws SystemException - { - // TODO Auto-generated method stub - - } - -} diff --git a/server/src/main/java/com/bitsofproof/supernode/model/StoredColor.java b/server/src/main/java/com/bitsofproof/supernode/model/StoredColor.java deleted file mode 100644 index 1ec60654..00000000 --- a/server/src/main/java/com/bitsofproof/supernode/model/StoredColor.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright 2013 bits of proof zrt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.bitsofproof.supernode.model; - -import java.io.Serializable; -import java.io.UnsupportedEncodingException; - -import javax.persistence.Basic; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.Lob; -import javax.persistence.Table; - -import com.bitsofproof.supernode.common.ECKeyPair; -import com.bitsofproof.supernode.common.Hash; -import com.bitsofproof.supernode.common.WireFormat; - -@Entity -@Table (name = "color") -public class StoredColor implements Serializable -{ - private static final long serialVersionUID = 1L; - - @Id - @GeneratedValue - private Long id; - - @Column (length = 64, nullable = false, unique = true) - private String fungibleName; - - @Column (length = 64, nullable = false, unique = true) - private String txHash; - - @Lob - @Basic (fetch = FetchType.EAGER) - private String terms; - - private long unit; - private int expiryHeight; - - @Lob - @Basic (fetch = FetchType.EAGER) - private byte[] signature; - - @Lob - @Basic (fetch = FetchType.EAGER) - private byte[] pubkey; - - public Long getId () - { - return id; - } - - public void setId (Long id) - { - this.id = id; - } - - public String getTerms () - { - return terms; - } - - public void setFungibleName (String fungibleName) - { - this.fungibleName = fungibleName; - } - - public String getFungibleName () - { - return fungibleName; - } - - public void setTerms (String terms) - { - this.terms = terms; - } - - public long getUnit () - { - return unit; - } - - public void setUnit (long unit) - { - this.unit = unit; - } - - public int getExpiryHeight () - { - return expiryHeight; - } - - public void setExpiryHeight (int expiryHeight) - { - this.expiryHeight = expiryHeight; - } - - public byte[] getSignature () - { - return signature; - } - - public void setSignature (byte[] signature) - { - this.signature = signature; - } - - public byte[] getPubkey () - { - return pubkey; - } - - public void setPubkey (byte[] pubkey) - { - this.pubkey = pubkey; - } - - public String getTxHash () - { - return txHash; - } - - public void setTxHash (String txHash) - { - this.txHash = txHash; - } - - public byte[] hashFungibleName () - { - WireFormat.Writer writer = new WireFormat.Writer (); - try - { - writer.writeBytes (terms.getBytes ("UTF-8")); - } - catch ( UnsupportedEncodingException e ) - { - } - writer.writeUint64 (unit); - writer.writeUint32 (expiryHeight); - byte[] content = writer.toByteArray (); - return content; - } - - private byte[] hashContent () - { - WireFormat.Writer writer = new WireFormat.Writer (); - writer.writeHash (new Hash (txHash)); - try - { - writer.writeBytes (terms.getBytes ("UTF-8")); - } - catch ( UnsupportedEncodingException e ) - { - } - writer.writeUint64 (unit); - writer.writeUint32 (expiryHeight); - writer.writeVarBytes (pubkey); - byte[] content = writer.toByteArray (); - return Hash.hash (content); - } - - public boolean verify () - { - if ( !Hash.hash (hashFungibleName ()).equals (fungibleName) ) - { - return false; - } - return ECKeyPair.verify (hashContent (), signature, pubkey); - } -} diff --git a/server/src/main/java/com/bitsofproof/supernode/model/Tx.java b/server/src/main/java/com/bitsofproof/supernode/model/Tx.java index 6daa9079..5befb939 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/Tx.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/Tx.java @@ -20,34 +20,21 @@ import java.util.List; import java.util.Set; -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.ManyToOne; -import javax.persistence.OneToMany; -import javax.persistence.Table; - -import org.hibernate.annotations.Index; import org.json.JSONException; import org.json.JSONObject; import com.bitsofproof.supernode.common.BloomFilter; +import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.common.ByteUtils; import com.bitsofproof.supernode.common.ByteVector; import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.ScriptFormat; +import com.bitsofproof.supernode.common.ScriptFormat.Token; import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.common.WireFormat; -import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; -import com.bitsofproof.supernode.common.ScriptFormat.Token; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -@Entity -@Table (name = "tx") public class Tx implements Serializable { private static final long serialVersionUID = 1L; @@ -56,8 +43,6 @@ public class Tx implements Serializable public static final long MAX_MONEY = 21000000L * COIN; // it should actually be 2099999997690000L, but above is in bitcoind - @Id - @GeneratedValue private Long id; private long version = 1; @@ -67,17 +52,12 @@ public class Tx implements Serializable private long ix = 0; // this is not unique since a transaction copy might be on different branches. - @Column (length = 64, nullable = false) - @Index (name = "txhash") private String hash; - @OneToMany (fetch = FetchType.LAZY, cascade = CascadeType.ALL) private List inputs; - @OneToMany (fetch = FetchType.LAZY, cascade = CascadeType.ALL) private List outputs; - @ManyToOne (fetch = FetchType.LAZY, optional = false) private Blk block; private transient String blockHash; diff --git a/server/src/main/java/com/bitsofproof/supernode/model/TxIn.java b/server/src/main/java/com/bitsofproof/supernode/model/TxIn.java index d2d82799..0b8fe708 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/TxIn.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/TxIn.java @@ -17,17 +17,6 @@ import java.io.Serializable; -import javax.persistence.Basic; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.Lob; -import javax.persistence.ManyToOne; -import javax.persistence.OneToOne; -import javax.persistence.Table; - import org.json.JSONException; import org.json.JSONObject; @@ -37,32 +26,22 @@ import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.common.WireFormat; -@Entity -@Table (name = "txin") public class TxIn implements Serializable { private static final long serialVersionUID = 1L; - @Id - @GeneratedValue private Long id; - @Column (length = 64) private String sourceHash; - @Column (nullable = false) private Long ix = new Long (0L); - @ManyToOne (fetch = FetchType.LAZY, optional = false) private Tx transaction; - @OneToOne (fetch = FetchType.LAZY, optional = true) private TxOut source; private long sequence = 0xFFFFFFFFL; - @Lob - @Basic (fetch = FetchType.EAGER) // scriptSig private byte[] script; diff --git a/server/src/main/java/com/bitsofproof/supernode/model/TxOut.java b/server/src/main/java/com/bitsofproof/supernode/model/TxOut.java index 9fd10119..82e7a36b 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/TxOut.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/TxOut.java @@ -17,17 +17,6 @@ import java.io.Serializable; -import javax.persistence.Basic; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.Lob; -import javax.persistence.ManyToOne; -import javax.persistence.Table; - -import org.hibernate.annotations.Index; import org.json.JSONException; import org.json.JSONObject; @@ -36,27 +25,19 @@ import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.common.WireFormat; -@Entity -@Table (name = "txout") public class TxOut implements Serializable { private static final long serialVersionUID = 1L; - @Id - @GeneratedValue private Long id; private long value; - @Lob - @Basic (fetch = FetchType.EAGER) // scriptPubKey private byte[] script; - @ManyToOne (fetch = FetchType.LAZY, optional = false) private Tx transaction; - @Column (nullable = false) private Long ix = new Long (0L); private boolean coinbase = false; @@ -67,8 +48,6 @@ public class TxOut implements Serializable private String color; // this is redundant but saves a join at finding UTXO that is critical for speed - @Column (length = 64, nullable = false) - @Index (name = "outthash") private String txHash; // this is redundant for a check diff --git a/server/src/main/resources/context/derby-profile.xml b/server/src/main/resources/context/derby-profile.xml deleted file mode 100644 index f08c0988..00000000 --- a/server/src/main/resources/context/derby-profile.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - org.hibernate.dialect.DerbyDialect - org.hibernate.cache.HashtableCacheProvider - - - - - - - - - - - - - - - - - - - diff --git a/server/src/main/resources/context/memdb-profile.xml b/server/src/main/resources/context/memdb-profile.xml deleted file mode 100644 index 56b1aecd..00000000 --- a/server/src/main/resources/context/memdb-profile.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - org.hibernate.dialect.DerbyDialect - org.hibernate.cache.HashtableCacheProvider - - - - - - - - - - - - - - - - - - - diff --git a/server/src/main/resources/context/persistence.xml b/server/src/main/resources/context/persistence.xml deleted file mode 100644 index 0cfd4a32..00000000 --- a/server/src/main/resources/context/persistence.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/server/src/main/resources/context/postgresql-profile.xml b/server/src/main/resources/context/postgresql-profile.xml deleted file mode 100644 index aad2dec1..00000000 --- a/server/src/main/resources/context/postgresql-profile.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - org.hibernate.dialect.PostgreSQLDialect - org.hibernate.cache.HashtableCacheProvider - - - - - - - - - - - - - - - - - - - diff --git a/server/src/main/resources/context/relational-profile.xml b/server/src/main/resources/context/relational-profile.xml deleted file mode 100644 index 362249df..00000000 --- a/server/src/main/resources/context/relational-profile.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - org.hibernate.dialect.DerbyDialect - org.hibernate.cache.HashtableCacheProvider - - - - - - - - - - - - - - - - - - - diff --git a/server/src/main/resources/context/remote-profile.xml b/server/src/main/resources/context/remote-profile.xml deleted file mode 100644 index bd38db64..00000000 --- a/server/src/main/resources/context/remote-profile.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - org.hibernate.dialect.DerbyDialect - org.hibernate.cache.HashtableCacheProvider - - - - - - - - - - - - - - - - - - - diff --git a/server/src/main/resources/context/server.xml b/server/src/main/resources/context/server.xml index 435be220..1f800021 100644 --- a/server/src/main/resources/context/server.xml +++ b/server/src/main/resources/context/server.xml @@ -31,14 +31,12 @@ - - @@ -51,7 +49,6 @@ - diff --git a/server/src/test/resources/context/EmbeddedBCSAPI.xml b/server/src/test/resources/context/EmbeddedBCSAPI.xml index 482bd5eb..3791d69b 100644 --- a/server/src/test/resources/context/EmbeddedBCSAPI.xml +++ b/server/src/test/resources/context/EmbeddedBCSAPI.xml @@ -17,7 +17,6 @@ - @@ -42,7 +41,6 @@ - diff --git a/server/src/test/resources/context/leveldb-store.xml b/server/src/test/resources/context/leveldb-store.xml index 12f59a26..2b67b3ba 100644 --- a/server/src/test/resources/context/leveldb-store.xml +++ b/server/src/test/resources/context/leveldb-store.xml @@ -9,14 +9,6 @@ http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> - - - - - - - - diff --git a/server/src/test/resources/context/memory-store.xml b/server/src/test/resources/context/memory-store.xml index 9545e3f0..25f53145 100644 --- a/server/src/test/resources/context/memory-store.xml +++ b/server/src/test/resources/context/memory-store.xml @@ -9,13 +9,6 @@ http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> - - - - - - - diff --git a/server/src/test/resources/context/persistence.xml b/server/src/test/resources/context/persistence.xml deleted file mode 100644 index d45d2aaa..00000000 --- a/server/src/test/resources/context/persistence.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - com.bitsofproof.supernode.model.Head - com.bitsofproof.supernode.model.Blk - com.bitsofproof.supernode.model.Tx - com.bitsofproof.supernode.model.TxIn - com.bitsofproof.supernode.model.TxOut - com.bitsofproof.supernode.model.KnownPeer - com.bitsofproof.supernode.model.StoredColor - - - - diff --git a/server/src/test/resources/context/store1.xml b/server/src/test/resources/context/store1.xml deleted file mode 100644 index d8b8c9e4..00000000 --- a/server/src/test/resources/context/store1.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - org.hibernate.dialect.DerbyDialect - org.hibernate.cache.HashtableCacheProvider - - - - - - - - - - - - - - - - - diff --git a/server/src/test/resources/context/store2.xml b/server/src/test/resources/context/store2.xml deleted file mode 100644 index 927bf471..00000000 --- a/server/src/test/resources/context/store2.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - org.hibernate.dialect.DerbyDialect - org.hibernate.cache.HashtableCacheProvider - - - - - - - - - - - - - - - - - diff --git a/server/src/test/resources/context/store3.xml b/server/src/test/resources/context/store3.xml deleted file mode 100644 index b71235f7..00000000 --- a/server/src/test/resources/context/store3.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - org.hibernate.dialect.DerbyDialect - org.hibernate.cache.HashtableCacheProvider - - - - - - - - - - - - - - - - - From cb890f1666049528caf9767325a20beec8c904d0 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 15 Aug 2013 13:37:16 +0200 Subject: [PATCH 152/299] 1.2-SNAPSHOT --- api/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 77f547bf..d3e477fd 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.1.8-SNAPSHOT + 1.2-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index 623eb902..8a89f949 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.1.8-SNAPSHOT + 1.2-SNAPSHOT supernode The bitsofproof supernode implementation of the Bitcoin protocol pom From ad9485e2ae1308cbf778f21646bfd23102d1b982 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 15 Aug 2013 13:42:47 +0200 Subject: [PATCH 153/299] no tx support for leveldb --- server/src/main/resources/context/leveldb-profile.xml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/server/src/main/resources/context/leveldb-profile.xml b/server/src/main/resources/context/leveldb-profile.xml index afa0abdd..25082aeb 100644 --- a/server/src/main/resources/context/leveldb-profile.xml +++ b/server/src/main/resources/context/leveldb-profile.xml @@ -9,14 +9,6 @@ http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> - - - - - - - - From a4967442f403db6de3e137fbe323baadc31bfa0f Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 15 Aug 2013 13:45:36 +0200 Subject: [PATCH 154/299] no transaction manager --- server/src/main/resources/context/BCSAPI-profile.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/main/resources/context/BCSAPI-profile.xml b/server/src/main/resources/context/BCSAPI-profile.xml index ccd89770..939cbf69 100644 --- a/server/src/main/resources/context/BCSAPI-profile.xml +++ b/server/src/main/resources/context/BCSAPI-profile.xml @@ -30,7 +30,6 @@ - From bae17afe1ec19d7ec6460f5f75e8c7378db140de Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 15 Aug 2013 14:04:51 +0200 Subject: [PATCH 155/299] more debug log from trunk update --- .../java/com/bitsofproof/supernode/core/TxHandler.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index 2e37537c..f7f8b48f 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -232,7 +232,7 @@ private void sendTransaction (Tx tx, BitcoinPeer peer) } } } - log.debug ("relaying transaction " + tx.getHash ()); + log.debug ("relaying transaction " + tx.getHash () + " pool size: " + unconfirmed.size ()); } private void notifyListener (Tx tx, boolean doubleSpend) @@ -267,7 +267,7 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo synchronized ( unconfirmed ) { List firstSeenInBlock = new ArrayList (); - + log.debug ("Removing " + removedBlocks.size () + " blocks"); for ( Blk blk : removedBlocks ) { boolean coinbase = true; @@ -282,6 +282,8 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo dependencyOrderedSet.add (tx); } } + log.debug ("Pool size: " + unconfirmed.size ()); + log.debug ("Adding " + addedBlocks.size () + " blocks"); for ( Blk blk : addedBlocks ) { for ( Tx tx : blk.getTransactions () ) @@ -296,9 +298,11 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo else { firstSeenInBlock.add (tx); + log.debug ("First seen in block: " + tx.getHash ()); } } } + log.debug ("Pool size: " + unconfirmed.size ()); availableOutput.reset (); @@ -321,6 +325,7 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo } catch ( ValidationException e ) { + log.debug ("Double spend: " + tx.getHash ()); unconfirmed.remove (tx.getHash ()); txi.remove (); notifyListener (tx, true); @@ -331,6 +336,7 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo { notifyListener (tx, false); } + } } catch ( Exception e ) From 18c6f7d86f1c4556002d950b6d7e8b07e4bbcc17 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 15 Aug 2013 14:08:11 +0200 Subject: [PATCH 156/299] simplified locking --- .../com/bitsofproof/supernode/core/TxHandler.java | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index f7f8b48f..35c0ec18 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -24,7 +24,6 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; -import java.util.concurrent.Semaphore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -156,14 +155,10 @@ public void process (final MempoolMessage m, final BitcoinPeer peer) } - private final Semaphore serializedValidation = new Semaphore (1); - public void validateCacheAndSend (final Tx t, final BitcoinPeer peer) throws ValidationException { - try + synchronized ( unconfirmed ) { - serializedValidation.acquireUninterruptibly (); - ValidationException exception = network.getStore ().runInCacheContext (new BlockStore.CacheContextRunnable () { @Override @@ -187,10 +182,6 @@ public void run (TxOutCache cache) throws ValidationException throw exception; } } - finally - { - serializedValidation.release (); - } } public Tx getTransaction (String hash) From 98c1c7c7f1ec8f8e1f0ca1ac7f8c0695d49a8875 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 15 Aug 2013 14:42:57 +0200 Subject: [PATCH 157/299] more log for API calls --- .../supernode/core/ImplementBCSAPI.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index d45298b8..88ced7ef 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -21,6 +21,7 @@ import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; import javax.jms.BytesMessage; import javax.jms.Connection; @@ -197,6 +198,7 @@ public void onMessage (Message msg) final UpdateMode mode = UpdateMode.values ()[request.getMode ()]; final Destination destination = msg.getJMSReplyTo (); final long after = request.hasAfter () ? request.getAfter () : 0; + log.debug ("matchRequest for ", match.size () + " patterns after " + after); requestProcessor.execute (new Runnable () { @Override @@ -208,6 +210,7 @@ public void run () session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); final MessageProducer producer = session.createProducer (destination); final Session passInSession = session; + final AtomicInteger counter = new AtomicInteger (0); TransactionProcessor processor = new TransactionProcessor () { @@ -224,6 +227,7 @@ public void process (Tx tx) m = passInSession.createBytesMessage (); m.writeBytes (transaction.toProtobuf ().toByteArray ()); producer.send (m); + counter.incrementAndGet (); } catch ( JMSException e ) { @@ -233,7 +237,12 @@ public void process (Tx tx) }; store.filterTransactions (match, mode, after, processor); + int c = counter.get (); + log.debug ("matchRequest returns " + counter.get () + " transactions from blockchain"); + txhandler.scanUnconfirmedPool (match, mode, processor); + log.debug ("matchRequest returns " + (counter.get () - c) + " transactions from mempool"); + try { BytesMessage m = session.createBytesMessage (); @@ -284,9 +293,11 @@ public void onMessage (Message msg) final ExtendedKey ek = ExtendedKey.parse (request.getPublicKey ()); final int lookAhead = request.getLookAhead (); final long after = request.getAfter (); + log.debug ("accountRequest for " + ek.serialize (true) + " after " + after); final Set match = new HashSet (); final UpdateMode mode = UpdateMode.all; final Destination destination = msg.getJMSReplyTo (); + final AtomicInteger counter = new AtomicInteger (0); requestProcessor.execute (new Runnable () { @Override @@ -313,6 +324,7 @@ public void process (Tx tx) m = passInSession.createBytesMessage (); m.writeBytes (transaction.toProtobuf ().toByteArray ()); producer.send (m); + counter.incrementAndGet (); } catch ( JMSException e ) { @@ -322,7 +334,11 @@ public void process (Tx tx) }; store.filterTransactions (match, ek, lookAhead, after, processor); + int c = counter.get (); + log.debug ("accountRequest returns " + counter.get () + " transactions from blockchain"); + txhandler.scanUnconfirmedPool (match, mode, processor); + log.debug ("accountRequest returns " + (counter.get () - c) + " transactions from mempool"); try { BytesMessage m = session.createBytesMessage (); @@ -373,6 +389,7 @@ public void onMessage (Message arg0) byte[] body = new byte[(int) o.getBodyLength ()]; o.readBytes (body); String hash = new Hash (BCSAPIMessage.Hash.parseFrom (body).getHash (0).toByteArray ()).toString (); + log.debug ("transactionRequest for " + hash); Transaction t = getTransaction (hash); if ( t != null ) { @@ -382,6 +399,7 @@ public void onMessage (Message arg0) { reply (o.getJMSReplyTo (), null); } + log.debug ("transactionRequest for " + hash + " replied"); } catch ( Exception e ) { @@ -404,6 +422,7 @@ public void onMessage (Message arg0) byte[] body = new byte[(int) o.getBodyLength ()]; o.readBytes (body); String hash = new Hash (BCSAPIMessage.Hash.parseFrom (body).getHash (0).toByteArray ()).toString (); + log.debug ("blockRequest for " + hash); Block b = getBlock (hash); if ( b != null ) { @@ -414,6 +433,7 @@ public void onMessage (Message arg0) { reply (o.getJMSReplyTo (), null); } + log.debug ("blockRequest for " + hash + " replied"); } catch ( Exception e ) { @@ -436,6 +456,7 @@ public void onMessage (Message arg0) byte[] body = new byte[(int) o.getBodyLength ()]; o.readBytes (body); String hash = new Hash (BCSAPIMessage.Hash.parseFrom (body).getHash (0).toByteArray ()).toString (); + log.debug ("headerRequest for " + hash); Block b = getBlockHeader (hash); if ( b != null ) { @@ -446,6 +467,7 @@ public void onMessage (Message arg0) { reply (o.getJMSReplyTo (), null); } + log.debug ("headerRequest for " + hash + " replied"); } catch ( Exception e ) { @@ -469,8 +491,10 @@ public void onMessage (Message arg0) o.readBytes (body); Block block = Block.fromProtobuf (BCSAPIMessage.Block.parseFrom (body)); block.computeHash (); + log.debug ("newBlock for " + block.getHash ()); sendBlock (block); reply (o.getJMSReplyTo (), null); + log.debug ("newBlock " + block.getHash () + " accepted"); } catch ( Exception e ) { @@ -480,6 +504,7 @@ public void onMessage (Message arg0) try { reply (o.getJMSReplyTo (), builder.build ().toByteArray ()); + log.debug ("newBlock rejected"); } catch ( JMSException e1 ) { @@ -501,8 +526,10 @@ public void onMessage (Message arg0) try { byte[] body = new byte[(int) o.getBodyLength ()]; + log.debug ("ping received"); o.readBytes (body); reply (o.getJMSReplyTo (), body); + log.debug ("ping replied"); } catch ( Exception e ) { @@ -526,8 +553,10 @@ public void onMessage (Message arg0) o.readBytes (body); Transaction transaction = Transaction.fromProtobuf (BCSAPIMessage.Transaction.parseFrom (body)); transaction.computeHash (); + log.debug ("newTransaction " + transaction.getHash ()); sendTransaction (transaction); reply (o.getJMSReplyTo (), null); + log.debug ("newTransaction " + transaction.getHash () + " accepted"); } catch ( Exception e ) { @@ -537,6 +566,7 @@ public void onMessage (Message arg0) try { reply (o.getJMSReplyTo (), builder.build ().toByteArray ()); + log.debug ("newTransaction rejected"); } catch ( JMSException e1 ) { From 85f87555df4f5ccf747d17dd3caee08a85c3df48 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 15 Aug 2013 15:14:20 +0200 Subject: [PATCH 158/299] 1.2 --- README.md | 10 ++++------ api/pom.xml | 2 +- pom.xml | 2 +- server/pom.xml | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 5c97da60..512fd16c 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ BOP Community Bitcoin Server This software enables your application to send/receive Bitcoin payments or to data mine the network's transaction history, with ease. -You may run it stand-alone utilizing its own validation engine or as a slave behind the Satoshi client so it accepts exactly what the 'reference client' does. In either case it builds a fully indexed block chain of transactions stored in LevelDB or in a relational database. +You may run it stand-alone utilizing its own validation engine or as a slave behind the Satoshi client so it accepts exactly what the 'reference client' does. In either case it builds a fully indexed block chain of transactions stored in LevelDB. The server process handles peer-to-peer communication with the network and serves clients connected to it through a message bus. Wallet(s) are implemented by the client library and transactions are also signed at the client side, therefore it is safe to operate the Server in a remote environment as it does not store or receive private keys. @@ -34,18 +34,16 @@ Make sure you have Maven3, JDK 7 (with JCE Unlimited Strength Policy Jurisdictio Run --- -java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.1.3.jar testnet3 memdb +java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.2.0.jar testnet3 memdb The final two parameters of the above example command line identify configuration contexts stored under server/src/main/resources/context. You have to choose one of the networks by specifying either testnet3 or production or slave, and a database layer, that could be (examples): * memdb - in memory database for tests * leveldb - LevelDB - * derby - For embedded relational database Derby - * progresql - To connect to a ProgreSQL server -Review the context file of the SQL databases before attempting to use them, since connection parameters will likely not apply to your installation. To use the API of your local server you need to run a message broker process providing the infrastructure. Since the message bus offers authentication and a wide selection of transports, your installation will likely be unique and need to be reflected in server/src/main/resources/context/BCSAPI-profile.xml. You find example configurations for the message broker Apollo and Active MQ there. The complete command line for a production environment might be: +To use the API of your local server you need to run a message broker process providing the infrastructure. Since the message bus offers authentication and a wide selection of transports, your installation will likely be unique and need to be reflected in server/src/main/resources/context/BCSAPI-profile.xml. You find example configurations for the message broker Apollo and Active MQ there. The complete command line for a production environment might be: -java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.1.3.jar production leveldb BCSAPI apollo +java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.2.0.jar production leveldb BCSAPI apollo License diff --git a/api/pom.xml b/api/pom.xml index d3e477fd..b2dcf8e9 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2-SNAPSHOT + 1.2.0 ../ diff --git a/pom.xml b/pom.xml index 8a89f949..bae693fd 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.2-SNAPSHOT + 1.2.0 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/pom.xml b/server/pom.xml index 5512f57e..7caa7776 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2-SNAPSHOT + 1.2.0 ../ From 50f4112d14793dc6e71a14f7858ea060f88ba085 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 15 Aug 2013 15:18:44 +0200 Subject: [PATCH 159/299] use correct version in script --- server/bop-service.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/bop-service.sh b/server/bop-service.sh index 6ecd9008..a6da183b 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.0.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.0.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" From 31ff90da91dd7da98aa0ed6ffb7890f3c90e7d13 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 15 Aug 2013 17:04:57 +0200 Subject: [PATCH 160/299] add utxo scan --- .../com/bitsofproof/supernode/api/BCSAPI.java | 23 ++++++++++++-- .../supernode/api/JMSServerConnector.java | 31 ++++++++++++++++--- .../supernode/core/BlockStore.java | 5 +-- .../supernode/core/CachedBlockStore.java | 9 +++--- .../supernode/core/ImplementBCSAPI.java | 20 +++++++++--- .../bitsofproof/supernode/core/TxHandler.java | 2 +- .../bitsofproof/supernode/model/LvlStore.java | 15 --------- .../supernode/model/OrderedMapStoreKey.java | 2 +- .../com/bitsofproof/supernode/model/Tx.java | 8 +++-- 9 files changed, 80 insertions(+), 35 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java index c5c05fcd..98b638a2 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java @@ -127,7 +127,7 @@ public interface BCSAPI public void removeTrunkListener (TrunkListener listener); /** - * Match transactions using and address or outpoint in match. + * Scan transactions using and address or outpoint in match. * * @param match * @param listener @@ -136,7 +136,7 @@ public interface BCSAPI public void scanTransactions (Collection match, UpdateMode mode, long after, TransactionListener listener) throws BCSAPIException; /** - * scan transactions for an account + * Scan transactions for an account * * @param master * - public master key @@ -144,4 +144,23 @@ public interface BCSAPI * @throws BCSAPIException */ public void scanTransactions (ExtendedKey master, int lookAhead, long after, TransactionListener listener) throws BCSAPIException; + + /** + * Scan unspent transactions using and address or outpoint in match. + * + * @param match + * @param listener + * @throws BCSAPIException + */ + public void scanUTXO (Collection match, UpdateMode mode, long after, TransactionListener listener) throws BCSAPIException; + + /** + * Scan unspent transactions for an account + * + * @param master + * - public master key + * @param listener + * @throws BCSAPIException + */ + public void scanUTXO (ExtendedKey master, int lookAhead, long after, TransactionListener listener) throws BCSAPIException; } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java index bdc28de9..e0368c33 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java @@ -320,8 +320,32 @@ public boolean isProduction () throws BCSAPIException return production = getBlockHeader ("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f") != null; } + @Override + public void scanUTXO (Collection match, UpdateMode mode, long after, TransactionListener listener) throws BCSAPIException + { + scanRequest (match, mode, after, listener, "utxoMatchRequest"); + } + + @Override + public void scanUTXO (ExtendedKey master, int lookAhead, long after, TransactionListener listener) throws BCSAPIException + { + scanRequest (master, lookAhead, after, listener, "utxoAccountRequest"); + } + @Override public void scanTransactions (Collection match, UpdateMode mode, long after, final TransactionListener listener) throws BCSAPIException + { + scanRequest (match, mode, after, listener, "matchRequest"); + } + + @Override + public void scanTransactions (ExtendedKey master, int lookAhead, long after, final TransactionListener listener) throws BCSAPIException + { + scanRequest (master, lookAhead, after, listener, "accountRequest"); + } + + private void scanRequest (Collection match, UpdateMode mode, long after, final TransactionListener listener, String requestQueue) + throws BCSAPIException { Session session = null; try @@ -329,7 +353,7 @@ public void scanTransactions (Collection match, UpdateMode mode, long af session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); BytesMessage m = session.createBytesMessage (); - MessageProducer exactMatchProducer = session.createProducer (session.createQueue ("matchRequest")); + MessageProducer exactMatchProducer = session.createProducer (session.createQueue (requestQueue)); BCSAPIMessage.ExactMatchRequest.Builder builder = BCSAPIMessage.ExactMatchRequest.newBuilder (); builder.setBcsapiversion (1); builder.setMode (mode.ordinal ()); @@ -400,8 +424,7 @@ public void onMessage (Message message) } } - @Override - public void scanTransactions (ExtendedKey master, int lookAhead, long after, final TransactionListener listener) throws BCSAPIException + private void scanRequest (ExtendedKey master, int lookAhead, long after, final TransactionListener listener, String request) throws BCSAPIException { if ( !master.isReadOnly () ) { @@ -413,7 +436,7 @@ public void scanTransactions (ExtendedKey master, int lookAhead, long after, fin session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); BytesMessage m = session.createBytesMessage (); - MessageProducer scanAccountProducer = session.createProducer (session.createQueue ("accountRequest")); + MessageProducer scanAccountProducer = session.createProducer (session.createQueue (request)); BCSAPIMessage.AccountRequest.Builder builder = BCSAPIMessage.AccountRequest.newBuilder (); builder.setBcsapiversion (1); builder.setPublicKey (master.serialize (production)); diff --git a/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java index f58eeb82..e67aeee3 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java @@ -76,8 +76,9 @@ public interface TransactionProcessor public void scan (BloomFilter filter, TransactionProcessor processor) throws ValidationException; - public void filterTransactions (Set match, UpdateMode update, long after, TransactionProcessor processor) throws ValidationException; + public void filterTransactions (boolean utxo, Set match, UpdateMode update, long after, TransactionProcessor processor) + throws ValidationException; - public void filterTransactions (Set match, ExtendedKey ek, int lookAhead, long after, TransactionProcessor processor) + public void filterTransactions (boolean utxo, Set match, ExtendedKey ek, int lookAhead, long after, TransactionProcessor processor) throws ValidationException; } \ No newline at end of file diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index ba1019bd..8f99acde 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -400,7 +400,7 @@ else if ( blockChain.getLast () != p ) } @Override - public void filterTransactions (Set matchSet, ExtendedKey ek, int lookAhead, long after, TransactionProcessor processor) + public void filterTransactions (boolean utxo, Set matchSet, ExtendedKey ek, int lookAhead, long after, TransactionProcessor processor) throws ValidationException { Map addressSet = new HashMap (); @@ -449,7 +449,7 @@ public void filterTransactions (Set matchSet, ExtendedKey ek, int lo Blk b = retrieveBlock (cb); for ( Tx t : b.getTransactions () ) { - if ( t.matches (matchSet, UpdateMode.all) ) + if ( t.matches (utxo, matchSet, UpdateMode.all) ) { processor.process (t); @@ -491,7 +491,8 @@ public void filterTransactions (Set matchSet, ExtendedKey ek, int lo } @Override - public void filterTransactions (Set matchSet, UpdateMode update, long after, TransactionProcessor processor) throws ValidationException + public void filterTransactions (boolean utxo, Set matchSet, UpdateMode update, long after, TransactionProcessor processor) + throws ValidationException { try { @@ -532,7 +533,7 @@ public void filterTransactions (Set matchSet, UpdateMode update, lon Blk b = retrieveBlock (cb); for ( Tx t : b.getTransactions () ) { - if ( t.matches (matchSet, update) ) + if ( t.matches (utxo, matchSet, update) ) { processor.process (t); ++n; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index 88ced7ef..f63b8c0f 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -178,7 +178,13 @@ private void closeSession (Session session) private void addMatchScanListener () throws JMSException { - addQueueListener ("matchRequest", new SessionMessageListener () + addMatchScanListener (false, "matchRequest"); + addMatchScanListener (true, "utxoMatchRequest"); + } + + private void addMatchScanListener (final boolean utxo, String topic) throws JMSException + { + addQueueListener (topic, new SessionMessageListener () { @Override public void onMessage (Message msg) @@ -236,7 +242,7 @@ public void process (Tx tx) } }; - store.filterTransactions (match, mode, after, processor); + store.filterTransactions (utxo, match, mode, after, processor); int c = counter.get (); log.debug ("matchRequest returns " + counter.get () + " transactions from blockchain"); @@ -278,7 +284,13 @@ public void process (Tx tx) private void addAccountScanListener () throws JMSException { - addQueueListener ("accountRequest", new SessionMessageListener () + addAccountScanListener (false, "accountRequest"); + addAccountScanListener (true, "utxoAccountRequest"); + } + + private void addAccountScanListener (final boolean utxo, String topic) throws JMSException + { + addQueueListener (topic, new SessionMessageListener () { @Override public void onMessage (Message msg) @@ -333,7 +345,7 @@ public void process (Tx tx) } }; - store.filterTransactions (match, ek, lookAhead, after, processor); + store.filterTransactions (utxo, match, ek, lookAhead, after, processor); int c = counter.get (); log.debug ("accountRequest returns " + counter.get () + " transactions from blockchain"); diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index 35c0ec18..97018adf 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -241,7 +241,7 @@ public void scanUnconfirmedPool (Set matchSet, UpdateMode update, Tr { for ( Tx t : unconfirmed.values () ) { - if ( t.matches (matchSet, update) ) + if ( t.matches (false, matchSet, update) ) { processor.process (t); } diff --git a/server/src/main/java/com/bitsofproof/supernode/model/LvlStore.java b/server/src/main/java/com/bitsofproof/supernode/model/LvlStore.java index 8a3eb9fa..1d48b347 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/LvlStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/LvlStore.java @@ -140,21 +140,6 @@ public Tx getTransaction (String hash) throws ValidationException return t; } - private void writePeer (KnownPeer p) - { - store.put (OrderedMapStoreKey.createKey (KeyType.PEER, p.getAddress ().getBytes ()), p.toLevelDB ()); - } - - private KnownPeer readPeer (String address) throws ValidationException - { - byte[] data = store.get (OrderedMapStoreKey.createKey (KeyType.PEER, address.getBytes ())); - if ( data != null ) - { - return KnownPeer.fromLevelDB (data); - } - return null; - } - @Override protected void cacheChain () { diff --git a/server/src/main/java/com/bitsofproof/supernode/model/OrderedMapStoreKey.java b/server/src/main/java/com/bitsofproof/supernode/model/OrderedMapStoreKey.java index b77a77cb..c6d6581b 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/OrderedMapStoreKey.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/OrderedMapStoreKey.java @@ -19,7 +19,7 @@ public class OrderedMapStoreKey { public static enum KeyType { - TX, BLOCK, HEAD, PEER, COLOR; + TX, BLOCK, HEAD; } public static byte[] createKey (KeyType kt, byte[] key) diff --git a/server/src/main/java/com/bitsofproof/supernode/model/Tx.java b/server/src/main/java/com/bitsofproof/supernode/model/Tx.java index 5befb939..27d79697 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/Tx.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/Tx.java @@ -330,12 +330,16 @@ else if ( filter.getUpdateMode () == UpdateMode.keys ) return false; } - public boolean matches (Set matchSet, UpdateMode update) + public boolean matches (boolean utxo, Set matchSet, UpdateMode update) { boolean found; found = false; for ( TxOut o : outputs ) { + if ( utxo && !o.isAvailable () ) + { + continue; + } try { for ( Token token : ScriptFormat.parse (o.getScript ()) ) @@ -363,7 +367,7 @@ else if ( update == UpdateMode.keys ) // best effort } } - if ( !found ) + if ( !utxo && !found ) { for ( TxIn i : inputs ) { From 97f13c6138cae2cdd76da00b0440398f95d2f795 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 15 Aug 2013 17:12:27 +0200 Subject: [PATCH 161/299] use utxo scan in account manager --- .../bitsofproof/supernode/api/AddressListAccountManager.java | 2 +- .../bitsofproof/supernode/api/ExtendedKeyAccountManager.java | 2 +- .../com/bitsofproof/supernode/api/KeyListAccountManager.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java index 6b2b852e..725e8094 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java @@ -63,7 +63,7 @@ public void addAddress (byte[] address) public void sync (BCSAPI api) throws BCSAPIException, ValidationException { log.trace ("Sync naddr: " + addresses.size ()); - api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () + api.scanUTXO (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () { @Override public void process (Transaction t) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java index a049af39..e5ba160c 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java @@ -143,7 +143,7 @@ public void sync (BCSAPI api, final int lookAhead) throws BCSAPIException, Valid this.lookAhead = lookAhead; ensureLookAhead (0); log.trace ("Sync nkeys: " + getNumberOfKeys ()); - api.scanTransactions (getMaster (), lookAhead, getCreated (), new TransactionListener () + api.scanUTXO (getMaster (), lookAhead, getCreated (), new TransactionListener () { @Override public void process (Transaction t) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java index a556589e..199798b3 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java @@ -62,7 +62,7 @@ public void addKey (ECKeyPair key) public void sync (BCSAPI api) throws BCSAPIException, ValidationException { log.trace ("Sync nkeys: " + keys.size ()); - api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () + api.scanUTXO (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () { @Override public void process (Transaction t) From 6234cd9316df3addac6c74c30c1dee3f6639c39c Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 15 Aug 2013 17:13:58 +0200 Subject: [PATCH 162/299] 1.2.1 --- api/pom.xml | 2 +- pom.xml | 2 +- server/bop-service.sh | 4 ++-- server/pom.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index b2dcf8e9..f91e7198 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.0 + 1.2.1 ../ diff --git a/pom.xml b/pom.xml index bae693fd..e55a72b3 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.2.0 + 1.2.1 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/bop-service.sh b/server/bop-service.sh index a6da183b..cbd1cb76 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.0.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.1.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.0.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.1.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" diff --git a/server/pom.xml b/server/pom.xml index 7caa7776..a505b2b0 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.0 + 1.2.1 ../ From 4cd6e7de738ae1ab63edc6ea41eb71bac5d0ed48 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 15 Aug 2013 17:35:32 +0200 Subject: [PATCH 163/299] log type of scan --- .../java/com/bitsofproof/supernode/core/ImplementBCSAPI.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index f63b8c0f..32850791 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -204,7 +204,7 @@ public void onMessage (Message msg) final UpdateMode mode = UpdateMode.values ()[request.getMode ()]; final Destination destination = msg.getJMSReplyTo (); final long after = request.hasAfter () ? request.getAfter () : 0; - log.debug ("matchRequest for ", match.size () + " patterns after " + after); + log.debug ("matchRequest " + (utxo ? "UTXO" : "") + " for ", match.size () + " patterns after " + after); requestProcessor.execute (new Runnable () { @Override @@ -305,7 +305,7 @@ public void onMessage (Message msg) final ExtendedKey ek = ExtendedKey.parse (request.getPublicKey ()); final int lookAhead = request.getLookAhead (); final long after = request.getAfter (); - log.debug ("accountRequest for " + ek.serialize (true) + " after " + after); + log.debug ("accountRequest " + (utxo ? "UTXO" : "") + " for " + ek.serialize (true) + " after " + after); final Set match = new HashSet (); final UpdateMode mode = UpdateMode.all; final Destination destination = msg.getJMSReplyTo (); From 5c32803a20794823082f36dcc17eff1eb7ca50d7 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 15 Aug 2013 18:37:09 +0200 Subject: [PATCH 164/299] initialize since --- api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java | 1 + 1 file changed, 1 insertion(+) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java index 236c1721..378a2dc0 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java @@ -45,6 +45,7 @@ public String getName () public FileWallet (String fileName) { this.fileName = fileName; + this.since = System.currentTimeMillis (); } public boolean exists () From 627765dae2872509e2683aeb5f18f6766a586498 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 17 Aug 2013 09:57:02 +0200 Subject: [PATCH 165/299] add fee estimation --- .../supernode/api/AccountManager.java | 2 ++ .../supernode/api/BaseAccountManager.java | 34 ++++++++++++------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java index 5755c955..635c38f9 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java @@ -48,6 +48,8 @@ public interface UTXO public Transaction pay (byte[] receiver, long amount, long fee) throws ValidationException; + public Transaction pay (byte[] receiver, long amount) throws ValidationException; + public Transaction split (long[] amounts, long fee) throws ValidationException; public long getBalance (); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java index 69a60841..efe4ad93 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java @@ -115,19 +115,6 @@ public static long estimateFee (Transaction t) return Math.min (MAXIMUM_FEE, Math.max (MINIMUM_FEE, writer.toByteArray ().length / 1000 * MINIMUM_FEE)); } - protected Transaction createSpend (List sources, List sinks) throws ValidationException - { - long fee = MINIMUM_FEE; - long prevfee = MINIMUM_FEE; - Transaction t = createSpend (sources, sinks, fee); - while ( (fee = estimateFee (t)) > prevfee ) - { - t = createSpend (sources, sinks, fee); - prevfee = fee; - } - return t; - } - protected Transaction createSpend (List sources, List sinks, long fee) throws ValidationException { if ( fee < 0 || fee > MAXIMUM_FEE ) @@ -404,6 +391,27 @@ public Transaction pay (byte[] receiver, long amount, long fee) throws Validatio } } + @Override + public Transaction pay (byte[] receiver, long amount) throws ValidationException + { + long fee = MINIMUM_FEE; + long estimate = 0; + Transaction t = null; + + do + { + fee = Math.max (fee, estimate); + t = pay (receiver, amount, fee); + estimate = estimateFee (t); + if ( fee < estimate ) + { + log.trace ("The transaction requires more network fees. Reassembling."); + } + } while ( fee < estimate ); + + return t; + } + @Override public Transaction split (long[] amounts, long fee) throws ValidationException { From 71816d619df83ecac01d7b273aeb601aa0593c46 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 17 Aug 2013 12:14:34 +0200 Subject: [PATCH 166/299] use -SNAPSHOT --- api/pom.xml | 2 +- pom.xml | 2 +- server/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index f91e7198..1445f0cf 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.1 + 1.2.2-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index e55a72b3..d106d746 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.2.1 + 1.2.2-SNAPSHOT supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/pom.xml b/server/pom.xml index a505b2b0..4d4638e5 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.1 + 1.2.2-SNAPSHOT ../ From 7855569f6538b7d304001c149049ca1025442109 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 17 Aug 2013 12:19:02 +0200 Subject: [PATCH 167/299] rather use -SNAPSHOT for latest of the branch --- api/pom.xml | 2 +- pom.xml | 2 +- server/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 1445f0cf..d3e477fd 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.2-SNAPSHOT + 1.2-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index d106d746..8a89f949 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.2.2-SNAPSHOT + 1.2-SNAPSHOT supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/pom.xml b/server/pom.xml index 4d4638e5..5512f57e 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.2-SNAPSHOT + 1.2-SNAPSHOT ../ From 9a32318d086d48572d40f03ba48166e406c639dc Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Tue, 20 Aug 2013 13:38:15 +0200 Subject: [PATCH 168/299] moved sync up into account manager add support for first index in account scan removed color --- .../supernode/api/AccountManager.java | 4 + .../api/AddressListAccountManager.java | 3 +- .../com/bitsofproof/supernode/api/BCSAPI.java | 4 +- .../supernode/api/BaseAccountManager.java | 5 - .../com/bitsofproof/supernode/api/Color.java | 168 ------------------ .../bitsofproof/supernode/api/ColorRules.java | 112 ------------ .../api/ExtendedKeyAccountManager.java | 33 +++- .../bitsofproof/supernode/api/FileWallet.java | 6 +- .../supernode/api/JMSServerConnector.java | 20 +-- .../supernode/api/KeyListAccountManager.java | 3 +- api/src/main/proto/BCSAPIMessage.proto | 19 +- .../supernode/api/ColorRulesTest.java | 118 ------------ server/bop-service.sh | 4 +- .../supernode/core/BlockStore.java | 4 +- .../supernode/core/CachedBlockStore.java | 26 +-- .../supernode/core/ImplementBCSAPI.java | 3 +- 16 files changed, 58 insertions(+), 474 deletions(-) delete mode 100644 api/src/main/java/com/bitsofproof/supernode/api/Color.java delete mode 100644 api/src/main/java/com/bitsofproof/supernode/api/ColorRules.java delete mode 100644 api/src/test/java/com/bitsofproof/supernode/api/ColorRulesTest.java diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java index 635c38f9..4e8f1d71 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java @@ -38,12 +38,16 @@ public interface UTXO } + public void sync (BCSAPI api) throws BCSAPIException; + public long getCreated (); public int getNumberOfKeys (); public Key getNextKey () throws ValidationException; + public Key getKeyForAddress (byte[] address); + public Collection getAddresses (); public Transaction pay (byte[] receiver, long amount, long fee) throws ValidationException; diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java index 725e8094..e8c8b822 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java @@ -60,7 +60,8 @@ public void addAddress (byte[] address) addresses.add (new ByteVector (address)); } - public void sync (BCSAPI api) throws BCSAPIException, ValidationException + @Override + public void sync (BCSAPI api) throws BCSAPIException { log.trace ("Sync naddr: " + addresses.size ()); api.scanUTXO (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java index 98b638a2..cf09185c 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java @@ -143,7 +143,7 @@ public interface BCSAPI * @param listener * @throws BCSAPIException */ - public void scanTransactions (ExtendedKey master, int lookAhead, long after, TransactionListener listener) throws BCSAPIException; + public void scanTransactions (ExtendedKey master, int firstIndex, int lookAhead, long after, TransactionListener listener) throws BCSAPIException; /** * Scan unspent transactions using and address or outpoint in match. @@ -162,5 +162,5 @@ public interface BCSAPI * @param listener * @throws BCSAPIException */ - public void scanUTXO (ExtendedKey master, int lookAhead, long after, TransactionListener listener) throws BCSAPIException; + public void scanUTXO (ExtendedKey master, int firstIndex, int lookAhead, long after, TransactionListener listener) throws BCSAPIException; } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java index efe4ad93..992b3bac 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java @@ -55,11 +55,6 @@ public void setCreated (long created) private final List accountListener = Collections.synchronizedList (new ArrayList ()); private final Map transactions = new HashMap (); - public abstract Key getKeyForAddress (byte[] address); - - @Override - public abstract Key getNextKey () throws ValidationException; - protected UTXO createConfirmedUTXO () { return new InMemoryUTXO (); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Color.java b/api/src/main/java/com/bitsofproof/supernode/api/Color.java deleted file mode 100644 index 57c4754c..00000000 --- a/api/src/main/java/com/bitsofproof/supernode/api/Color.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2013 bits of proof zrt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.bitsofproof.supernode.api; - -import java.io.UnsupportedEncodingException; -import java.util.Arrays; - -import com.bitsofproof.supernode.common.ECKeyPair; -import com.bitsofproof.supernode.common.Hash; -import com.bitsofproof.supernode.common.Key; -import com.bitsofproof.supernode.common.ValidationException; -import com.bitsofproof.supernode.common.WireFormat; -import com.google.protobuf.ByteString; - -public class Color -{ - private String transaction; - private String terms; - private long unit; - private int expiryHeight; - private byte[] pubkey; - private byte[] signature; - - public BCSAPIMessage.Color toProtobuf () - { - BCSAPIMessage.Color.Builder builder = BCSAPIMessage.Color.newBuilder (); - builder.setTransaction (ByteString.copyFrom (new Hash (transaction).toByteArray ())); - builder.setTerms (terms); - builder.setUnit (unit); - builder.setExpiryHeight (expiryHeight); - builder.setPubkey (ByteString.copyFrom (pubkey)); - builder.setSignature (ByteString.copyFrom (signature)); - return builder.build (); - } - - public static Color fromProtobuf (BCSAPIMessage.Color po) - { - Color color = new Color (); - color.setTransaction (new Hash (po.getTransaction ().toByteArray ()).toString ()); - color.setTerms (po.getTerms ()); - color.setUnit (po.getUnit ()); - color.setExpiryHeight (po.getExpiryHeight ()); - color.setPubkey (po.getPubkey ().toByteArray ()); - color.setSignature (po.getSignature ().toByteArray ()); - return color; - } - - public String getFungibleName () - { - WireFormat.Writer writer = new WireFormat.Writer (); - try - { - writer.writeBytes (terms.getBytes ("UTF-8")); - } - catch ( UnsupportedEncodingException e ) - { - } - writer.writeUint64 (unit); - writer.writeUint32 (expiryHeight); - byte[] content = writer.toByteArray (); - return new Hash (Hash.hash (content)).toString (); - } - - private byte[] hashContent () - { - WireFormat.Writer writer = new WireFormat.Writer (); - writer.writeHash (new Hash (transaction)); - try - { - writer.writeBytes (terms.getBytes ("UTF-8")); - } - catch ( UnsupportedEncodingException e ) - { - } - writer.writeUint64 (unit); - writer.writeUint32 (expiryHeight); - writer.writeVarBytes (pubkey); - byte[] content = writer.toByteArray (); - return Hash.hash (content); - } - - public void sign (Key key) throws ValidationException - { - if ( !Arrays.equals (key.getAddress (), Hash.keyHash (pubkey)) ) - { - throw new ValidationException ("Wrong key to sign this color"); - } - signature = key.sign (hashContent ()); - } - - public boolean verify () - { - return ECKeyPair.verify (hashContent (), signature, pubkey); - } - - public int getExpiryHeight () - { - return expiryHeight; - } - - public void setExpiryHeight (int expiryHeight) - { - this.expiryHeight = expiryHeight; - } - - public long getUnit () - { - return unit; - } - - public void setUnit (long unit) - { - this.unit = unit; - } - - public byte[] getSignature () - { - return signature; - } - - public void setSignature (byte[] signature) - { - this.signature = signature; - } - - public String getTransaction () - { - return transaction; - } - - public void setTransaction (String transaction) - { - this.transaction = transaction; - } - - public String getTerms () - { - return terms; - } - - public void setTerms (String terms) - { - this.terms = terms; - } - - public byte[] getPubkey () - { - return pubkey; - } - - public void setPubkey (byte[] pubkey) - { - this.pubkey = pubkey; - } -} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ColorRules.java b/api/src/main/java/com/bitsofproof/supernode/api/ColorRules.java deleted file mode 100644 index 53e3eccd..00000000 --- a/api/src/main/java/com/bitsofproof/supernode/api/ColorRules.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2013 bits of proof zrt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.bitsofproof.supernode.api; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public class ColorRules -{ - public static class ColoredCoin - { - public String color; - public long value; - } - - public static void colorOutput (List input, List output) - { - if ( validColorband (input) ) - { - List colorOrder = new ArrayList (); - Map colorSums = new HashMap (); - for ( ColoredCoin c : input ) - { - if ( c.color != null ) - { - if ( colorOrder.isEmpty () || !c.color.equals (colorOrder.get (colorOrder.size () - 1)) ) - { - colorOrder.add (c.color); - } - Long s = colorSums.get (c.color); - colorSums.put (c.color, s == null ? c.value : s.longValue () + c.value); - } - } - Iterator o = output.iterator (); - for ( String color : colorOrder ) - { - long offer = colorSums.get (color); - while ( o.hasNext () ) - { - ColoredCoin out = o.next (); - if ( out.value > offer ) - { - // broke coloring from this point - return; - } - offer -= out.value; - out.color = color; - if ( offer == 0 ) - { - break; - } - } - } - } - } - - private static boolean validColorband (List coins) - { - boolean first = true; - boolean startsWithColors = false; - String lastColor = null; - Set seen = new HashSet (); - - for ( ColoredCoin c : coins ) - { - if ( first ) - { - if ( c.color != null ) - { - // must start with colors or no colors at all - startsWithColors = true; - } - first = false; - } - - if ( c.color != null ) - { - if ( !startsWithColors ) - { - // color should have been first - return false; - } - if ( lastColor != null && !lastColor.equals (c.color) && seen.contains (c.color) ) - { - // same colors not consecutive - return false; - } - lastColor = c.color; - seen.add (lastColor); - } - } - return true; - } -} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java index e5ba160c..2dbf8c82 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java @@ -36,6 +36,7 @@ public class ExtendedKeyAccountManager extends BaseAccountManager implements Tra private ExtendedKey master; private int nextSequence; private int lookAhead = 100; + private int firstIndex; public ExtendedKey getMaster () { @@ -53,6 +54,26 @@ public int getNumberOfKeys () return nextSequence; } + public int getFirstIndex () + { + return firstIndex; + } + + public void setLookAhead (int lookAhead) + { + this.lookAhead = lookAhead; + } + + public void setFirstIndex (int firstIndex) + { + this.firstIndex = firstIndex; + } + + public int getLookAhead () + { + return lookAhead; + } + @Override protected void notifyListener (Transaction t) { @@ -69,17 +90,17 @@ protected void notifyListener (Transaction t) private void ensureLookAhead (int from) { - while ( keyIDForAddress.size () < (from + lookAhead) ) + while ( keyIDForAddress.size () < (from + lookAhead - firstIndex) ) { Key key = null; try { - key = master.getKey (keyIDForAddress.size ()); + key = master.getKey (keyIDForAddress.size () + firstIndex); } catch ( ValidationException e ) { } - keyIDForAddress.put (new ByteVector (key.getAddress ()), keyIDForAddress.size ()); + keyIDForAddress.put (new ByteVector (key.getAddress ()), keyIDForAddress.size () + firstIndex); } } @@ -138,12 +159,12 @@ public Collection getAddresses () return addresses; } - public void sync (BCSAPI api, final int lookAhead) throws BCSAPIException, ValidationException + @Override + public void sync (BCSAPI api) throws BCSAPIException { - this.lookAhead = lookAhead; ensureLookAhead (0); log.trace ("Sync nkeys: " + getNumberOfKeys ()); - api.scanUTXO (getMaster (), lookAhead, getCreated (), new TransactionListener () + api.scanUTXO (getMaster (), firstIndex, lookAhead, getCreated (), new TransactionListener () { @Override public void process (Transaction t) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java index 378a2dc0..681cb70e 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java @@ -137,6 +137,7 @@ public static FileWallet read (String fileName) throws IOException, ValidationEx { ExtendedKey pub = ExtendedKey.parse (account.getPublicKey ()); NCExtendedKeyAccountManager am = new NCExtendedKeyAccountManager (account.getName (), account.getCreated () * 1000); + am.setFirstIndex (account.getFirstIndex ()); wallet.accounts.put (account.getName (), am); am.setMaster (pub); } @@ -148,11 +149,11 @@ public static FileWallet read (String fileName) throws IOException, ValidationEx return wallet; } - public void sync (BCSAPI api, int lookAhead) throws BCSAPIException, ValidationException + public void sync (BCSAPI api) throws BCSAPIException, ValidationException { for ( NCExtendedKeyAccountManager account : accounts.values () ) { - account.sync (api, lookAhead); + account.sync (api); } } @@ -196,6 +197,7 @@ public void persist () throws IOException BCSAPIMessage.Wallet.Account.Builder ab = BCSAPIMessage.Wallet.Account.newBuilder (); ab.setName (am.getName ()); ab.setCreated (am.getCreated () / 1000); + ab.setFirstIndex (am.getFirstIndex ()); ab.setPublicKey (am.getMaster ().getReadOnly ().serialize (true)); builder.addAccounts (ab.build ()); } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java index e0368c33..01b6c187 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java @@ -20,7 +20,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.UUID; import java.util.concurrent.Semaphore; import javax.jms.BytesMessage; @@ -50,8 +49,6 @@ public class JMSServerConnector implements BCSAPI private ConnectionFactory connectionFactory; private Connection connection; - private String clientId = UUID.randomUUID ().toString (); - private Boolean production = null; private final Map messageDispatcher = new HashMap (); @@ -153,11 +150,6 @@ public MessageConsumer getConsumer () } } - public void setClientId (String clientId) - { - this.clientId = clientId; - } - public void setConnectionFactory (ConnectionFactory connectionFactory) { this.connectionFactory = connectionFactory; @@ -208,7 +200,6 @@ public void init () { log.debug ("Initialize BCSAPI Bus adaptor"); connection = connectionFactory.createConnection (); - connection.setClientID (clientId); connection.start (); } catch ( Exception e ) @@ -327,9 +318,9 @@ public void scanUTXO (Collection match, UpdateMode mode, long after, Tra } @Override - public void scanUTXO (ExtendedKey master, int lookAhead, long after, TransactionListener listener) throws BCSAPIException + public void scanUTXO (ExtendedKey master, int firstIndex, int lookAhead, long after, TransactionListener listener) throws BCSAPIException { - scanRequest (master, lookAhead, after, listener, "utxoAccountRequest"); + scanRequest (master, firstIndex, lookAhead, after, listener, "utxoAccountRequest"); } @Override @@ -339,9 +330,9 @@ public void scanTransactions (Collection match, UpdateMode mode, long af } @Override - public void scanTransactions (ExtendedKey master, int lookAhead, long after, final TransactionListener listener) throws BCSAPIException + public void scanTransactions (ExtendedKey master, int firstIndex, int lookAhead, long after, final TransactionListener listener) throws BCSAPIException { - scanRequest (master, lookAhead, after, listener, "accountRequest"); + scanRequest (master, firstIndex, lookAhead, after, listener, "accountRequest"); } private void scanRequest (Collection match, UpdateMode mode, long after, final TransactionListener listener, String requestQueue) @@ -424,7 +415,8 @@ public void onMessage (Message message) } } - private void scanRequest (ExtendedKey master, int lookAhead, long after, final TransactionListener listener, String request) throws BCSAPIException + private void scanRequest (ExtendedKey master, int firstIndex, int lookAhead, long after, final TransactionListener listener, String request) + throws BCSAPIException { if ( !master.isReadOnly () ) { diff --git a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java index 199798b3..5dd5b086 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java @@ -59,7 +59,8 @@ public void addKey (ECKeyPair key) keyByAddress.put (new ByteVector (key.getAddress ()), key); } - public void sync (BCSAPI api) throws BCSAPIException, ValidationException + @Override + public void sync (BCSAPI api) throws BCSAPIException { log.trace ("Sync nkeys: " + keys.size ()); api.scanUTXO (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () diff --git a/api/src/main/proto/BCSAPIMessage.proto b/api/src/main/proto/BCSAPIMessage.proto index 739dd256..bfa58dce 100644 --- a/api/src/main/proto/BCSAPIMessage.proto +++ b/api/src/main/proto/BCSAPIMessage.proto @@ -86,23 +86,6 @@ message ExceptionMessage { repeated string message = 2; } -message Color { - required bytes transaction = 1; - required string terms = 2; - required uint64 unit = 3; - required uint32 expiryHeight = 4; - required bytes pubkey = 5; - required bytes signature = 6; -} - -message FilterRequest { - required uint32 bcsapiversion = 1; - required bytes filter = 2; - required uint32 hashFunctions = 3; - required uint32 tweak = 4; - required uint32 mode = 5; -} - message ExactMatchRequest { required uint32 bcsapiversion = 1; @@ -117,6 +100,7 @@ message AccountRequest required string publicKey = 2; required uint32 lookAhead = 3; optional uint64 after = 4; + optional uint32 firstIndex = 5; } message Wallet { @@ -128,6 +112,7 @@ message Wallet { required string publicKey = 1; required uint64 created = 2; required string name = 3; + optional uint32 firstIndex = 4; } repeated Account accounts = 4; } \ No newline at end of file diff --git a/api/src/test/java/com/bitsofproof/supernode/api/ColorRulesTest.java b/api/src/test/java/com/bitsofproof/supernode/api/ColorRulesTest.java deleted file mode 100644 index 2902120a..00000000 --- a/api/src/test/java/com/bitsofproof/supernode/api/ColorRulesTest.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2013 bits of proof zrt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.bitsofproof.supernode.api; - -import static org.junit.Assert.assertTrue; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.junit.Test; - -import com.bitsofproof.supernode.api.ColorRules.ColoredCoin; - -public class ColorRulesTest -{ - private static final String COLOR_RULES_TEST = "colorRules.json"; - - private JSONArray readObjectArray (String resource) throws IOException, JSONException - { - InputStream input = this.getClass ().getResource ("/" + resource).openStream (); - StringBuffer content = new StringBuffer (); - byte[] buffer = new byte[1024]; - int len; - while ( (len = input.read (buffer)) > 0 ) - { - byte[] s = new byte[len]; - System.arraycopy (buffer, 0, s, 0, len); - content.append (new String (buffer, "UTF-8")); - } - return new JSONArray (content.toString ()); - } - - /** - * format of the tests = [ test, ... ] - * - * test = { inputs : [{color: "color", value: n}] outputs : [{color: "color", value: n}], valid: true|false, comment: "" } - * - * - * @throws JSONException - * @throws IOException - * - */ - @Test - public void rulesTest () throws IOException, JSONException - { - JSONArray testData = readObjectArray (COLOR_RULES_TEST); - for ( int i = 0; i < testData.length (); ++i ) - { - JSONObject test = testData.getJSONObject (i); - List inputs = fromJSON (test.getJSONArray ("inputs")); - List outputs = fromJSON (test.getJSONArray ("outputs")); - List workingCopy = new ArrayList (); - for ( ColoredCoin c : outputs ) - { - ColoredCoin copy = new ColoredCoin (); - copy.color = null; - copy.value = c.value; - workingCopy.add (copy); - } - - System.out.println ("Checking color scenario: " + test.getString ("comment") + ", expecting: " + test.getBoolean ("valid")); - ColorRules.colorOutput (inputs, workingCopy); - Iterator a = outputs.iterator (); - Iterator b = workingCopy.iterator (); - boolean valid = true; - while ( a.hasNext () ) - { - ColoredCoin one = a.next (); - ColoredCoin two = b.next (); - if ( one.color == null || two.color == null ) - { - if ( !(one.color == null && two.color == null) ) - { - valid = false; - } - } - else - { - valid = valid && one.color.equals (two.color); - } - } - assertTrue (valid == test.getBoolean ("valid")); - } - } - - private List fromJSON (JSONArray a) throws JSONException - { - List result = new ArrayList (); - for ( int i = 0; i < a.length (); ++i ) - { - JSONObject coin = a.getJSONObject (i); - ColoredCoin c = new ColoredCoin (); - c.color = coin.optString ("color", null); - c.value = coin.getLong ("value"); - result.add (c); - } - return result; - } -} diff --git a/server/bop-service.sh b/server/bop-service.sh index cbd1cb76..6ecd9008 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.1.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.1.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" diff --git a/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java index e67aeee3..9dddfe51 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java @@ -79,6 +79,6 @@ public interface TransactionProcessor public void filterTransactions (boolean utxo, Set match, UpdateMode update, long after, TransactionProcessor processor) throws ValidationException; - public void filterTransactions (boolean utxo, Set match, ExtendedKey ek, int lookAhead, long after, TransactionProcessor processor) - throws ValidationException; + public void filterTransactions (boolean utxo, Set match, ExtendedKey ek, int firstIndex, int lookAhead, long after, + TransactionProcessor processor) throws ValidationException; } \ No newline at end of file diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 8f99acde..75d5b449 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -20,7 +20,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -35,8 +34,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.bitsofproof.supernode.api.ColorRules; -import com.bitsofproof.supernode.api.ColorRules.ColoredCoin; import com.bitsofproof.supernode.api.ExtendedKey; import com.bitsofproof.supernode.common.BloomFilter; import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; @@ -400,12 +397,12 @@ else if ( blockChain.getLast () != p ) } @Override - public void filterTransactions (boolean utxo, Set matchSet, ExtendedKey ek, int lookAhead, long after, TransactionProcessor processor) - throws ValidationException + public void filterTransactions (boolean utxo, Set matchSet, ExtendedKey ek, int firstIndex, int lookAhead, long after, + TransactionProcessor processor) throws ValidationException { Map addressSet = new HashMap (); lookAhead = Math.min (Math.max (10, lookAhead), 1000); - for ( int i = 0; i < lookAhead; ++i ) + for ( int i = firstIndex; i < lookAhead; ++i ) { ByteVector address = new ByteVector (ek.getKey (i).getAddress ()); matchSet.add (address); @@ -1065,7 +1062,6 @@ public TransactionValidationException call () for ( Tx t : b.getTransactions () ) { t.setBlock (b); - List inputCoins = new ArrayList (); for ( TxIn i : t.getInputs () ) { if ( !i.getSourceHash ().equals (Hash.ZERO_HASH_STRING) ) @@ -1095,27 +1091,11 @@ public TransactionValidationException call () i.setSource (getSourceReference (source)); } - ColoredCoin c = new ColoredCoin (); - c.color = source.getColor (); - c.value = source.getValue (); - inputCoins.add (c); } } - List outputCoins = new ArrayList (); - for ( TxOut o : t.getOutputs () ) - { - ColoredCoin c = new ColoredCoin (); - c.color = null; - c.value = o.getValue (); - outputCoins.add (c); - } - ColorRules.colorOutput (inputCoins, outputCoins); - Iterator i = outputCoins.iterator (); - for ( TxOut o : t.getOutputs () ) { - o.setColor (i.next ().color); try { for ( Token token : ScriptFormat.parse (o.getScript ()) ) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index 32850791..57a73859 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -303,6 +303,7 @@ public void onMessage (Message msg) o.readBytes (body); BCSAPIMessage.AccountRequest request = BCSAPIMessage.AccountRequest.parseFrom (body); final ExtendedKey ek = ExtendedKey.parse (request.getPublicKey ()); + final int firstIndex = request.getFirstIndex (); final int lookAhead = request.getLookAhead (); final long after = request.getAfter (); log.debug ("accountRequest " + (utxo ? "UTXO" : "") + " for " + ek.serialize (true) + " after " + after); @@ -345,7 +346,7 @@ public void process (Tx tx) } }; - store.filterTransactions (utxo, match, ek, lookAhead, after, processor); + store.filterTransactions (utxo, match, ek, firstIndex, lookAhead, after, processor); int c = counter.get (); log.debug ("accountRequest returns " + counter.get () + " transactions from blockchain"); From 195948eba4b4ad15b1649d52c5f113f79565828e Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Tue, 20 Aug 2013 13:44:10 +0200 Subject: [PATCH 169/299] fix scan with firstIndex > 0 --- .../java/com/bitsofproof/supernode/core/CachedBlockStore.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 75d5b449..7ece89cd 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -402,7 +402,7 @@ public void filterTransactions (boolean utxo, Set matchSet, Extended { Map addressSet = new HashMap (); lookAhead = Math.min (Math.max (10, lookAhead), 1000); - for ( int i = firstIndex; i < lookAhead; ++i ) + for ( int i = firstIndex; i < firstIndex + lookAhead; ++i ) { ByteVector address = new ByteVector (ek.getKey (i).getAddress ()); matchSet.add (address); @@ -462,7 +462,7 @@ public void filterTransactions (boolean utxo, Set matchSet, Extended } } } - for ( int i = addressSet.size (); i < lastUsedAddress + lookAhead; ++i ) + for ( int i = addressSet.size (); i < lastUsedAddress + lookAhead - firstIndex; ++i ) { ByteVector address = new ByteVector (ek.getKey (i).getAddress ()); matchSet.add (address); From c96adffb8ee15449a7ea13a743a7705b517c61a0 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Tue, 20 Aug 2013 14:25:37 +0200 Subject: [PATCH 170/299] 1.2.2 --- api/pom.xml | 2 +- pom.xml | 2 +- server/bop-service.sh | 4 ++-- server/pom.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index d3e477fd..41d421fa 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2-SNAPSHOT + 1.2.2 ../ diff --git a/pom.xml b/pom.xml index 8a89f949..ba3e88e7 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.2-SNAPSHOT + 1.2.2 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/bop-service.sh b/server/bop-service.sh index cbd1cb76..6abd89a5 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.1.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.2.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.1.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.2.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" diff --git a/server/pom.xml b/server/pom.xml index 5512f57e..ceff0598 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2-SNAPSHOT + 1.2.2 ../ From d71d71cfbba7d9dd4c02e6d4880c06306b47e2cb Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Tue, 20 Aug 2013 14:36:16 +0200 Subject: [PATCH 171/299] wrong test conf --- server/src/test/resources/context/EmbeddedBCSAPI.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/test/resources/context/EmbeddedBCSAPI.xml b/server/src/test/resources/context/EmbeddedBCSAPI.xml index 3791d69b..c9c557fa 100644 --- a/server/src/test/resources/context/EmbeddedBCSAPI.xml +++ b/server/src/test/resources/context/EmbeddedBCSAPI.xml @@ -45,7 +45,6 @@ - From 1522722150b5ae422eda6b19084f686ad21ee7fd Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Tue, 20 Aug 2013 22:41:54 +0200 Subject: [PATCH 172/299] remove number of keys --- .../com/bitsofproof/supernode/api/AccountManager.java | 2 -- .../supernode/api/AddressListAccountManager.java | 6 ------ .../supernode/api/ExtendedKeyAccountManager.java | 10 ++-------- .../supernode/api/KeyListAccountManager.java | 6 ------ 4 files changed, 2 insertions(+), 22 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java index 4e8f1d71..94cf2e89 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java @@ -42,8 +42,6 @@ public interface UTXO public long getCreated (); - public int getNumberOfKeys (); - public Key getNextKey () throws ValidationException; public Key getKeyForAddress (byte[] address); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java index e8c8b822..241edf51 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java @@ -20,12 +20,6 @@ public class AddressListAccountManager extends BaseAccountManager private final Set addresses = new HashSet (); - @Override - public int getNumberOfKeys () - { - return addresses.size (); - } - @Override public Collection getAddresses () { diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java index 2dbf8c82..2737aab1 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java @@ -48,12 +48,6 @@ public void setMaster (ExtendedKey master) this.master = master; } - @Override - public int getNumberOfKeys () - { - return nextSequence; - } - public int getFirstIndex () { return firstIndex; @@ -163,7 +157,7 @@ public Collection getAddresses () public void sync (BCSAPI api) throws BCSAPIException { ensureLookAhead (0); - log.trace ("Sync nkeys: " + getNumberOfKeys ()); + log.trace ("Sync nkeys: " + (nextSequence - firstIndex)); api.scanUTXO (getMaster (), firstIndex, lookAhead, getCreated (), new TransactionListener () { @Override @@ -181,6 +175,6 @@ public void process (Transaction t) updateWithTransaction (t); } }); - log.trace ("Sync finished with nkeys: " + getNumberOfKeys ()); + log.trace ("Sync finished with nkeys: " + (nextSequence - firstIndex)); } } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java index 5dd5b086..d2dd5c9b 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java @@ -24,12 +24,6 @@ public class KeyListAccountManager extends BaseAccountManager private final List keys = new ArrayList (); private static final SecureRandom rnd = new SecureRandom (); - @Override - public int getNumberOfKeys () - { - return keys.size (); - } - @Override public Collection getAddresses () { From 7ad9aa89ec6eebb544983f30f76109d6559582ed Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 22 Aug 2013 12:18:12 +0200 Subject: [PATCH 173/299] re-enabling full scan as account manager need to be more intelligent for utxo scan. --- api/pom.xml | 2 +- .../supernode/api/AccountManager.java | 2 +- .../api/AddressListAccountManager.java | 28 +++++++++++---- .../api/ExtendedKeyAccountManager.java | 34 ++++++++++++++++--- .../bitsofproof/supernode/api/FileWallet.java | 2 +- .../supernode/api/KeyListAccountManager.java | 28 +++++++++++---- pom.xml | 2 +- server/bop-service.sh | 4 +-- server/pom.xml | 2 +- 9 files changed, 79 insertions(+), 25 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 41d421fa..cda9d380 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.2 + 1.2.3 ../ diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java index 94cf2e89..caa2a4a2 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java @@ -38,7 +38,7 @@ public interface UTXO } - public void sync (BCSAPI api) throws BCSAPIException; + public void sync (BCSAPI api, boolean utxo) throws BCSAPIException; public long getCreated (); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java index 241edf51..2e2754d3 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java @@ -55,17 +55,31 @@ public void addAddress (byte[] address) } @Override - public void sync (BCSAPI api) throws BCSAPIException + public void sync (BCSAPI api, boolean utxo) throws BCSAPIException { log.trace ("Sync naddr: " + addresses.size ()); - api.scanUTXO (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () + if ( utxo ) { - @Override - public void process (Transaction t) + api.scanUTXO (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () { - updateWithTransaction (t); - } - }); + @Override + public void process (Transaction t) + { + updateWithTransaction (t); + } + }); + } + else + { + api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () + { + @Override + public void process (Transaction t) + { + updateWithTransaction (t); + } + }); + } log.trace ("Sync finished naddr: " + addresses.size ()); } } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java index 2737aab1..16f53ef3 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java @@ -18,8 +18,10 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,6 +34,7 @@ public class ExtendedKeyAccountManager extends BaseAccountManager implements Tra { private static final Logger log = LoggerFactory.getLogger (ExtendedKeyAccountManager.class); + private final Set usedKeys = new HashSet (); private final Map keyIDForAddress = new HashMap (); private ExtendedKey master; private int nextSequence; @@ -116,9 +119,19 @@ public Key getNextKey () throws ValidationException return getKey (nextSequence++); } + public Set getUsedKeys () + { + return usedKeys; + } + public Integer getKeyIDForAddress (byte[] address) { - return keyIDForAddress.get (new ByteVector (address)); + Integer id = getKeyIDForAddress (address); + if ( id != null ) + { + usedKeys.add (id); + } + return id; } @Override @@ -154,11 +167,11 @@ public Collection getAddresses () } @Override - public void sync (BCSAPI api) throws BCSAPIException + public void sync (BCSAPI api, boolean utxo) throws BCSAPIException { ensureLookAhead (0); log.trace ("Sync nkeys: " + (nextSequence - firstIndex)); - api.scanUTXO (getMaster (), firstIndex, lookAhead, getCreated (), new TransactionListener () + final TransactionListener processor = new TransactionListener () { @Override public void process (Transaction t) @@ -174,7 +187,20 @@ public void process (Transaction t) } updateWithTransaction (t); } - }); + }; + if ( utxo ) + { + api.scanUTXO (getMaster (), firstIndex, lookAhead, getCreated (), processor); + } + else + { + api.scanTransactions (getMaster (), firstIndex, lookAhead, getCreated (), processor); + } + firstIndex = nextSequence; + for ( Integer id : usedKeys ) + { + firstIndex = Math.min (id, firstIndex); + } log.trace ("Sync finished with nkeys: " + (nextSequence - firstIndex)); } } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java index 681cb70e..4366e10a 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java @@ -153,7 +153,7 @@ public void sync (BCSAPI api) throws BCSAPIException, ValidationException { for ( NCExtendedKeyAccountManager account : accounts.values () ) { - account.sync (api); + account.sync (api, false); } } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java index d2dd5c9b..bf40fbdd 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java @@ -54,17 +54,31 @@ public void addKey (ECKeyPair key) } @Override - public void sync (BCSAPI api) throws BCSAPIException + public void sync (BCSAPI api, boolean utxo) throws BCSAPIException { log.trace ("Sync nkeys: " + keys.size ()); - api.scanUTXO (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () + if ( utxo ) { - @Override - public void process (Transaction t) + api.scanUTXO (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () { - updateWithTransaction (t); - } - }); + @Override + public void process (Transaction t) + { + updateWithTransaction (t); + } + }); + } + else + { + api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () + { + @Override + public void process (Transaction t) + { + updateWithTransaction (t); + } + }); + } log.trace ("Sync finished nkeys: " + keys.size ()); } } diff --git a/pom.xml b/pom.xml index ba3e88e7..e298cb63 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.2.2 + 1.2.3 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/bop-service.sh b/server/bop-service.sh index 6abd89a5..394503e1 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.2.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.3.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.2.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.3.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" diff --git a/server/pom.xml b/server/pom.xml index ceff0598..a324763a 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.2 + 1.2.3 ../ From dd8e43fd017149d2e7a4b0f95a075cb7756a0056 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 22 Aug 2013 12:30:30 +0200 Subject: [PATCH 174/299] make history sync explicit instead of a flag --- .../supernode/api/AccountManager.java | 4 +- .../api/AddressListAccountManager.java | 39 ++++++++------- .../api/ExtendedKeyAccountManager.java | 50 +++++++++++-------- .../bitsofproof/supernode/api/FileWallet.java | 2 +- .../supernode/api/KeyListAccountManager.java | 43 ++++++++-------- 5 files changed, 75 insertions(+), 63 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java index caa2a4a2..4f299b95 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java @@ -38,7 +38,9 @@ public interface UTXO } - public void sync (BCSAPI api, boolean utxo) throws BCSAPIException; + public void sync (BCSAPI api) throws BCSAPIException; + + public void syncHistory (BCSAPI api) throws BCSAPIException; public long getCreated (); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java index 2e2754d3..03ebb5be 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java @@ -55,31 +55,32 @@ public void addAddress (byte[] address) } @Override - public void sync (BCSAPI api, boolean utxo) throws BCSAPIException + public void syncHistory (BCSAPI api) throws BCSAPIException { log.trace ("Sync naddr: " + addresses.size ()); - if ( utxo ) + api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () { - api.scanUTXO (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () + @Override + public void process (Transaction t) { - @Override - public void process (Transaction t) - { - updateWithTransaction (t); - } - }); - } - else + updateWithTransaction (t); + } + }); + log.trace ("Sync finished naddr: " + addresses.size ()); + } + + @Override + public void sync (BCSAPI api) throws BCSAPIException + { + log.trace ("Sync naddr: " + addresses.size ()); + api.scanUTXO (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () { - api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () + @Override + public void process (Transaction t) { - @Override - public void process (Transaction t) - { - updateWithTransaction (t); - } - }); - } + updateWithTransaction (t); + } + }); log.trace ("Sync finished naddr: " + addresses.size ()); } } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java index 16f53ef3..d6ebdaa3 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java @@ -166,36 +166,44 @@ public Collection getAddresses () return addresses; } - @Override - public void sync (BCSAPI api, boolean utxo) throws BCSAPIException + private final TransactionListener processor = new TransactionListener () { - ensureLookAhead (0); - log.trace ("Sync nkeys: " + (nextSequence - firstIndex)); - final TransactionListener processor = new TransactionListener () + @Override + public void process (Transaction t) { - @Override - public void process (Transaction t) + for ( TransactionOutput o : t.getOutputs () ) { - for ( TransactionOutput o : t.getOutputs () ) + Integer thisKey = getKeyIDForAddress (o.getOutputAddress ()); + if ( thisKey != null ) { - Integer thisKey = getKeyIDForAddress (o.getOutputAddress ()); - if ( thisKey != null ) - { - ensureLookAhead (thisKey); - nextSequence = Math.max (nextSequence, thisKey + 1); - } + ensureLookAhead (thisKey); + nextSequence = Math.max (nextSequence, thisKey + 1); } - updateWithTransaction (t); } - }; - if ( utxo ) - { - api.scanUTXO (getMaster (), firstIndex, lookAhead, getCreated (), processor); + updateWithTransaction (t); } - else + }; + + @Override + public void syncHistory (BCSAPI api) throws BCSAPIException + { + ensureLookAhead (0); + log.trace ("Sync nkeys: " + (nextSequence - firstIndex)); + api.scanTransactions (getMaster (), firstIndex, lookAhead, getCreated (), processor); + firstIndex = nextSequence; + for ( Integer id : usedKeys ) { - api.scanTransactions (getMaster (), firstIndex, lookAhead, getCreated (), processor); + firstIndex = Math.min (id, firstIndex); } + log.trace ("Sync finished with nkeys: " + (nextSequence - firstIndex)); + } + + @Override + public void sync (BCSAPI api) throws BCSAPIException + { + ensureLookAhead (0); + log.trace ("Sync nkeys: " + (nextSequence - firstIndex)); + api.scanUTXO (getMaster (), firstIndex, lookAhead, getCreated (), processor); firstIndex = nextSequence; for ( Integer id : usedKeys ) { diff --git a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java index 4366e10a..4981c1d3 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java @@ -153,7 +153,7 @@ public void sync (BCSAPI api) throws BCSAPIException, ValidationException { for ( NCExtendedKeyAccountManager account : accounts.values () ) { - account.sync (api, false); + account.syncHistory (api); } } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java index bf40fbdd..ab1424f9 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java @@ -54,31 +54,32 @@ public void addKey (ECKeyPair key) } @Override - public void sync (BCSAPI api, boolean utxo) throws BCSAPIException + public void syncHistory (BCSAPI api) throws BCSAPIException { - log.trace ("Sync nkeys: " + keys.size ()); - if ( utxo ) + log.trace ("Sync naddr: " + keys.size ()); + api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () { - api.scanUTXO (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () + @Override + public void process (Transaction t) { - @Override - public void process (Transaction t) - { - updateWithTransaction (t); - } - }); - } - else + updateWithTransaction (t); + } + }); + log.trace ("Sync finished naddr: " + keys.size ()); + } + + @Override + public void sync (BCSAPI api) throws BCSAPIException + { + log.trace ("Sync naddr: " + keys.size ()); + api.scanUTXO (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () { - api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () + @Override + public void process (Transaction t) { - @Override - public void process (Transaction t) - { - updateWithTransaction (t); - } - }); - } - log.trace ("Sync finished nkeys: " + keys.size ()); + updateWithTransaction (t); + } + }); + log.trace ("Sync finished naddr: " + keys.size ()); } } From c713644044900401e471da02965af965b7a9af82 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 22 Aug 2013 12:35:22 +0200 Subject: [PATCH 175/299] endless recursion bug --- .../bitsofproof/supernode/api/ExtendedKeyAccountManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java index d6ebdaa3..b05e4aad 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java @@ -126,7 +126,7 @@ public Set getUsedKeys () public Integer getKeyIDForAddress (byte[] address) { - Integer id = getKeyIDForAddress (address); + Integer id = keyIDForAddress.get (address); if ( id != null ) { usedKeys.add (id); From a0df8fd562366514e1e142c2dcdbb615d59c6684 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 22 Aug 2013 13:06:01 +0200 Subject: [PATCH 176/299] why is get (Object) ! --- .../bitsofproof/supernode/api/ExtendedKeyAccountManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java index b05e4aad..5bf912f2 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java @@ -126,7 +126,7 @@ public Set getUsedKeys () public Integer getKeyIDForAddress (byte[] address) { - Integer id = keyIDForAddress.get (address); + Integer id = keyIDForAddress.get (new ByteVector (address)); if ( id != null ) { usedKeys.add (id); From 15747e6d5de3e4c669d226d7bf7e31b3ee37b26b Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 22 Aug 2013 13:15:36 +0200 Subject: [PATCH 177/299] switching back to snapshot --- api/pom.xml | 2 +- pom.xml | 2 +- server/bop-service.sh | 4 ++-- server/pom.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index cda9d380..d3e477fd 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.3 + 1.2-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index e298cb63..8a89f949 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.2.3 + 1.2-SNAPSHOT supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/bop-service.sh b/server/bop-service.sh index 394503e1..6ecd9008 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.3.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.3.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" diff --git a/server/pom.xml b/server/pom.xml index a324763a..5512f57e 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.3 + 1.2-SNAPSHOT ../ From ee30c27766086310946fa42b4ec3db703922e8fb Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Mon, 26 Aug 2013 12:08:13 +0200 Subject: [PATCH 178/299] Moving account management to its own project --- api/pom.xml | 2 +- .../supernode/api/AccountListener.java | 22 - .../supernode/api/AccountManager.java | 82 -- .../api/AddressListAccountManager.java | 86 -- .../supernode/api/BaseAccountManager.java | 743 ------------------ .../api/ExtendedKeyAccountManager.java | 214 ----- .../supernode/api/ExtendedKeySetWallet.java | 42 - .../bitsofproof/supernode/api/FileWallet.java | 211 ----- .../supernode/api/InMemoryUTXO.java | 87 -- .../supernode/api/KeyListAccountManager.java | 85 -- .../supernode/api/TransactionListener.java | 1 + .../com/bitsofproof/supernode/api/Wallet.java | 10 - api/src/main/proto/BCSAPIMessage.proto | 14 - pom.xml | 2 +- server/pom.xml | 2 +- .../bitsofproof/supernode/test/APITest.java | 265 ------- 16 files changed, 4 insertions(+), 1864 deletions(-) delete mode 100644 api/src/main/java/com/bitsofproof/supernode/api/AccountListener.java delete mode 100644 api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java delete mode 100644 api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java delete mode 100644 api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java delete mode 100644 api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java delete mode 100644 api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeySetWallet.java delete mode 100644 api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java delete mode 100644 api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java delete mode 100644 api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java delete mode 100644 api/src/main/java/com/bitsofproof/supernode/api/Wallet.java delete mode 100644 server/src/test/java/com/bitsofproof/supernode/test/APITest.java diff --git a/api/pom.xml b/api/pom.xml index d3e477fd..8ba67212 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2-SNAPSHOT + 1.2.4 ../ diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AccountListener.java b/api/src/main/java/com/bitsofproof/supernode/api/AccountListener.java deleted file mode 100644 index e70f2a22..00000000 --- a/api/src/main/java/com/bitsofproof/supernode/api/AccountListener.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2013 bits of proof zrt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.bitsofproof.supernode.api; - - -public interface AccountListener -{ - public void accountChanged (AccountManager account, Transaction t); -} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java deleted file mode 100644 index 4f299b95..00000000 --- a/api/src/main/java/com/bitsofproof/supernode/api/AccountManager.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2013 bits of proof zrt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.bitsofproof.supernode.api; - -import java.util.Collection; -import java.util.List; - -import com.bitsofproof.supernode.common.Key; -import com.bitsofproof.supernode.common.ValidationException; - -public interface AccountManager extends TransactionListener -{ - public interface UTXO - { - - public void add (TransactionOutput out); - - public Collection getUTXO (); - - public TransactionOutput get (String tx, long ix); - - public TransactionOutput remove (String tx, long ix); - - public long getTotal (); - - } - - public void sync (BCSAPI api) throws BCSAPIException; - - public void syncHistory (BCSAPI api) throws BCSAPIException; - - public long getCreated (); - - public Key getNextKey () throws ValidationException; - - public Key getKeyForAddress (byte[] address); - - public Collection getAddresses (); - - public Transaction pay (byte[] receiver, long amount, long fee) throws ValidationException; - - public Transaction pay (byte[] receiver, long amount) throws ValidationException; - - public Transaction split (long[] amounts, long fee) throws ValidationException; - - public long getBalance (); - - public long getConfirmed (); - - public long getSending (); - - public long getReceiving (); - - public long getChange (); - - public Collection getConfirmedOutputs (); - - public Collection getSendingOutputs (); - - public Collection getReceivingOutputs (); - - public Collection getChangeOutputs (); - - public List getTransactions (); - - public void addAccountListener (AccountListener listener); - - public void removeAccountListener (AccountListener listener); -} \ No newline at end of file diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java deleted file mode 100644 index 03ebb5be..00000000 --- a/api/src/main/java/com/bitsofproof/supernode/api/AddressListAccountManager.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.bitsofproof.supernode.api; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; -import com.bitsofproof.supernode.common.ByteVector; -import com.bitsofproof.supernode.common.Key; -import com.bitsofproof.supernode.common.ValidationException; - -public class AddressListAccountManager extends BaseAccountManager -{ - private static final Logger log = LoggerFactory.getLogger (AddressListAccountManager.class); - - private final Set addresses = new HashSet (); - - @Override - public Collection getAddresses () - { - List al = new ArrayList (); - for ( ByteVector b : addresses ) - { - al.add (b.toByteArray ()); - } - return al; - } - - @Override - public Key getKeyForAddress (byte[] address) - { - return null; - } - - @Override - public boolean isOwnAddress (byte[] address) - { - return addresses.contains (new ByteVector (address)); - } - - @Override - public Key getNextKey () throws ValidationException - { - return null; - } - - public void addAddress (byte[] address) - { - addresses.add (new ByteVector (address)); - } - - @Override - public void syncHistory (BCSAPI api) throws BCSAPIException - { - log.trace ("Sync naddr: " + addresses.size ()); - api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () - { - @Override - public void process (Transaction t) - { - updateWithTransaction (t); - } - }); - log.trace ("Sync finished naddr: " + addresses.size ()); - } - - @Override - public void sync (BCSAPI api) throws BCSAPIException - { - log.trace ("Sync naddr: " + addresses.size ()); - api.scanUTXO (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () - { - @Override - public void process (Transaction t) - { - updateWithTransaction (t); - } - }); - log.trace ("Sync finished naddr: " + addresses.size ()); - } -} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java deleted file mode 100644 index 992b3bac..00000000 --- a/api/src/main/java/com/bitsofproof/supernode/api/BaseAccountManager.java +++ /dev/null @@ -1,743 +0,0 @@ -package com.bitsofproof.supernode.api; - -import java.io.PrintStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.bouncycastle.util.Arrays; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.bitsofproof.supernode.common.ByteUtils; -import com.bitsofproof.supernode.common.ByteVector; -import com.bitsofproof.supernode.common.ECKeyPair; -import com.bitsofproof.supernode.common.Key; -import com.bitsofproof.supernode.common.ScriptFormat; -import com.bitsofproof.supernode.common.ScriptFormat.Opcode; -import com.bitsofproof.supernode.common.ValidationException; -import com.bitsofproof.supernode.common.WireFormat; - -public abstract class BaseAccountManager implements AccountManager -{ - private static final Logger log = LoggerFactory.getLogger (BaseAccountManager.class); - - private static final long MINIMUM_FEE = 10000; - private static final long MAXIMUM_FEE = 1000000; - - private final UTXO confirmed = createConfirmedUTXO (); - private final UTXO change = createChangeUTXO (); - private final UTXO receiving = createReceivingUTXO (); - private final UTXO sending = createSendingUTXO (); - - private long created; - - @Override - public long getCreated () - { - return created; - } - - public void setCreated (long created) - { - this.created = created; - } - - private final List accountListener = Collections.synchronizedList (new ArrayList ()); - private final Map transactions = new HashMap (); - - protected UTXO createConfirmedUTXO () - { - return new InMemoryUTXO (); - } - - protected UTXO createChangeUTXO () - { - return new InMemoryUTXO (); - } - - protected UTXO createSendingUTXO () - { - return new InMemoryUTXO (); - } - - protected UTXO createReceivingUTXO () - { - return new InMemoryUTXO (); - } - - public boolean isOwnAddress (byte[] address) - { - return getKeyForAddress (address) != null; - } - - protected static class TransactionSink - { - private final byte[] address; - private final long value; - - public TransactionSink (byte[] address, long value) - { - super (); - this.address = Arrays.clone (address); - this.value = value; - } - - public byte[] getAddress () - { - return Arrays.clone (address); - } - - public long getValue () - { - return value; - } - } - - public static long estimateFee (Transaction t) - { - WireFormat.Writer writer = new WireFormat.Writer (); - t.toWire (writer); - return Math.min (MAXIMUM_FEE, Math.max (MINIMUM_FEE, writer.toByteArray ().length / 1000 * MINIMUM_FEE)); - } - - protected Transaction createSpend (List sources, List sinks, long fee) throws ValidationException - { - if ( fee < 0 || fee > MAXIMUM_FEE ) - { - throw new ValidationException ("You unlikely want to do that"); - } - Transaction transaction = new Transaction (); - transaction.setInputs (new ArrayList ()); - transaction.setOutputs (new ArrayList ()); - - long sumOut = 0; - for ( TransactionSink s : sinks ) - { - TransactionOutput o = new TransactionOutput (); - o.setValue (s.getValue ()); - sumOut += s.getValue (); - - ScriptFormat.Writer writer = new ScriptFormat.Writer (); - writer.writeToken (new ScriptFormat.Token (Opcode.OP_DUP)); - writer.writeToken (new ScriptFormat.Token (Opcode.OP_HASH160)); - if ( s.getAddress ().length != 20 ) - { - throw new ValidationException ("Sink is not an address"); - } - writer.writeData (s.getAddress ()); - writer.writeToken (new ScriptFormat.Token (Opcode.OP_EQUALVERIFY)); - writer.writeToken (new ScriptFormat.Token (Opcode.OP_CHECKSIG)); - o.setScript (writer.toByteArray ()); - - transaction.getOutputs ().add (o); - } - - long sumInput = 0; - for ( TransactionOutput o : sources ) - { - TransactionInput i = new TransactionInput (); - i.setSourceHash (o.getTxHash ()); - i.setIx (o.getIx ()); - sumInput += o.getValue (); - - transaction.getInputs ().add (i); - } - if ( sumInput != (sumOut + fee) ) - { - throw new ValidationException ("Sum of sinks (+fee) does not match sum of sources"); - } - - int j = 0; - for ( TransactionOutput o : sources ) - { - TransactionInput i = transaction.getInputs ().get (j); - ScriptFormat.Writer sw = new ScriptFormat.Writer (); - byte[] address = o.getOutputAddress (); - if ( address == null ) - { - throw new ValidationException ("Can only spend pay to address outputs"); - } - Key key = getKeyForAddress (address); - if ( key == null ) - { - throw new ValidationException ("Have no key to spend this output"); - } - byte[] sig = key.sign (hashTransaction (transaction, j, ScriptFormat.SIGHASH_ALL, o.getScript ())); - byte[] sigPlusType = new byte[sig.length + 1]; - System.arraycopy (sig, 0, sigPlusType, 0, sig.length); - sigPlusType[sigPlusType.length - 1] = (byte) (ScriptFormat.SIGHASH_ALL & 0xff); - sw.writeData (sigPlusType); - sw.writeData (key.getPublic ()); - i.setScript (sw.toByteArray ()); - ++j; - } - - transaction.computeHash (); - return transaction; - } - - private static byte[] hashTransaction (Transaction transaction, int inr, int hashType, byte[] script) throws ValidationException - { - Transaction copy = null; - try - { - copy = transaction.clone (); - } - catch ( CloneNotSupportedException e1 ) - { - return null; - } - - // implicit SIGHASH_ALL - int i = 0; - for ( TransactionInput in : copy.getInputs () ) - { - if ( i == inr ) - { - in.setScript (script); - } - else - { - in.setScript (new byte[0]); - } - ++i; - } - - if ( (hashType & 0x1f) == ScriptFormat.SIGHASH_NONE ) - { - copy.getOutputs ().clear (); - i = 0; - for ( TransactionInput in : copy.getInputs () ) - { - if ( i != inr ) - { - in.setSequence (0); - } - ++i; - } - } - else if ( (hashType & 0x1f) == ScriptFormat.SIGHASH_SINGLE ) - { - int onr = inr; - if ( onr >= copy.getOutputs ().size () ) - { - // this is a Satoshi client bug. - // This case should throw an error but it instead retuns 1 that is not checked and interpreted as below - return ByteUtils.fromHex ("0100000000000000000000000000000000000000000000000000000000000000"); - } - for ( i = copy.getOutputs ().size () - 1; i > onr; --i ) - { - copy.getOutputs ().remove (i); - } - for ( i = 0; i < onr; ++i ) - { - copy.getOutputs ().get (i).setScript (new byte[0]); - copy.getOutputs ().get (i).setValue (-1L); - } - i = 0; - for ( TransactionInput in : copy.getInputs () ) - { - if ( i != inr ) - { - in.setSequence (0); - } - ++i; - } - } - if ( (hashType & ScriptFormat.SIGHASH_ANYONECANPAY) != 0 ) - { - List oneIn = new ArrayList (); - oneIn.add (copy.getInputs ().get (inr)); - copy.setInputs (oneIn); - } - - WireFormat.Writer writer = new WireFormat.Writer (); - copy.toWire (writer); - - byte[] txwire = writer.toByteArray (); - byte[] hash = null; - try - { - MessageDigest a = MessageDigest.getInstance ("SHA-256"); - a.update (txwire); - a.update (new byte[] { (byte) (hashType & 0xff), 0, 0, 0 }); - hash = a.digest (a.digest ()); - } - catch ( NoSuchAlgorithmException e ) - { - } - return hash; - } - - protected List getSufficientSources (long amount, long fee, String color) - { - List candidates = new ArrayList (); - candidates.addAll (confirmed.getUTXO ()); - // prefer confirmed - Collections.sort (candidates, new Comparator () - { - // prefer aggregation of UTXO - @Override - public int compare (TransactionOutput o1, TransactionOutput o2) - { - return (int) (o1.getValue () - o2.getValue ()); - } - }); - List changelist = new ArrayList (); - changelist.addAll (change.getUTXO ()); - // ... then change - Collections.sort (changelist, new Comparator () - { - // prefer aggregation of UTXO - @Override - public int compare (TransactionOutput o1, TransactionOutput o2) - { - return (int) (o1.getValue () - o2.getValue ()); - } - }); - candidates.addAll (changelist); - - List result = new ArrayList (); - long sum = 0; - for ( TransactionOutput o : candidates ) - { - if ( color == null ) - { - if ( o.getColor () == null ) - { - sum += o.getValue (); - result.add (o); - if ( sum >= (amount + fee) ) - { - return result; - } - } - } - else - { - if ( o.getColor ().equals (color) ) - { - sum += o.getValue (); - result.add (o); - if ( sum >= amount ) - { - if ( fee > 0 ) - { - result.addAll (getSufficientSources (0, fee, null)); - } - return result; - } - } - } - } - return null; - } - - @Override - public Transaction pay (byte[] receiver, long amount, long fee) throws ValidationException - { - synchronized ( confirmed ) - { - log.trace ("pay to " + ByteUtils.toHex (receiver) + " " + amount + " + " + fee); - List sources = getSufficientSources (amount, fee, null); - if ( sources == null ) - { - throw new ValidationException ("Insufficient funds to pay " + (amount + fee)); - } - List sinks = new ArrayList (); - - long in = 0; - for ( TransactionOutput o : sources ) - { - log.trace ("using input " + o.getTxHash () + "[" + o.getIx () + "] " + o.getValue ()); - in += o.getValue (); - } - TransactionSink target = new TransactionSink (receiver, amount); - if ( (in - amount - fee) > 0 ) - { - TransactionSink change = new TransactionSink (getNextKey ().getAddress (), in - amount - fee); - log.trace ("change to " + ByteUtils.toHex (change.getAddress ()) + " " + change.getValue ()); - if ( new SecureRandom ().nextBoolean () ) - { - sinks.add (target); - sinks.add (change); - } - else - { - sinks.add (change); - sinks.add (target); - } - } - else - { - sinks.add (target); - } - return createSpend (sources, sinks, fee); - } - } - - @Override - public Transaction pay (byte[] receiver, long amount) throws ValidationException - { - long fee = MINIMUM_FEE; - long estimate = 0; - Transaction t = null; - - do - { - fee = Math.max (fee, estimate); - t = pay (receiver, amount, fee); - estimate = estimateFee (t); - if ( fee < estimate ) - { - log.trace ("The transaction requires more network fees. Reassembling."); - } - } while ( fee < estimate ); - - return t; - } - - @Override - public Transaction split (long[] amounts, long fee) throws ValidationException - { - synchronized ( confirmed ) - { - List sinks = new ArrayList (); - - long amount = 0; - for ( long a : amounts ) - { - amount += a; - } - List sources = getSufficientSources (amount, fee, null); - if ( sources == null ) - { - throw new ValidationException ("Insufficient funds to pay " + (amount + fee)); - } - long in = 0; - for ( TransactionOutput o : sources ) - { - in += o.getValue (); - } - for ( long a : amounts ) - { - TransactionSink target = new TransactionSink (getNextKey ().getAddress (), a); - sinks.add (target); - } - if ( (in - amount - fee) > 0 ) - { - TransactionSink change = new TransactionSink (getNextKey ().getAddress (), in - amount - fee); - sinks.add (change); - } - Collections.shuffle (sinks); - return createSpend (sources, sinks, fee); - } - } - - public boolean updateWithTransaction (Transaction t) - { - synchronized ( confirmed ) - { - boolean modified = false; - if ( !t.isDoubleSpend () ) - { - TransactionOutput spend = null; - for ( TransactionInput i : t.getInputs () ) - { - spend = confirmed.get (i.getSourceHash (), i.getIx ()); - if ( spend != null ) - { - confirmed.remove (i.getSourceHash (), i.getIx ()); - log.trace ("Spend settled output " + i.getSourceHash () + " [" + i.getIx () + "] " + spend.getValue ()); - } - else - { - spend = change.get (i.getSourceHash (), i.getIx ()); - if ( spend != null ) - { - change.remove (i.getSourceHash (), i.getIx ()); - log.trace ("Spend change output " + i.getSourceHash () + " [" + i.getIx () + "] " + spend.getValue ()); - } - else - { - spend = receiving.get (i.getSourceHash (), i.getIx ()); - if ( spend != null ) - { - receiving.remove (i.getSourceHash (), i.getIx ()); - log.trace ("Spend receiving output " + i.getSourceHash () + " [" + i.getIx () + "] " + spend.getValue ()); - } - } - } - } - modified = spend != null; - for ( TransactionOutput o : t.getOutputs () ) - { - confirmed.remove (o.getTxHash (), o.getIx ()); - change.remove (o.getTxHash (), o.getIx ()); - receiving.remove (o.getTxHash (), o.getIx ()); - sending.remove (o.getTxHash (), o.getIx ()); - - if ( isOwnAddress (o.getOutputAddress ()) ) - { - modified = true; - if ( t.getBlockHash () != null ) - { - confirmed.add (o); - log.trace ("Settled " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + o.getValue ()); - } - else - { - if ( spend != null ) - { - change.add (o); - log.trace ("Change " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " - + o.getValue ()); - } - else - { - receiving.add (o); - log.trace ("Receiving " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " - + o.getValue ()); - } - } - } - else - { - if ( t.getBlockHash () == null && spend != null ) - { - modified = true; - sending.add (o); - log.trace ("Sending " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + o.getValue ()); - } - } - } - if ( modified ) - { - transactions.put (t.getHash (), t); - } - } - else - { - for ( long ix = 0; ix < t.getOutputs ().size (); ++ix ) - { - TransactionOutput out = null; - out = confirmed.remove (t.getHash (), ix); - if ( out == null ) - { - out = change.remove (t.getHash (), ix); - } - if ( out == null ) - { - out = receiving.remove (t.getHash (), ix); - } - if ( out == null ) - { - out = sending.remove (t.getHash (), ix); - } - if ( out != null ) - { - log.trace ("Remove DS " + out.getTxHash () + " [" + out.getIx () + "] (" + ByteUtils.toHex (out.getOutputAddress ()) + ")" - + out.getValue ()); - } - modified |= out != null; - } - transactions.remove (t.getHash ()); - } - return modified; - } - } - - @Override - public long getBalance () - { - synchronized ( confirmed ) - { - return confirmed.getTotal () + change.getTotal () + receiving.getTotal (); - } - } - - @Override - public long getConfirmed () - { - synchronized ( confirmed ) - { - return confirmed.getTotal (); - } - } - - @Override - public long getSending () - { - synchronized ( confirmed ) - { - return sending.getTotal (); - } - } - - @Override - public long getReceiving () - { - synchronized ( confirmed ) - { - return receiving.getTotal (); - } - } - - @Override - public long getChange () - { - synchronized ( confirmed ) - { - return change.getTotal (); - } - } - - @Override - public Collection getConfirmedOutputs () - { - return confirmed.getUTXO (); - } - - @Override - public Collection getSendingOutputs () - { - return sending.getUTXO (); - } - - @Override - public Collection getReceivingOutputs () - { - return receiving.getUTXO (); - } - - @Override - public Collection getChangeOutputs () - { - return change.getUTXO (); - } - - @Override - public void addAccountListener (AccountListener listener) - { - accountListener.add (listener); - } - - @Override - public void removeAccountListener (AccountListener listener) - { - accountListener.remove (listener); - } - - protected void notifyListener (Transaction t) - { - synchronized ( accountListener ) - { - for ( AccountListener l : accountListener ) - { - try - { - l.accountChanged (this, t); - } - catch ( Exception e ) - { - } - } - } - } - - @Override - public void process (Transaction t) - { - if ( updateWithTransaction (t) ) - { - notifyListener (t); - } - } - - @Override - public List getTransactions () - { - List tl = new ArrayList (); - tl.addAll (transactions.values ()); - Collections.sort (tl, new Comparator () - { - @Override - public int compare (Transaction a, Transaction b) - { - for ( TransactionInput i : b.getInputs () ) - { - if ( i.getSourceHash ().equals (a.getHash ()) ) - { - return -1; - } - } - for ( TransactionInput i : a.getInputs () ) - { - if ( i.getSourceHash ().equals (b.getHash ()) ) - { - return 1; - } - } - return 0; - } - }); - - return tl; - } - - public void dumpOutputs (PrintStream print) - { - print.println ("Confirmed:"); - dumpUTXO (print, confirmed); - print.println ("Receiving:"); - dumpUTXO (print, receiving); - print.println ("Change:"); - dumpUTXO (print, change); - print.println ("Sending:"); - dumpUTXO (print, sending); - } - - public void dumpKeys (PrintStream print) - { - Set addresses = new HashSet (); - for ( TransactionOutput o : confirmed.getUTXO () ) - { - addresses.add (new ByteVector (o.getOutputAddress ())); - } - for ( TransactionOutput o : change.getUTXO () ) - { - addresses.add (new ByteVector (o.getOutputAddress ())); - } - for ( TransactionOutput o : receiving.getUTXO () ) - { - addresses.add (new ByteVector (o.getOutputAddress ())); - } - for ( ByteVector b : addresses ) - { - Key k = getKeyForAddress (b.toByteArray ()); - if ( k != null ) - { - print.println (ECKeyPair.serializeWIF (k) + " (" + ByteUtils.toHex (b.toByteArray ()) + ")"); - } - } - } - - private void dumpUTXO (PrintStream print, UTXO set) - { - for ( TransactionOutput o : set.getUTXO () ) - { - print.println (o.getTxHash () + "[" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + o.getValue ()); - } - print.println (); - } -} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java deleted file mode 100644 index 5bf912f2..00000000 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeyAccountManager.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright 2013 bits of proof zrt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.bitsofproof.supernode.api; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.bitsofproof.supernode.common.ByteVector; -import com.bitsofproof.supernode.common.Key; -import com.bitsofproof.supernode.common.ValidationException; - -public class ExtendedKeyAccountManager extends BaseAccountManager implements TransactionListener -{ - private static final Logger log = LoggerFactory.getLogger (ExtendedKeyAccountManager.class); - - private final Set usedKeys = new HashSet (); - private final Map keyIDForAddress = new HashMap (); - private ExtendedKey master; - private int nextSequence; - private int lookAhead = 100; - private int firstIndex; - - public ExtendedKey getMaster () - { - return master; - } - - public void setMaster (ExtendedKey master) - { - this.master = master; - } - - public int getFirstIndex () - { - return firstIndex; - } - - public void setLookAhead (int lookAhead) - { - this.lookAhead = lookAhead; - } - - public void setFirstIndex (int firstIndex) - { - this.firstIndex = firstIndex; - } - - public int getLookAhead () - { - return lookAhead; - } - - @Override - protected void notifyListener (Transaction t) - { - for ( TransactionOutput o : t.getOutputs () ) - { - if ( isOwnAddress (o.getOutputAddress ()) ) - { - int keyId = keyIDForAddress.get (new ByteVector (o.getOutputAddress ())); - ensureLookAhead (keyId); - } - } - super.notifyListener (t); - } - - private void ensureLookAhead (int from) - { - while ( keyIDForAddress.size () < (from + lookAhead - firstIndex) ) - { - Key key = null; - try - { - key = master.getKey (keyIDForAddress.size () + firstIndex); - } - catch ( ValidationException e ) - { - } - keyIDForAddress.put (new ByteVector (key.getAddress ()), keyIDForAddress.size () + firstIndex); - } - } - - public Key getKey (int i) throws ValidationException - { - ensureLookAhead (i); - return master.getKey (i); - } - - public void setNextKey (int i) - { - nextSequence = i; - ensureLookAhead (nextSequence); - } - - @Override - public Key getNextKey () throws ValidationException - { - return getKey (nextSequence++); - } - - public Set getUsedKeys () - { - return usedKeys; - } - - public Integer getKeyIDForAddress (byte[] address) - { - Integer id = keyIDForAddress.get (new ByteVector (address)); - if ( id != null ) - { - usedKeys.add (id); - } - return id; - } - - @Override - public Key getKeyForAddress (byte[] address) - { - Integer keyId = getKeyIDForAddress (address); - if ( keyId == null ) - { - return null; - } - try - { - return getKey (keyId); - } - catch ( ValidationException e ) - { - return null; - } - } - - @Override - public Collection getAddresses () - { - List addresses = new ArrayList (); - for ( ByteVector v : keyIDForAddress.keySet () ) - { - if ( keyIDForAddress.get (v).intValue () < nextSequence ) - { - addresses.add (v.toByteArray ()); - } - } - return addresses; - } - - private final TransactionListener processor = new TransactionListener () - { - @Override - public void process (Transaction t) - { - for ( TransactionOutput o : t.getOutputs () ) - { - Integer thisKey = getKeyIDForAddress (o.getOutputAddress ()); - if ( thisKey != null ) - { - ensureLookAhead (thisKey); - nextSequence = Math.max (nextSequence, thisKey + 1); - } - } - updateWithTransaction (t); - } - }; - - @Override - public void syncHistory (BCSAPI api) throws BCSAPIException - { - ensureLookAhead (0); - log.trace ("Sync nkeys: " + (nextSequence - firstIndex)); - api.scanTransactions (getMaster (), firstIndex, lookAhead, getCreated (), processor); - firstIndex = nextSequence; - for ( Integer id : usedKeys ) - { - firstIndex = Math.min (id, firstIndex); - } - log.trace ("Sync finished with nkeys: " + (nextSequence - firstIndex)); - } - - @Override - public void sync (BCSAPI api) throws BCSAPIException - { - ensureLookAhead (0); - log.trace ("Sync nkeys: " + (nextSequence - firstIndex)); - api.scanUTXO (getMaster (), firstIndex, lookAhead, getCreated (), processor); - firstIndex = nextSequence; - for ( Integer id : usedKeys ) - { - firstIndex = Math.min (id, firstIndex); - } - log.trace ("Sync finished with nkeys: " + (nextSequence - firstIndex)); - } -} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeySetWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeySetWallet.java deleted file mode 100644 index 3993513f..00000000 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKeySetWallet.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.bitsofproof.supernode.api; - -import java.util.HashMap; -import java.util.Map; - -import com.bitsofproof.supernode.common.ValidationException; - -public class ExtendedKeySetWallet implements Wallet -{ - private final Map accountManager = new HashMap<> (); - - public void addKey (String name, ExtendedKey key, long created) throws ValidationException - { - if ( accountManager.containsKey (name) ) - { - throw new ValidationException (name + " account manager exsists"); - } - ExtendedKeyAccountManager manager; - accountManager.put (name, manager = new ExtendedKeyAccountManager ()); - manager.setMaster (key); - } - - @Override - public AccountManager getAccountManager (String name) - { - return accountManager.get (name); - } - - @Override - public AccountManager createAccountManager (String name) throws ValidationException - { - if ( accountManager.containsKey (name) ) - { - throw new ValidationException (name + " account manager exsists"); - } - ExtendedKeyAccountManager manager; - accountManager.put (name, manager = new ExtendedKeyAccountManager ()); - manager.setMaster (ExtendedKey.createNew ()); - return manager; - } - -} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java b/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java deleted file mode 100644 index 4981c1d3..00000000 --- a/api/src/main/java/com/bitsofproof/supernode/api/FileWallet.java +++ /dev/null @@ -1,211 +0,0 @@ -package com.bitsofproof.supernode.api; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.bitsofproof.supernode.common.ValidationException; -import com.google.protobuf.ByteString; - -public class FileWallet implements Wallet -{ - private static final SecureRandom random = new SecureRandom (); - private transient ExtendedKey master; - private byte[] encrypted; - private byte[] signature; - private String fileName; - private long since; - - private static class NCExtendedKeyAccountManager extends ExtendedKeyAccountManager - { - private final String name; - - public NCExtendedKeyAccountManager (String name, long created) - { - super (); - this.name = name; - setCreated (created); - } - - public String getName () - { - return name; - } - } - - private final Map accounts = new HashMap (); - - public FileWallet (String fileName) - { - this.fileName = fileName; - this.since = System.currentTimeMillis (); - } - - public boolean exists () - { - return new File (fileName).exists (); - } - - public void init (String passphrase) - { - master = null; - encrypted = new byte[32]; - random.nextBytes (encrypted); - try - { - signature = ExtendedKey.createFromPassphrase (passphrase, encrypted).getMaster ().sign (encrypted); - } - catch ( ValidationException e ) - { - } - } - - public void init (String passphrase, ExtendedKey master, boolean production, long since) - { - this.master = master; - this.since = since; - try - { - encrypted = master.encrypt (passphrase, production); - signature = ExtendedKey.createFromPassphrase (passphrase, encrypted).getMaster ().sign (encrypted); - } - catch ( ValidationException e ) - { - } - } - - public ExtendedKey getMaster () - { - return master; - } - - public void unlock (String passphrase) throws ValidationException - { - master = ExtendedKey.createFromPassphrase (passphrase, encrypted); - if ( !master.getMaster ().verify (encrypted, signature) ) - { - throw new ValidationException ("incorrect passphrase"); - } - for ( NCExtendedKeyAccountManager account : accounts.values () ) - { - account.setMaster (master.getChild (account.getMaster ().getSequence ())); - } - } - - public void lock () - { - master = null; - for ( ExtendedKeyAccountManager account : accounts.values () ) - { - account.setMaster (account.getMaster ().getReadOnly ()); - } - } - - public List getAccountNames () - { - List names = new ArrayList (); - for ( NCExtendedKeyAccountManager account : accounts.values () ) - { - names.add (account.getName ()); - } - return names; - } - - public void setFileName (String fileName) - { - this.fileName = fileName; - } - - public static FileWallet read (String fileName) throws IOException, ValidationException - { - FileWallet wallet = new FileWallet (fileName); - File f = new File (fileName); - InputStream in = new FileInputStream (f); - try - { - BCSAPIMessage.Wallet walletMessage = BCSAPIMessage.Wallet.parseFrom (in); - wallet.encrypted = walletMessage.getEncryptedSeed ().toByteArray (); - wallet.signature = walletMessage.getSignature ().toByteArray (); - for ( BCSAPIMessage.Wallet.Account account : walletMessage.getAccountsList () ) - { - ExtendedKey pub = ExtendedKey.parse (account.getPublicKey ()); - NCExtendedKeyAccountManager am = new NCExtendedKeyAccountManager (account.getName (), account.getCreated () * 1000); - am.setFirstIndex (account.getFirstIndex ()); - wallet.accounts.put (account.getName (), am); - am.setMaster (pub); - } - } - finally - { - in.close (); - } - return wallet; - } - - public void sync (BCSAPI api) throws BCSAPIException, ValidationException - { - for ( NCExtendedKeyAccountManager account : accounts.values () ) - { - account.syncHistory (api); - } - } - - @Override - public synchronized AccountManager getAccountManager (String name) - { - return accounts.get (name); - } - - @Override - public synchronized AccountManager createAccountManager (String name) throws ValidationException - { - if ( accounts.containsKey (name) ) - { - return accounts.get (name); - } - else - { - if ( master == null ) - { - throw new ValidationException ("The wallet is locked"); - } - NCExtendedKeyAccountManager account = new NCExtendedKeyAccountManager (name, Math.min (System.currentTimeMillis (), since)); - account.setMaster (master.getChild (accounts.size () | 0x80000000)); - accounts.put (name, account); - return account; - } - } - - public void persist () throws IOException - { - FileOutputStream out = new FileOutputStream (fileName); - try - { - BCSAPIMessage.Wallet.Builder builder = BCSAPIMessage.Wallet.newBuilder (); - builder.setBcsapiversion (1); - builder.setEncryptedSeed (ByteString.copyFrom (encrypted)); - builder.setSignature (ByteString.copyFrom (signature)); - for ( NCExtendedKeyAccountManager am : accounts.values () ) - { - BCSAPIMessage.Wallet.Account.Builder ab = BCSAPIMessage.Wallet.Account.newBuilder (); - ab.setName (am.getName ()); - ab.setCreated (am.getCreated () / 1000); - ab.setFirstIndex (am.getFirstIndex ()); - ab.setPublicKey (am.getMaster ().getReadOnly ().serialize (true)); - builder.addAccounts (ab.build ()); - } - builder.build ().writeTo (out); - } - finally - { - out.close (); - } - } -} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java b/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java deleted file mode 100644 index b29271aa..00000000 --- a/api/src/main/java/com/bitsofproof/supernode/api/InMemoryUTXO.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2013 bits of proof zrt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.bitsofproof.supernode.api; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -public class InMemoryUTXO implements AccountManager.UTXO -{ - private static class TxOutKey - { - String hash; - long ix; - - public TxOutKey (String hash, long ix) - { - this.hash = hash; - this.ix = ix; - } - - @Override - public boolean equals (Object obj) - { - TxOutKey o = (TxOutKey) obj; - return o.hash.equals (hash) && o.ix == ix; - } - - @Override - public int hashCode () - { - return (int) (hash.hashCode () + ix); - } - - } - - private final Map utxo = new HashMap (); - - @Override - public void add (TransactionOutput out) - { - utxo.put (new TxOutKey (out.getTxHash (), out.getIx ()), out); - } - - @Override - public Collection getUTXO () - { - return Collections.unmodifiableCollection (utxo.values ()); - } - - @Override - public TransactionOutput get (String tx, long ix) - { - return utxo.get (new TxOutKey (tx, ix)); - } - - @Override - public TransactionOutput remove (String tx, long ix) - { - return utxo.remove (new TxOutKey (tx, ix)); - } - - @Override - public long getTotal () - { - long s = 0; - for ( TransactionOutput o : utxo.values () ) - { - s += o.getValue (); - } - return s; - } -} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java deleted file mode 100644 index ab1424f9..00000000 --- a/api/src/main/java/com/bitsofproof/supernode/api/KeyListAccountManager.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.bitsofproof.supernode.api; - -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; -import com.bitsofproof.supernode.common.ByteVector; -import com.bitsofproof.supernode.common.ECKeyPair; -import com.bitsofproof.supernode.common.Key; -import com.bitsofproof.supernode.common.ValidationException; - -public class KeyListAccountManager extends BaseAccountManager -{ - private static final Logger log = LoggerFactory.getLogger (KeyListAccountManager.class); - - private final Map keyByAddress = new HashMap (); - private final List keys = new ArrayList (); - private static final SecureRandom rnd = new SecureRandom (); - - @Override - public Collection getAddresses () - { - List a = new ArrayList (); - for ( ByteVector b : keyByAddress.keySet () ) - { - a.add (b.toByteArray ()); - } - return a; - } - - @Override - public Key getKeyForAddress (byte[] address) - { - return keyByAddress.get (new ByteVector (address)); - } - - @Override - public Key getNextKey () throws ValidationException - { - return keys.get (rnd.nextInt (keys.size ())); - } - - public void addKey (ECKeyPair key) - { - keys.add (key); - keyByAddress.put (new ByteVector (key.getAddress ()), key); - } - - @Override - public void syncHistory (BCSAPI api) throws BCSAPIException - { - log.trace ("Sync naddr: " + keys.size ()); - api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () - { - @Override - public void process (Transaction t) - { - updateWithTransaction (t); - } - }); - log.trace ("Sync finished naddr: " + keys.size ()); - } - - @Override - public void sync (BCSAPI api) throws BCSAPIException - { - log.trace ("Sync naddr: " + keys.size ()); - api.scanUTXO (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () - { - @Override - public void process (Transaction t) - { - updateWithTransaction (t); - } - }); - log.trace ("Sync finished naddr: " + keys.size ()); - } -} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/TransactionListener.java b/api/src/main/java/com/bitsofproof/supernode/api/TransactionListener.java index 7e490bee..62b0f605 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/TransactionListener.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/TransactionListener.java @@ -15,6 +15,7 @@ */ package com.bitsofproof.supernode.api; + public interface TransactionListener { public void process (Transaction t); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java b/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java deleted file mode 100644 index 0c4d9513..00000000 --- a/api/src/main/java/com/bitsofproof/supernode/api/Wallet.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.bitsofproof.supernode.api; - -import com.bitsofproof.supernode.common.ValidationException; - -public interface Wallet -{ - public AccountManager getAccountManager (String name); - - public AccountManager createAccountManager (String name) throws ValidationException; -} \ No newline at end of file diff --git a/api/src/main/proto/BCSAPIMessage.proto b/api/src/main/proto/BCSAPIMessage.proto index bfa58dce..0d65fa37 100644 --- a/api/src/main/proto/BCSAPIMessage.proto +++ b/api/src/main/proto/BCSAPIMessage.proto @@ -102,17 +102,3 @@ message AccountRequest optional uint64 after = 4; optional uint32 firstIndex = 5; } - -message Wallet { - required uint32 bcsapiversion = 1; - required bytes encryptedSeed = 2; - required bytes signature = 3; - message Account - { - required string publicKey = 1; - required uint64 created = 2; - required string name = 3; - optional uint32 firstIndex = 4; - } - repeated Account accounts = 4; -} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 8a89f949..2d93de18 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.2-SNAPSHOT + 1.2.4 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/pom.xml b/server/pom.xml index 5512f57e..d5e635cf 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2-SNAPSHOT + 1.2.4 ../ diff --git a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java deleted file mode 100644 index 40d0153f..00000000 --- a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 2013 bits of proof zrt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.bitsofproof.supernode.test; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.io.IOException; -import java.math.BigInteger; -import java.security.Security; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import com.bitsofproof.supernode.api.AccountListener; -import com.bitsofproof.supernode.api.AccountManager; -import com.bitsofproof.supernode.api.BCSAPI; -import com.bitsofproof.supernode.api.BCSAPIException; -import com.bitsofproof.supernode.api.Block; -import com.bitsofproof.supernode.api.ExtendedKeySetWallet; -import com.bitsofproof.supernode.api.Transaction; -import com.bitsofproof.supernode.api.TransactionListener; -import com.bitsofproof.supernode.api.TrunkListener; -import com.bitsofproof.supernode.api.Wallet; -import com.bitsofproof.supernode.common.Hash; -import com.bitsofproof.supernode.common.ValidationException; -import com.bitsofproof.supernode.core.BlockStore; -import com.bitsofproof.supernode.core.Chain; -import com.bitsofproof.supernode.core.Difficulty; - -@RunWith (SpringJUnit4ClassRunner.class) -@ContextConfiguration (locations = { "/context/memory-store.xml", "/context/EmbeddedBCSAPI.xml" }) -public class APITest -{ - @Autowired - BlockStore store; - - @Autowired - Chain chain; - - @Autowired - BCSAPI api; - - private static final long COIN = 100000000L; - private static final long FEE = COIN / 1000L; - - private static class AccountMonitor implements AccountListener - { - private final Semaphore ready = new Semaphore (0); - private final AccountManager account; - - public AccountMonitor (AccountManager account) - { - this.account = account; - } - - @Override - public void accountChanged (AccountManager account, Transaction t) - { - assertTrue (this.account == account); - ready.release (); - } - - public void expectUpdates (int n) - { - try - { - assertTrue (ready.tryAcquire (n, 1, TimeUnit.SECONDS)); - assertFalse (ready.tryAcquire ()); - } - catch ( InterruptedException e ) - { - } - } - } - - public static class ValidationMonitor implements TrunkListener, TransactionListener - { - private final Semaphore blockValidated = new Semaphore (0); - private final Semaphore transactionValidated = new Semaphore (0); - private String last; - - @Override - public void trunkUpdate (List removed, List added) - { - if ( added != null ) - { - for ( Block b : added ) - { - b.computeHash (); - last = b.getHash (); - blockValidated.release (); - } - } - } - - public String getLast () - { - return last; - } - - public void expectBlockValidations (int n) - { - try - { - assertTrue (blockValidated.tryAcquire (n, 1, TimeUnit.SECONDS)); - assertFalse (blockValidated.tryAcquire ()); - } - catch ( InterruptedException e ) - { - } - } - - public void expectTransactionValidations (int n) - { - try - { - assertTrue (transactionValidated.tryAcquire (n, 1, TimeUnit.SECONDS)); - assertFalse (transactionValidated.tryAcquire ()); - } - catch ( InterruptedException e ) - { - } - } - - @Override - public void process (Transaction t) - { - transactionValidated.release (); - } - } - - @BeforeClass - public static void provider () - { - Security.addProvider (new BouncyCastleProvider ()); - } - - @Test - public void test () throws BCSAPIException, ValidationException, IOException - { - Map blocks = new HashMap (); - - Wallet wallet = new ExtendedKeySetWallet (); - AccountManager alice = wallet.createAccountManager ("Alice"); - AccountManager bob = wallet.createAccountManager ("Bob"); - - AccountMonitor bobMonitor = new AccountMonitor (bob); - AccountMonitor aliceMonitor = new AccountMonitor (alice); - ValidationMonitor validationMonitor = new ValidationMonitor (); - - store.resetStore (chain); - store.cache (chain, 0); - - api.registerTransactionListener (alice); - api.registerTransactionListener (bob); - - alice.addAccountListener (aliceMonitor); - bob.addAccountListener (bobMonitor); - - api.registerTrunkListener (validationMonitor); - api.registerTransactionListener (validationMonitor); - - // check genesis - String genesisHash = chain.getGenesis ().getHash (); - assertTrue (api.getBlock (genesisHash).getHash ().equals (genesisHash)); - - // send 1 block - Block block = createBlock (chain.getGenesis ().getHash (), Transaction.createCoinbase (alice.getNextKey (), 50 * COIN, 1)); - mineBlock (block); - blocks.put (1, block); - - api.sendBlock (block); - - validationMonitor.expectBlockValidations (1); - assertTrue (validationMonitor.getLast ().equals (block.getHash ())); - validationMonitor.expectTransactionValidations (1); - aliceMonitor.expectUpdates (1); - - // send 10 blocks - String hash = blocks.get (1).getHash (); - for ( int i = 0; i < 10; ++i ) - { - block = createBlock (hash, Transaction.createCoinbase (alice.getNextKey (), 5000000000L, i + 2)); - block.setCreateTime (block.getCreateTime () + (i + 1) * 1000); // avoid clash of timestamp with median - mineBlock (block); - blocks.put (i + 2, block); - hash = block.getHash (); - api.sendBlock (block); - } - validationMonitor.expectBlockValidations (10); - assertTrue (validationMonitor.getLast ().equals (hash)); - aliceMonitor.expectUpdates (10); - validationMonitor.expectTransactionValidations (10); - - // spend some - long aliceStartingBalance = alice.getBalance (); - Transaction spend = alice.pay (bob.getNextKey ().getAddress (), 50 * COIN, FEE); - api.sendTransaction (spend); - aliceMonitor.expectUpdates (1); - bobMonitor.expectUpdates (1); - validationMonitor.expectTransactionValidations (1); - assertTrue (bob.getBalance () == 50 * COIN); - assertTrue (alice.getBalance () == aliceStartingBalance - bob.getBalance () - FEE); - - // split - aliceStartingBalance = alice.getBalance (); - spend = alice.split (new long[] { 1 * COIN, 2 * COIN }, FEE); - api.sendTransaction (spend); - aliceMonitor.expectUpdates (1); - validationMonitor.expectTransactionValidations (1); - assertTrue (alice.getBalance () == aliceStartingBalance - FEE); - } - - private Block createBlock (String previous, Transaction coinbase) - { - Block block = new Block (); - block.setCreateTime (System.currentTimeMillis () / 1000); - block.setDifficultyTarget (chain.getGenesis ().getDifficultyTarget ()); - block.setPreviousHash (previous); - block.setVersion (2); - block.setNonce (0); - block.setTransactions (new ArrayList ()); - block.getTransactions ().add (coinbase); - return block; - } - - private void mineBlock (Block b) - { - for ( int nonce = Integer.MIN_VALUE; nonce <= Integer.MAX_VALUE; ++nonce ) - { - b.setNonce (nonce); - b.computeHash (); - BigInteger hashAsInteger = new Hash (b.getHash ()).toBigInteger (); - if ( hashAsInteger.compareTo (Difficulty.getTarget (b.getDifficultyTarget ())) <= 0 ) - { - break; - } - } - } -} From 267cf954a56451ecae6a6e040a043b4831dfcc05 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Mon, 26 Aug 2013 12:14:13 +0200 Subject: [PATCH 179/299] switch back to snapshot --- api/pom.xml | 2 +- pom.xml | 2 +- server/pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 8ba67212..d3e477fd 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.4 + 1.2-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index 2d93de18..8a89f949 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.2.4 + 1.2-SNAPSHOT supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/pom.xml b/server/pom.xml index d5e635cf..5512f57e 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.4 + 1.2-SNAPSHOT ../ From 6db5cb01a4092c7b7ddce3a391afa0bb562d8864 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 29 Aug 2013 18:38:53 +0200 Subject: [PATCH 180/299] added catchUp call to BCSAPI --- .../com/bitsofproof/supernode/api/BCSAPI.java | 9 +++ .../supernode/api/JMSServerConnector.java | 57 +++++++++++++++++++ .../supernode/core/BlockStore.java | 2 + .../supernode/core/CachedBlockStore.java | 33 +++++++++++ .../supernode/core/ImplementBCSAPI.java | 47 +++++++++++++++ 5 files changed, 148 insertions(+) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java index cf09185c..0bd03554 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java @@ -163,4 +163,13 @@ public interface BCSAPI * @throws BCSAPIException */ public void scanUTXO (ExtendedKey master, int firstIndex, int lookAhead, long after, TransactionListener listener) throws BCSAPIException; + + /** + * Generate a trunk update to cach up from a last seen block + * + * @param hash + * @param listener + * @throws BCSAPIException + */ + public void catchUp (String hash, TrunkListener listener) throws BCSAPIException; } diff --git a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java index 01b6c187..5bd0ff79 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java @@ -494,6 +494,63 @@ public void onMessage (Message message) } } + @Override + public void catchUp (String hash, final TrunkListener listener) throws BCSAPIException + { + log.trace ("catchUp from " + hash); + BytesMessage m; + Session session = null; + try + { + session = connection.createSession (false, Session.AUTO_ACKNOWLEDGE); + MessageProducer transactionRequestProducer = session.createProducer (session.createQueue ("catchUpRequest")); + + m = session.createBytesMessage (); + BCSAPIMessage.Hash.Builder builder = BCSAPIMessage.Hash.newBuilder (); + builder.setBcsapiversion (1); + builder.addHash (ByteString.copyFrom (new Hash (hash).toByteArray ())); + m.writeBytes (builder.build ().toByteArray ()); + byte[] response = synchronousRequest (session, transactionRequestProducer, m); + if ( response != null ) + { + TrunkUpdateMessage tu = TrunkUpdateMessage.fromProtobuf (BCSAPIMessage.TrunkUpdate.parseFrom (response)); + if ( tu.getRemoved () != null ) + { + for ( Block b : tu.getRemoved () ) + { + b.computeHash (); + } + } + if ( tu.getAdded () != null ) + { + for ( Block b : tu.getAdded () ) + { + b.computeHash (); + } + } + listener.trunkUpdate (tu.getRemoved (), tu.getAdded ()); + } + } + catch ( JMSException e ) + { + throw new BCSAPIException (e); + } + catch ( InvalidProtocolBufferException e ) + { + throw new BCSAPIException (e); + } + finally + { + try + { + session.close (); + } + catch ( JMSException e ) + { + } + } + } + @Override public void registerTransactionListener (final TransactionListener listener) throws BCSAPIException { diff --git a/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java index 9dddfe51..cdcd97e5 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java @@ -81,4 +81,6 @@ public void filterTransactions (boolean utxo, Set match, UpdateMode public void filterTransactions (boolean utxo, Set match, ExtendedKey ek, int firstIndex, int lookAhead, long after, TransactionProcessor processor) throws ValidationException; + + public void catchUp (String hash, List added, List removed) throws ValidationException; } \ No newline at end of file diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 7ece89cd..c6a6f8b1 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -368,6 +368,39 @@ public void cache (Chain chain, int size) throws ValidationException } } + @Override + public void catchUp (String hash, List added, List removed) throws ValidationException + { + try + { + String trunk = hash; + while ( !isOnTrunk (trunk) ) + { + CachedBlock b = cachedBlocks.get (trunk); + removed.add (retrieveBlock (b)); + b = b.previous; + trunk = b.hash; + } + CachedBlock q = currentHead.getLast (); + CachedBlock p = q.previous; + while ( p != null ) + { + if ( q.hash.equals (trunk) ) + { + break; + } + added.add (retrieveBlock (q)); + p = q.previous; + } + Collections.reverse (added); + } + finally + { + lock.readLock ().unlock (); + } + + } + private void updateBlockChainCache () { List appendList = new ArrayList (); diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index 57a73859..6fe77226 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -155,6 +155,7 @@ public void init () addMatchScanListener (); addPingListener (); addAccountScanListener (); + addCatchUpListener (); } catch ( JMSException e ) { @@ -389,6 +390,52 @@ public void process (Tx tx) }); } + private void addCatchUpListener () throws JMSException + { + addQueueListener ("catchUpRequest", new SessionMessageListener () + { + @Override + public void onMessage (Message arg0) + { + BytesMessage o = (BytesMessage) arg0; + try + { + byte[] body = new byte[(int) o.getBodyLength ()]; + o.readBytes (body); + String hash = new Hash (BCSAPIMessage.Hash.parseFrom (body).getHash (0).toByteArray ()).toString (); + log.debug ("catchUpRequest for " + hash); + List added = new ArrayList (); + List removed = new ArrayList (); + store.catchUp (hash, added, removed); + if ( !added.isEmpty () || !removed.isEmpty () ) + { + List r = new ArrayList (); + List a = new ArrayList (); + for ( Blk blk : removed ) + { + r.add (toBCSAPIBlock (blk)); + } + for ( Blk blk : added ) + { + a.add (toBCSAPIBlock (blk)); + } + TrunkUpdateMessage tu = new TrunkUpdateMessage (a, r); + reply (o.getJMSReplyTo (), tu.toProtobuf ().toByteArray ()); + } + else + { + reply (o.getJMSReplyTo (), null); + } + log.debug ("blockRequest for " + hash + " replied"); + } + catch ( Exception e ) + { + log.trace ("Rejected invalid block request ", e); + } + } + }); + } + private void addTransactionRequestListener () throws JMSException { addQueueListener ("transactionRequest", new SessionMessageListener () From 839f9dc3b3e90d8abe556311fb4807845e2df15d Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 29 Aug 2013 18:40:10 +0200 Subject: [PATCH 181/299] 1.2.5 --- api/pom.xml | 2 +- pom.xml | 2 +- server/bop-service.sh | 2 +- server/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index d3e477fd..2da506ab 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2-SNAPSHOT + 1.2.5 ../ diff --git a/pom.xml b/pom.xml index 8a89f949..e7a35428 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.2-SNAPSHOT + 1.2.5 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/bop-service.sh b/server/bop-service.sh index 6ecd9008..53c3c0f8 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,7 +95,7 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.5.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi diff --git a/server/pom.xml b/server/pom.xml index 5512f57e..4ac84d4c 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2-SNAPSHOT + 1.2.5 ../ From 42f54de9558744073963607bae2aacaf4b945090 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Mon, 2 Sep 2013 10:30:02 +0200 Subject: [PATCH 182/299] catch null pointer at timeout --- .../com/bitsofproof/supernode/api/JMSServerConnector.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java index 5bd0ff79..d331a3cf 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java @@ -645,6 +645,10 @@ private byte[] synchronousRequest (Session session, MessageProducer producer, Me consumer = session.createConsumer (answerQueue); producer.send (m); BytesMessage reply = (BytesMessage) consumer.receive (timeout); + if ( reply == null ) + { + throw new BCSAPIException ("timeout"); + } if ( reply.getBodyLength () == 0 ) { return null; From 2e1a61266564fa628b51107d2fd8e563da647c59 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 7 Sep 2013 14:51:36 +0200 Subject: [PATCH 183/299] re-merging wallet code --- api/pom.xml | 2 +- .../com/bitsofproof/supernode/api/BCSAPI.java | 1 + .../supernode/api/JMSServerConnector.java | 1 + .../supernode/api/ServerConnector.java | 147 ---- .../supernode/common/BloomFilter.java | 2 +- .../{api => common}/ExtendedKey.java | 8 +- .../supernode/wallet/AccountListener.java | 24 + .../supernode/wallet/AccountManager.java | 87 ++ .../{api => wallet}/AddressConverter.java | 2 +- .../wallet/AddressListAccountManager.java | 90 +++ .../supernode/wallet/BaseAccountManager.java | 746 ++++++++++++++++++ .../wallet/ExtendedKeyAccountManager.java | 220 ++++++ .../wallet/ExtendedKeySetWallet.java | 43 + .../supernode/wallet/InMemoryUTXO.java | 89 +++ .../{api => wallet}/KeyFormatter.java | 2 +- .../wallet/KeyListAccountManager.java | 89 +++ .../supernode/wallet/SimpleFileWallet.java | 214 +++++ .../bitsofproof/supernode/wallet/Wallet.java | 10 + api/src/main/proto/BCSAPIMessage.proto | 14 + api/src/main/proto/WalletFormat.proto | 31 + .../supernode/api/ExtendedKeyTest.java | 1 + .../supernode/api/KeySerializerTest.java | 2 + pom.xml | 2 +- server/bop-service.sh | 4 +- server/pom.xml | 2 +- .../supernode/core/BlockStore.java | 2 +- .../supernode/core/CachedBlockStore.java | 2 +- .../supernode/core/ImplementBCSAPI.java | 2 +- .../bitsofproof/supernode/test/APITest.java | 265 +++++++ .../supernode/test/AddressTest.java | 2 +- 30 files changed, 1940 insertions(+), 166 deletions(-) delete mode 100644 api/src/main/java/com/bitsofproof/supernode/api/ServerConnector.java rename api/src/main/java/com/bitsofproof/supernode/{api => common}/ExtendedKey.java (97%) create mode 100644 api/src/main/java/com/bitsofproof/supernode/wallet/AccountListener.java create mode 100644 api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java rename api/src/main/java/com/bitsofproof/supernode/{api => wallet}/AddressConverter.java (97%) create mode 100644 api/src/main/java/com/bitsofproof/supernode/wallet/AddressListAccountManager.java create mode 100644 api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java create mode 100644 api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeyAccountManager.java create mode 100644 api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeySetWallet.java create mode 100644 api/src/main/java/com/bitsofproof/supernode/wallet/InMemoryUTXO.java rename api/src/main/java/com/bitsofproof/supernode/{api => wallet}/KeyFormatter.java (99%) create mode 100644 api/src/main/java/com/bitsofproof/supernode/wallet/KeyListAccountManager.java create mode 100644 api/src/main/java/com/bitsofproof/supernode/wallet/SimpleFileWallet.java create mode 100644 api/src/main/java/com/bitsofproof/supernode/wallet/Wallet.java create mode 100644 api/src/main/proto/WalletFormat.proto create mode 100644 server/src/test/java/com/bitsofproof/supernode/test/APITest.java diff --git a/api/pom.xml b/api/pom.xml index 2da506ab..168c69d1 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.5 + 1.2.6 ../ diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java index 0bd03554..aa169874 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java @@ -17,6 +17,7 @@ import java.util.Collection; +import com.bitsofproof.supernode.common.ExtendedKey; import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; /** diff --git a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java index d331a3cf..143b88c9 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java @@ -38,6 +38,7 @@ import org.slf4j.LoggerFactory; import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; +import com.bitsofproof.supernode.common.ExtendedKey; import com.bitsofproof.supernode.common.Hash; import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/ServerConnector.java deleted file mode 100644 index 44f6439e..00000000 --- a/api/src/main/java/com/bitsofproof/supernode/api/ServerConnector.java +++ /dev/null @@ -1,147 +0,0 @@ -package com.bitsofproof.supernode.api; - -import java.util.Collection; - -import com.bitsofproof.supernode.common.BloomFilter; -import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; - -public interface ServerConnector -{ - /** - * returns nounce while doing a full roundtrip to the server - * - * @param nonce - * @return - * @throws BCSAPIException - */ - public long ping (long nonce) throws BCSAPIException; - - /** - * sets the alert listener for the connections - * - * @param listener - * @throws BCSAPIException - */ - public void addAlertListener (AlertListener listener) throws BCSAPIException; - - public void removeAlertListener (AlertListener listener); - - /** - * Are we talking to production? - * - * @return - * @throws BCSAPIException - */ - public boolean isProduction () throws BCSAPIException; - - /** - * get block header for the hash - * - * @param hash - * @return block header or null if hash is unknown - * @throws BCSAPIException - */ - public Block getBlockHeader (String hash) throws BCSAPIException; - - /** - * get block for the hash - * - * @param hash - * @return block or null if hash is unknown - * @throws BCSAPIException - */ - public Block getBlock (String hash) throws BCSAPIException; - - /** - * get the transaction identified by the hash on the trunk - * - * @param hash - * @return transaction or null if no transaction with that hash on the trunk - * @throws BCSAPIException - */ - public Transaction getTransaction (String hash) throws BCSAPIException; - - /** - * send a signed transaction - * - * @param transaction - * @throws BCSAPIException - */ - public void sendTransaction (Transaction transaction) throws BCSAPIException; - - /** - * send a mined block - * - * @param block - * @throws BCSAPIException - */ - public void sendBlock (Block block) throws BCSAPIException; - - /** - * Register a transactions listener - * - * @param listener - * will be called for every validated transaction - * @throws BCSAPIException - */ - public void registerTransactionListener (TransactionListener listener) throws BCSAPIException; - - /** - * remove a listener for validated transactions - * - * @param listener - */ - public void removeTransactionListener (TransactionListener listener); - - /** - * Register a block listener - * - * @param listener - * will be called for every validated new block - * @throws BCSAPIException - */ - public void registerTrunkListener (TrunkListener listener) throws BCSAPIException; - - /** - * remove a trunk listener previously registered - * - * @param listener - */ - public void removeTrunkListener (TrunkListener listener); - - /** - * Match transactions using and address or outpoint in match. This returns only exact matches but limited in result set. - * - * @param match - * @param listener - * @throws BCSAPIException - */ - public void scanTransactions (Collection match, UpdateMode mode, long after, TransactionListener listener) throws BCSAPIException; - - /** - * scan transactions matching the filter - * - * @param filter - * @param after - * @param listener - * @throws BCSAPIException - */ - public void scanTransactions (BloomFilter filter, TransactionListener listener) throws BCSAPIException; - - /** - * register listener with a Bloom filter - * - * @param filter - * @param listener - * @throws BCSAPIException - */ - public void registerFilteredListener (BloomFilter filter, TransactionListener listener) throws BCSAPIException; - - /** - * remove the listener of a Bloom filter - * - * @param filter - * @param listener - */ - public void removeFilteredListener (BloomFilter filter, TransactionListener listener); -} diff --git a/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java b/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java index 69107dc8..ba9332fc 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java @@ -22,7 +22,7 @@ import org.bouncycastle.util.Arrays; -import com.bitsofproof.supernode.api.AddressConverter; +import com.bitsofproof.supernode.wallet.AddressConverter; public class BloomFilter { diff --git a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java b/api/src/main/java/com/bitsofproof/supernode/common/ExtendedKey.java similarity index 97% rename from api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java rename to api/src/main/java/com/bitsofproof/supernode/common/ExtendedKey.java index eecbc0bf..3061b0bd 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/ExtendedKey.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/ExtendedKey.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.bitsofproof.supernode.api; +package com.bitsofproof.supernode.common; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -40,12 +40,6 @@ import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; -import com.bitsofproof.supernode.common.ByteUtils; -import com.bitsofproof.supernode.common.ECKeyPair; -import com.bitsofproof.supernode.common.ECPublicKey; -import com.bitsofproof.supernode.common.Key; -import com.bitsofproof.supernode.common.ValidationException; - /** * Key Generator following BIP32 https://en.bitcoin.it/wiki/BIP_0032 */ diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/AccountListener.java b/api/src/main/java/com/bitsofproof/supernode/wallet/AccountListener.java new file mode 100644 index 00000000..00655652 --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/AccountListener.java @@ -0,0 +1,24 @@ +/* + * Copyright 2013 bits of proof zrt. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bitsofproof.supernode.wallet; + +import com.bitsofproof.supernode.api.Transaction; + + +public interface AccountListener +{ + public void accountChanged (AccountManager account, Transaction t); +} diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java new file mode 100644 index 00000000..97b3a0eb --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java @@ -0,0 +1,87 @@ +/* + * Copyright 2013 bits of proof zrt. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bitsofproof.supernode.wallet; + +import java.util.Collection; +import java.util.List; + +import com.bitsofproof.supernode.api.BCSAPI; +import com.bitsofproof.supernode.api.BCSAPIException; +import com.bitsofproof.supernode.api.Transaction; +import com.bitsofproof.supernode.api.TransactionListener; +import com.bitsofproof.supernode.api.TransactionOutput; +import com.bitsofproof.supernode.common.Key; +import com.bitsofproof.supernode.common.ValidationException; + +public interface AccountManager extends TransactionListener +{ + public interface UTXO + { + + public void add (TransactionOutput out); + + public Collection getUTXO (); + + public TransactionOutput get (String tx, long ix); + + public TransactionOutput remove (String tx, long ix); + + public long getTotal (); + + } + + public void sync (BCSAPI api) throws BCSAPIException; + + public void syncHistory (BCSAPI api) throws BCSAPIException; + + public long getCreated (); + + public Key getNextKey () throws ValidationException; + + public Key getKeyForAddress (byte[] address); + + public Collection getAddresses (); + + public Transaction pay (byte[] receiver, long amount, long fee) throws ValidationException; + + public Transaction pay (byte[] receiver, long amount) throws ValidationException; + + public Transaction split (long[] amounts, long fee) throws ValidationException; + + public long getBalance (); + + public long getConfirmed (); + + public long getSending (); + + public long getReceiving (); + + public long getChange (); + + public Collection getConfirmedOutputs (); + + public Collection getSendingOutputs (); + + public Collection getReceivingOutputs (); + + public Collection getChangeOutputs (); + + public List getTransactions (); + + public void addAccountListener (AccountListener listener); + + public void removeAccountListener (AccountListener listener); +} \ No newline at end of file diff --git a/api/src/main/java/com/bitsofproof/supernode/api/AddressConverter.java b/api/src/main/java/com/bitsofproof/supernode/wallet/AddressConverter.java similarity index 97% rename from api/src/main/java/com/bitsofproof/supernode/api/AddressConverter.java rename to api/src/main/java/com/bitsofproof/supernode/wallet/AddressConverter.java index d5d791e8..640a1031 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/AddressConverter.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/AddressConverter.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.bitsofproof.supernode.api; +package com.bitsofproof.supernode.wallet; import com.bitsofproof.supernode.common.ByteUtils; import com.bitsofproof.supernode.common.Hash; diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/AddressListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/AddressListAccountManager.java new file mode 100644 index 00000000..830e6a30 --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/AddressListAccountManager.java @@ -0,0 +1,90 @@ +package com.bitsofproof.supernode.wallet; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.bitsofproof.supernode.api.BCSAPI; +import com.bitsofproof.supernode.api.BCSAPIException; +import com.bitsofproof.supernode.api.Transaction; +import com.bitsofproof.supernode.api.TransactionListener; +import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; +import com.bitsofproof.supernode.common.ByteVector; +import com.bitsofproof.supernode.common.Key; +import com.bitsofproof.supernode.common.ValidationException; + +public class AddressListAccountManager extends BaseAccountManager +{ + private static final Logger log = LoggerFactory.getLogger (AddressListAccountManager.class); + + private final Set addresses = new HashSet (); + + @Override + public Collection getAddresses () + { + List al = new ArrayList (); + for ( ByteVector b : addresses ) + { + al.add (b.toByteArray ()); + } + return al; + } + + @Override + public Key getKeyForAddress (byte[] address) + { + return null; + } + + @Override + public boolean isOwnAddress (byte[] address) + { + return addresses.contains (new ByteVector (address)); + } + + @Override + public Key getNextKey () throws ValidationException + { + return null; + } + + public void addAddress (byte[] address) + { + addresses.add (new ByteVector (address)); + } + + @Override + public void syncHistory (BCSAPI api) throws BCSAPIException + { + log.trace ("Sync naddr: " + addresses.size ()); + api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () + { + @Override + public void process (Transaction t) + { + updateWithTransaction (t); + } + }); + log.trace ("Sync finished naddr: " + addresses.size ()); + } + + @Override + public void sync (BCSAPI api) throws BCSAPIException + { + log.trace ("Sync naddr: " + addresses.size ()); + api.scanUTXO (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () + { + @Override + public void process (Transaction t) + { + updateWithTransaction (t); + } + }); + log.trace ("Sync finished naddr: " + addresses.size ()); + } +} diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java new file mode 100644 index 00000000..cc87b8e0 --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java @@ -0,0 +1,746 @@ +package com.bitsofproof.supernode.wallet; + +import java.io.PrintStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.bouncycastle.util.Arrays; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.bitsofproof.supernode.api.Transaction; +import com.bitsofproof.supernode.api.TransactionInput; +import com.bitsofproof.supernode.api.TransactionOutput; +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.ByteVector; +import com.bitsofproof.supernode.common.ECKeyPair; +import com.bitsofproof.supernode.common.Key; +import com.bitsofproof.supernode.common.ScriptFormat; +import com.bitsofproof.supernode.common.ScriptFormat.Opcode; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.common.WireFormat; + +public abstract class BaseAccountManager implements AccountManager +{ + private static final Logger log = LoggerFactory.getLogger (BaseAccountManager.class); + + private static final long MINIMUM_FEE = 10000; + private static final long MAXIMUM_FEE = 1000000; + + private final UTXO confirmed = createConfirmedUTXO (); + private final UTXO change = createChangeUTXO (); + private final UTXO receiving = createReceivingUTXO (); + private final UTXO sending = createSendingUTXO (); + + private long created; + + @Override + public long getCreated () + { + return created; + } + + public void setCreated (long created) + { + this.created = created; + } + + private final List accountListener = Collections.synchronizedList (new ArrayList ()); + private final Map transactions = new HashMap (); + + protected UTXO createConfirmedUTXO () + { + return new InMemoryUTXO (); + } + + protected UTXO createChangeUTXO () + { + return new InMemoryUTXO (); + } + + protected UTXO createSendingUTXO () + { + return new InMemoryUTXO (); + } + + protected UTXO createReceivingUTXO () + { + return new InMemoryUTXO (); + } + + public boolean isOwnAddress (byte[] address) + { + return getKeyForAddress (address) != null; + } + + protected static class TransactionSink + { + private final byte[] address; + private final long value; + + public TransactionSink (byte[] address, long value) + { + super (); + this.address = Arrays.clone (address); + this.value = value; + } + + public byte[] getAddress () + { + return Arrays.clone (address); + } + + public long getValue () + { + return value; + } + } + + public static long estimateFee (Transaction t) + { + WireFormat.Writer writer = new WireFormat.Writer (); + t.toWire (writer); + return Math.min (MAXIMUM_FEE, Math.max (MINIMUM_FEE, writer.toByteArray ().length / 1000 * MINIMUM_FEE)); + } + + protected Transaction createSpend (List sources, List sinks, long fee) throws ValidationException + { + if ( fee < 0 || fee > MAXIMUM_FEE ) + { + throw new ValidationException ("You unlikely want to do that"); + } + Transaction transaction = new Transaction (); + transaction.setInputs (new ArrayList ()); + transaction.setOutputs (new ArrayList ()); + + long sumOut = 0; + for ( TransactionSink s : sinks ) + { + TransactionOutput o = new TransactionOutput (); + o.setValue (s.getValue ()); + sumOut += s.getValue (); + + ScriptFormat.Writer writer = new ScriptFormat.Writer (); + writer.writeToken (new ScriptFormat.Token (Opcode.OP_DUP)); + writer.writeToken (new ScriptFormat.Token (Opcode.OP_HASH160)); + if ( s.getAddress ().length != 20 ) + { + throw new ValidationException ("Sink is not an address"); + } + writer.writeData (s.getAddress ()); + writer.writeToken (new ScriptFormat.Token (Opcode.OP_EQUALVERIFY)); + writer.writeToken (new ScriptFormat.Token (Opcode.OP_CHECKSIG)); + o.setScript (writer.toByteArray ()); + + transaction.getOutputs ().add (o); + } + + long sumInput = 0; + for ( TransactionOutput o : sources ) + { + TransactionInput i = new TransactionInput (); + i.setSourceHash (o.getTxHash ()); + i.setIx (o.getIx ()); + sumInput += o.getValue (); + + transaction.getInputs ().add (i); + } + if ( sumInput != (sumOut + fee) ) + { + throw new ValidationException ("Sum of sinks (+fee) does not match sum of sources"); + } + + int j = 0; + for ( TransactionOutput o : sources ) + { + TransactionInput i = transaction.getInputs ().get (j); + ScriptFormat.Writer sw = new ScriptFormat.Writer (); + byte[] address = o.getOutputAddress (); + if ( address == null ) + { + throw new ValidationException ("Can only spend pay to address outputs"); + } + Key key = getKeyForAddress (address); + if ( key == null ) + { + throw new ValidationException ("Have no key to spend this output"); + } + byte[] sig = key.sign (hashTransaction (transaction, j, ScriptFormat.SIGHASH_ALL, o.getScript ())); + byte[] sigPlusType = new byte[sig.length + 1]; + System.arraycopy (sig, 0, sigPlusType, 0, sig.length); + sigPlusType[sigPlusType.length - 1] = (byte) (ScriptFormat.SIGHASH_ALL & 0xff); + sw.writeData (sigPlusType); + sw.writeData (key.getPublic ()); + i.setScript (sw.toByteArray ()); + ++j; + } + + transaction.computeHash (); + return transaction; + } + + private static byte[] hashTransaction (Transaction transaction, int inr, int hashType, byte[] script) throws ValidationException + { + Transaction copy = null; + try + { + copy = transaction.clone (); + } + catch ( CloneNotSupportedException e1 ) + { + return null; + } + + // implicit SIGHASH_ALL + int i = 0; + for ( TransactionInput in : copy.getInputs () ) + { + if ( i == inr ) + { + in.setScript (script); + } + else + { + in.setScript (new byte[0]); + } + ++i; + } + + if ( (hashType & 0x1f) == ScriptFormat.SIGHASH_NONE ) + { + copy.getOutputs ().clear (); + i = 0; + for ( TransactionInput in : copy.getInputs () ) + { + if ( i != inr ) + { + in.setSequence (0); + } + ++i; + } + } + else if ( (hashType & 0x1f) == ScriptFormat.SIGHASH_SINGLE ) + { + int onr = inr; + if ( onr >= copy.getOutputs ().size () ) + { + // this is a Satoshi client bug. + // This case should throw an error but it instead retuns 1 that is not checked and interpreted as below + return ByteUtils.fromHex ("0100000000000000000000000000000000000000000000000000000000000000"); + } + for ( i = copy.getOutputs ().size () - 1; i > onr; --i ) + { + copy.getOutputs ().remove (i); + } + for ( i = 0; i < onr; ++i ) + { + copy.getOutputs ().get (i).setScript (new byte[0]); + copy.getOutputs ().get (i).setValue (-1L); + } + i = 0; + for ( TransactionInput in : copy.getInputs () ) + { + if ( i != inr ) + { + in.setSequence (0); + } + ++i; + } + } + if ( (hashType & ScriptFormat.SIGHASH_ANYONECANPAY) != 0 ) + { + List oneIn = new ArrayList (); + oneIn.add (copy.getInputs ().get (inr)); + copy.setInputs (oneIn); + } + + WireFormat.Writer writer = new WireFormat.Writer (); + copy.toWire (writer); + + byte[] txwire = writer.toByteArray (); + byte[] hash = null; + try + { + MessageDigest a = MessageDigest.getInstance ("SHA-256"); + a.update (txwire); + a.update (new byte[] { (byte) (hashType & 0xff), 0, 0, 0 }); + hash = a.digest (a.digest ()); + } + catch ( NoSuchAlgorithmException e ) + { + } + return hash; + } + + protected List getSufficientSources (long amount, long fee, String color) + { + List candidates = new ArrayList (); + candidates.addAll (confirmed.getUTXO ()); + // prefer confirmed + Collections.sort (candidates, new Comparator () + { + // prefer aggregation of UTXO + @Override + public int compare (TransactionOutput o1, TransactionOutput o2) + { + return (int) (o1.getValue () - o2.getValue ()); + } + }); + List changelist = new ArrayList (); + changelist.addAll (change.getUTXO ()); + // ... then change + Collections.sort (changelist, new Comparator () + { + // prefer aggregation of UTXO + @Override + public int compare (TransactionOutput o1, TransactionOutput o2) + { + return (int) (o1.getValue () - o2.getValue ()); + } + }); + candidates.addAll (changelist); + + List result = new ArrayList (); + long sum = 0; + for ( TransactionOutput o : candidates ) + { + if ( color == null ) + { + if ( o.getColor () == null ) + { + sum += o.getValue (); + result.add (o); + if ( sum >= (amount + fee) ) + { + return result; + } + } + } + else + { + if ( o.getColor ().equals (color) ) + { + sum += o.getValue (); + result.add (o); + if ( sum >= amount ) + { + if ( fee > 0 ) + { + result.addAll (getSufficientSources (0, fee, null)); + } + return result; + } + } + } + } + return null; + } + + @Override + public Transaction pay (byte[] receiver, long amount, long fee) throws ValidationException + { + synchronized ( confirmed ) + { + log.trace ("pay to " + ByteUtils.toHex (receiver) + " " + amount + " + " + fee); + List sources = getSufficientSources (amount, fee, null); + if ( sources == null ) + { + throw new ValidationException ("Insufficient funds to pay " + (amount + fee)); + } + List sinks = new ArrayList (); + + long in = 0; + for ( TransactionOutput o : sources ) + { + log.trace ("using input " + o.getTxHash () + "[" + o.getIx () + "] " + o.getValue ()); + in += o.getValue (); + } + TransactionSink target = new TransactionSink (receiver, amount); + if ( (in - amount - fee) > 0 ) + { + TransactionSink change = new TransactionSink (getNextKey ().getAddress (), in - amount - fee); + log.trace ("change to " + ByteUtils.toHex (change.getAddress ()) + " " + change.getValue ()); + if ( new SecureRandom ().nextBoolean () ) + { + sinks.add (target); + sinks.add (change); + } + else + { + sinks.add (change); + sinks.add (target); + } + } + else + { + sinks.add (target); + } + return createSpend (sources, sinks, fee); + } + } + + @Override + public Transaction pay (byte[] receiver, long amount) throws ValidationException + { + long fee = MINIMUM_FEE; + long estimate = 0; + Transaction t = null; + + do + { + fee = Math.max (fee, estimate); + t = pay (receiver, amount, fee); + estimate = estimateFee (t); + if ( fee < estimate ) + { + log.trace ("The transaction requires more network fees. Reassembling."); + } + } while ( fee < estimate ); + + return t; + } + + @Override + public Transaction split (long[] amounts, long fee) throws ValidationException + { + synchronized ( confirmed ) + { + List sinks = new ArrayList (); + + long amount = 0; + for ( long a : amounts ) + { + amount += a; + } + List sources = getSufficientSources (amount, fee, null); + if ( sources == null ) + { + throw new ValidationException ("Insufficient funds to pay " + (amount + fee)); + } + long in = 0; + for ( TransactionOutput o : sources ) + { + in += o.getValue (); + } + for ( long a : amounts ) + { + TransactionSink target = new TransactionSink (getNextKey ().getAddress (), a); + sinks.add (target); + } + if ( (in - amount - fee) > 0 ) + { + TransactionSink change = new TransactionSink (getNextKey ().getAddress (), in - amount - fee); + sinks.add (change); + } + Collections.shuffle (sinks); + return createSpend (sources, sinks, fee); + } + } + + public boolean updateWithTransaction (Transaction t) + { + synchronized ( confirmed ) + { + boolean modified = false; + if ( !t.isDoubleSpend () ) + { + TransactionOutput spend = null; + for ( TransactionInput i : t.getInputs () ) + { + spend = confirmed.get (i.getSourceHash (), i.getIx ()); + if ( spend != null ) + { + confirmed.remove (i.getSourceHash (), i.getIx ()); + log.trace ("Spend settled output " + i.getSourceHash () + " [" + i.getIx () + "] " + spend.getValue ()); + } + else + { + spend = change.get (i.getSourceHash (), i.getIx ()); + if ( spend != null ) + { + change.remove (i.getSourceHash (), i.getIx ()); + log.trace ("Spend change output " + i.getSourceHash () + " [" + i.getIx () + "] " + spend.getValue ()); + } + else + { + spend = receiving.get (i.getSourceHash (), i.getIx ()); + if ( spend != null ) + { + receiving.remove (i.getSourceHash (), i.getIx ()); + log.trace ("Spend receiving output " + i.getSourceHash () + " [" + i.getIx () + "] " + spend.getValue ()); + } + } + } + } + modified = spend != null; + for ( TransactionOutput o : t.getOutputs () ) + { + confirmed.remove (o.getTxHash (), o.getIx ()); + change.remove (o.getTxHash (), o.getIx ()); + receiving.remove (o.getTxHash (), o.getIx ()); + sending.remove (o.getTxHash (), o.getIx ()); + + if ( isOwnAddress (o.getOutputAddress ()) ) + { + modified = true; + if ( t.getBlockHash () != null ) + { + confirmed.add (o); + log.trace ("Settled " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + o.getValue ()); + } + else + { + if ( spend != null ) + { + change.add (o); + log.trace ("Change " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + + o.getValue ()); + } + else + { + receiving.add (o); + log.trace ("Receiving " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + + o.getValue ()); + } + } + } + else + { + if ( t.getBlockHash () == null && spend != null ) + { + modified = true; + sending.add (o); + log.trace ("Sending " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + o.getValue ()); + } + } + } + if ( modified ) + { + transactions.put (t.getHash (), t); + } + } + else + { + for ( long ix = 0; ix < t.getOutputs ().size (); ++ix ) + { + TransactionOutput out = null; + out = confirmed.remove (t.getHash (), ix); + if ( out == null ) + { + out = change.remove (t.getHash (), ix); + } + if ( out == null ) + { + out = receiving.remove (t.getHash (), ix); + } + if ( out == null ) + { + out = sending.remove (t.getHash (), ix); + } + if ( out != null ) + { + log.trace ("Remove DS " + out.getTxHash () + " [" + out.getIx () + "] (" + ByteUtils.toHex (out.getOutputAddress ()) + ")" + + out.getValue ()); + } + modified |= out != null; + } + transactions.remove (t.getHash ()); + } + return modified; + } + } + + @Override + public long getBalance () + { + synchronized ( confirmed ) + { + return confirmed.getTotal () + change.getTotal () + receiving.getTotal (); + } + } + + @Override + public long getConfirmed () + { + synchronized ( confirmed ) + { + return confirmed.getTotal (); + } + } + + @Override + public long getSending () + { + synchronized ( confirmed ) + { + return sending.getTotal (); + } + } + + @Override + public long getReceiving () + { + synchronized ( confirmed ) + { + return receiving.getTotal (); + } + } + + @Override + public long getChange () + { + synchronized ( confirmed ) + { + return change.getTotal (); + } + } + + @Override + public Collection getConfirmedOutputs () + { + return confirmed.getUTXO (); + } + + @Override + public Collection getSendingOutputs () + { + return sending.getUTXO (); + } + + @Override + public Collection getReceivingOutputs () + { + return receiving.getUTXO (); + } + + @Override + public Collection getChangeOutputs () + { + return change.getUTXO (); + } + + @Override + public void addAccountListener (AccountListener listener) + { + accountListener.add (listener); + } + + @Override + public void removeAccountListener (AccountListener listener) + { + accountListener.remove (listener); + } + + protected void notifyListener (Transaction t) + { + synchronized ( accountListener ) + { + for ( AccountListener l : accountListener ) + { + try + { + l.accountChanged (this, t); + } + catch ( Exception e ) + { + } + } + } + } + + @Override + public void process (Transaction t) + { + if ( updateWithTransaction (t) ) + { + notifyListener (t); + } + } + + @Override + public List getTransactions () + { + List tl = new ArrayList (); + tl.addAll (transactions.values ()); + Collections.sort (tl, new Comparator () + { + @Override + public int compare (Transaction a, Transaction b) + { + for ( TransactionInput i : b.getInputs () ) + { + if ( i.getSourceHash ().equals (a.getHash ()) ) + { + return -1; + } + } + for ( TransactionInput i : a.getInputs () ) + { + if ( i.getSourceHash ().equals (b.getHash ()) ) + { + return 1; + } + } + return 0; + } + }); + + return tl; + } + + public void dumpOutputs (PrintStream print) + { + print.println ("Confirmed:"); + dumpUTXO (print, confirmed); + print.println ("Receiving:"); + dumpUTXO (print, receiving); + print.println ("Change:"); + dumpUTXO (print, change); + print.println ("Sending:"); + dumpUTXO (print, sending); + } + + public void dumpKeys (PrintStream print) + { + Set addresses = new HashSet (); + for ( TransactionOutput o : confirmed.getUTXO () ) + { + addresses.add (new ByteVector (o.getOutputAddress ())); + } + for ( TransactionOutput o : change.getUTXO () ) + { + addresses.add (new ByteVector (o.getOutputAddress ())); + } + for ( TransactionOutput o : receiving.getUTXO () ) + { + addresses.add (new ByteVector (o.getOutputAddress ())); + } + for ( ByteVector b : addresses ) + { + Key k = getKeyForAddress (b.toByteArray ()); + if ( k != null ) + { + print.println (ECKeyPair.serializeWIF (k) + " (" + ByteUtils.toHex (b.toByteArray ()) + ")"); + } + } + } + + private void dumpUTXO (PrintStream print, UTXO set) + { + for ( TransactionOutput o : set.getUTXO () ) + { + print.println (o.getTxHash () + "[" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + o.getValue ()); + } + print.println (); + } +} diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeyAccountManager.java new file mode 100644 index 00000000..d5d235c7 --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeyAccountManager.java @@ -0,0 +1,220 @@ +/* + * Copyright 2013 bits of proof zrt. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bitsofproof.supernode.wallet; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.bitsofproof.supernode.api.BCSAPI; +import com.bitsofproof.supernode.api.BCSAPIException; +import com.bitsofproof.supernode.api.Transaction; +import com.bitsofproof.supernode.api.TransactionListener; +import com.bitsofproof.supernode.api.TransactionOutput; +import com.bitsofproof.supernode.common.ByteVector; +import com.bitsofproof.supernode.common.ExtendedKey; +import com.bitsofproof.supernode.common.Key; +import com.bitsofproof.supernode.common.ValidationException; + +public class ExtendedKeyAccountManager extends BaseAccountManager implements TransactionListener +{ + private static final Logger log = LoggerFactory.getLogger (ExtendedKeyAccountManager.class); + + private final Set usedKeys = new HashSet (); + private final Map keyIDForAddress = new HashMap (); + private ExtendedKey master; + private int nextSequence; + private int lookAhead = 100; + private int firstIndex; + + public ExtendedKey getMaster () + { + return master; + } + + public void setMaster (ExtendedKey master) + { + this.master = master; + } + + public int getFirstIndex () + { + return firstIndex; + } + + public void setLookAhead (int lookAhead) + { + this.lookAhead = lookAhead; + } + + public void setFirstIndex (int firstIndex) + { + this.firstIndex = firstIndex; + } + + public int getLookAhead () + { + return lookAhead; + } + + @Override + protected void notifyListener (Transaction t) + { + for ( TransactionOutput o : t.getOutputs () ) + { + if ( isOwnAddress (o.getOutputAddress ()) ) + { + int keyId = keyIDForAddress.get (new ByteVector (o.getOutputAddress ())); + ensureLookAhead (keyId); + } + } + super.notifyListener (t); + } + + private void ensureLookAhead (int from) + { + while ( keyIDForAddress.size () < (from + lookAhead - firstIndex) ) + { + Key key = null; + try + { + key = master.getKey (keyIDForAddress.size () + firstIndex); + } + catch ( ValidationException e ) + { + } + keyIDForAddress.put (new ByteVector (key.getAddress ()), keyIDForAddress.size () + firstIndex); + } + } + + public Key getKey (int i) throws ValidationException + { + ensureLookAhead (i); + return master.getKey (i); + } + + public void setNextKey (int i) + { + nextSequence = i; + ensureLookAhead (nextSequence); + } + + @Override + public Key getNextKey () throws ValidationException + { + return getKey (nextSequence++); + } + + public Set getUsedKeys () + { + return usedKeys; + } + + public Integer getKeyIDForAddress (byte[] address) + { + Integer id = keyIDForAddress.get (new ByteVector (address)); + if ( id != null ) + { + usedKeys.add (id); + } + return id; + } + + @Override + public Key getKeyForAddress (byte[] address) + { + Integer keyId = getKeyIDForAddress (address); + if ( keyId == null ) + { + return null; + } + try + { + return getKey (keyId); + } + catch ( ValidationException e ) + { + return null; + } + } + + @Override + public Collection getAddresses () + { + List addresses = new ArrayList (); + for ( ByteVector v : keyIDForAddress.keySet () ) + { + if ( keyIDForAddress.get (v).intValue () < nextSequence ) + { + addresses.add (v.toByteArray ()); + } + } + return addresses; + } + + private final TransactionListener processor = new TransactionListener () + { + @Override + public void process (Transaction t) + { + for ( TransactionOutput o : t.getOutputs () ) + { + Integer thisKey = getKeyIDForAddress (o.getOutputAddress ()); + if ( thisKey != null ) + { + ensureLookAhead (thisKey); + nextSequence = Math.max (nextSequence, thisKey + 1); + } + } + updateWithTransaction (t); + } + }; + + @Override + public void syncHistory (BCSAPI api) throws BCSAPIException + { + ensureLookAhead (0); + log.trace ("Sync nkeys: " + (nextSequence - firstIndex)); + api.scanTransactions (getMaster (), firstIndex, lookAhead, getCreated (), processor); + firstIndex = nextSequence; + for ( Integer id : usedKeys ) + { + firstIndex = Math.min (id, firstIndex); + } + log.trace ("Sync finished with nkeys: " + (nextSequence - firstIndex)); + } + + @Override + public void sync (BCSAPI api) throws BCSAPIException + { + ensureLookAhead (0); + log.trace ("Sync nkeys: " + (nextSequence - firstIndex)); + api.scanUTXO (getMaster (), firstIndex, lookAhead, getCreated (), processor); + firstIndex = nextSequence; + for ( Integer id : usedKeys ) + { + firstIndex = Math.min (id, firstIndex); + } + log.trace ("Sync finished with nkeys: " + (nextSequence - firstIndex)); + } +} diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeySetWallet.java b/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeySetWallet.java new file mode 100644 index 00000000..4ce488b5 --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeySetWallet.java @@ -0,0 +1,43 @@ +package com.bitsofproof.supernode.wallet; + +import java.util.HashMap; +import java.util.Map; + +import com.bitsofproof.supernode.common.ExtendedKey; +import com.bitsofproof.supernode.common.ValidationException; + +public class ExtendedKeySetWallet implements Wallet +{ + private final Map accountManager = new HashMap (); + + public void addKey (String name, ExtendedKey key, long created) throws ValidationException + { + if ( accountManager.containsKey (name) ) + { + throw new ValidationException (name + " account manager exsists"); + } + ExtendedKeyAccountManager manager; + accountManager.put (name, manager = new ExtendedKeyAccountManager ()); + manager.setMaster (key); + } + + @Override + public AccountManager getAccountManager (String name) + { + return accountManager.get (name); + } + + @Override + public AccountManager createAccountManager (String name) throws ValidationException + { + if ( accountManager.containsKey (name) ) + { + throw new ValidationException (name + " account manager exsists"); + } + ExtendedKeyAccountManager manager; + accountManager.put (name, manager = new ExtendedKeyAccountManager ()); + manager.setMaster (ExtendedKey.createNew ()); + return manager; + } + +} diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/InMemoryUTXO.java b/api/src/main/java/com/bitsofproof/supernode/wallet/InMemoryUTXO.java new file mode 100644 index 00000000..08245805 --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/InMemoryUTXO.java @@ -0,0 +1,89 @@ +/* + * Copyright 2013 bits of proof zrt. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bitsofproof.supernode.wallet; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import com.bitsofproof.supernode.api.TransactionOutput; + +public class InMemoryUTXO implements AccountManager.UTXO +{ + private static class TxOutKey + { + String hash; + long ix; + + public TxOutKey (String hash, long ix) + { + this.hash = hash; + this.ix = ix; + } + + @Override + public boolean equals (Object obj) + { + TxOutKey o = (TxOutKey) obj; + return o.hash.equals (hash) && o.ix == ix; + } + + @Override + public int hashCode () + { + return (int) (hash.hashCode () + ix); + } + + } + + private final Map utxo = new HashMap (); + + @Override + public void add (TransactionOutput out) + { + utxo.put (new TxOutKey (out.getTxHash (), out.getIx ()), out); + } + + @Override + public Collection getUTXO () + { + return Collections.unmodifiableCollection (utxo.values ()); + } + + @Override + public TransactionOutput get (String tx, long ix) + { + return utxo.get (new TxOutKey (tx, ix)); + } + + @Override + public TransactionOutput remove (String tx, long ix) + { + return utxo.remove (new TxOutKey (tx, ix)); + } + + @Override + public long getTotal () + { + long s = 0; + for ( TransactionOutput o : utxo.values () ) + { + s += o.getValue (); + } + return s; + } +} diff --git a/api/src/main/java/com/bitsofproof/supernode/api/KeyFormatter.java b/api/src/main/java/com/bitsofproof/supernode/wallet/KeyFormatter.java similarity index 99% rename from api/src/main/java/com/bitsofproof/supernode/api/KeyFormatter.java rename to api/src/main/java/com/bitsofproof/supernode/wallet/KeyFormatter.java index cb458790..a0f5d435 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/KeyFormatter.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/KeyFormatter.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.bitsofproof.supernode.api; +package com.bitsofproof.supernode.wallet; import java.io.UnsupportedEncodingException; import java.math.BigInteger; diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/KeyListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/KeyListAccountManager.java new file mode 100644 index 00000000..a461fbc4 --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/KeyListAccountManager.java @@ -0,0 +1,89 @@ +package com.bitsofproof.supernode.wallet; + +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.bitsofproof.supernode.api.BCSAPI; +import com.bitsofproof.supernode.api.BCSAPIException; +import com.bitsofproof.supernode.api.Transaction; +import com.bitsofproof.supernode.api.TransactionListener; +import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; +import com.bitsofproof.supernode.common.ByteVector; +import com.bitsofproof.supernode.common.ECKeyPair; +import com.bitsofproof.supernode.common.Key; +import com.bitsofproof.supernode.common.ValidationException; + +public class KeyListAccountManager extends BaseAccountManager +{ + private static final Logger log = LoggerFactory.getLogger (KeyListAccountManager.class); + + private final Map keyByAddress = new HashMap (); + private final List keys = new ArrayList (); + private static final SecureRandom rnd = new SecureRandom (); + + @Override + public Collection getAddresses () + { + List a = new ArrayList (); + for ( ByteVector b : keyByAddress.keySet () ) + { + a.add (b.toByteArray ()); + } + return a; + } + + @Override + public Key getKeyForAddress (byte[] address) + { + return keyByAddress.get (new ByteVector (address)); + } + + @Override + public Key getNextKey () throws ValidationException + { + return keys.get (rnd.nextInt (keys.size ())); + } + + public void addKey (ECKeyPair key) + { + keys.add (key); + keyByAddress.put (new ByteVector (key.getAddress ()), key); + } + + @Override + public void syncHistory (BCSAPI api) throws BCSAPIException + { + log.trace ("Sync naddr: " + keys.size ()); + api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () + { + @Override + public void process (Transaction t) + { + updateWithTransaction (t); + } + }); + log.trace ("Sync finished naddr: " + keys.size ()); + } + + @Override + public void sync (BCSAPI api) throws BCSAPIException + { + log.trace ("Sync naddr: " + keys.size ()); + api.scanUTXO (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () + { + @Override + public void process (Transaction t) + { + updateWithTransaction (t); + } + }); + log.trace ("Sync finished naddr: " + keys.size ()); + } +} diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/SimpleFileWallet.java b/api/src/main/java/com/bitsofproof/supernode/wallet/SimpleFileWallet.java new file mode 100644 index 00000000..8060591d --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/SimpleFileWallet.java @@ -0,0 +1,214 @@ +package com.bitsofproof.supernode.wallet; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.bitsofproof.supernode.api.BCSAPI; +import com.bitsofproof.supernode.api.BCSAPIException; +import com.bitsofproof.supernode.common.ExtendedKey; +import com.bitsofproof.supernode.common.ValidationException; +import com.google.protobuf.ByteString; + +public class SimpleFileWallet implements Wallet +{ + private static final SecureRandom random = new SecureRandom (); + private transient ExtendedKey master; + private byte[] encrypted; + private byte[] signature; + private String fileName; + private long since; + + private static class NCExtendedKeyAccountManager extends ExtendedKeyAccountManager + { + private final String name; + + public NCExtendedKeyAccountManager (String name, long created) + { + super (); + this.name = name; + setCreated (created); + } + + public String getName () + { + return name; + } + } + + private final Map accounts = new HashMap (); + + public SimpleFileWallet (String fileName) + { + this.fileName = fileName; + this.since = System.currentTimeMillis (); + } + + public boolean exists () + { + return new File (fileName).exists (); + } + + public void init (String passphrase) + { + master = null; + encrypted = new byte[32]; + random.nextBytes (encrypted); + try + { + signature = ExtendedKey.createFromPassphrase (passphrase, encrypted).getMaster ().sign (encrypted); + } + catch ( ValidationException e ) + { + } + } + + public void init (String passphrase, ExtendedKey master, boolean production, long since) + { + this.master = master; + this.since = since; + try + { + encrypted = master.encrypt (passphrase, production); + signature = ExtendedKey.createFromPassphrase (passphrase, encrypted).getMaster ().sign (encrypted); + } + catch ( ValidationException e ) + { + } + } + + public ExtendedKey getMaster () + { + return master; + } + + public void unlock (String passphrase) throws ValidationException + { + master = ExtendedKey.createFromPassphrase (passphrase, encrypted); + if ( !master.getMaster ().verify (encrypted, signature) ) + { + throw new ValidationException ("incorrect passphrase"); + } + for ( NCExtendedKeyAccountManager account : accounts.values () ) + { + account.setMaster (master.getChild (account.getMaster ().getSequence ())); + } + } + + public void lock () + { + master = null; + for ( ExtendedKeyAccountManager account : accounts.values () ) + { + account.setMaster (account.getMaster ().getReadOnly ()); + } + } + + public List getAccountNames () + { + List names = new ArrayList (); + for ( NCExtendedKeyAccountManager account : accounts.values () ) + { + names.add (account.getName ()); + } + return names; + } + + public void setFileName (String fileName) + { + this.fileName = fileName; + } + + public static SimpleFileWallet read (String fileName) throws IOException, ValidationException + { + SimpleFileWallet wallet = new SimpleFileWallet (fileName); + File f = new File (fileName); + InputStream in = new FileInputStream (f); + try + { + WalletFormat.SimpleWallet walletMessage = WalletFormat.SimpleWallet.parseFrom (in); + wallet.encrypted = walletMessage.getEncryptedSeed ().toByteArray (); + wallet.signature = walletMessage.getSignature ().toByteArray (); + for ( WalletFormat.SimpleWallet.Account account : walletMessage.getAccountsList () ) + { + ExtendedKey pub = ExtendedKey.parse (account.getPublicKey ()); + NCExtendedKeyAccountManager am = new NCExtendedKeyAccountManager (account.getName (), account.getCreated () * 1000); + am.setFirstIndex (account.getFirstIndex ()); + wallet.accounts.put (account.getName (), am); + am.setMaster (pub); + } + } + finally + { + in.close (); + } + return wallet; + } + + public void sync (BCSAPI api) throws BCSAPIException, ValidationException + { + for ( NCExtendedKeyAccountManager account : accounts.values () ) + { + account.syncHistory (api); + } + } + + @Override + public synchronized AccountManager getAccountManager (String name) + { + return accounts.get (name); + } + + @Override + public synchronized AccountManager createAccountManager (String name) throws ValidationException + { + if ( accounts.containsKey (name) ) + { + return accounts.get (name); + } + else + { + if ( master == null ) + { + throw new ValidationException ("The wallet is locked"); + } + NCExtendedKeyAccountManager account = new NCExtendedKeyAccountManager (name, Math.min (System.currentTimeMillis (), since)); + account.setMaster (master.getChild (accounts.size () | 0x80000000)); + accounts.put (name, account); + return account; + } + } + + public void persist () throws IOException + { + FileOutputStream out = new FileOutputStream (fileName); + try + { + WalletFormat.SimpleWallet.Builder builder = WalletFormat.SimpleWallet.newBuilder (); + builder.setBcsapiversion (1); + builder.setEncryptedSeed (ByteString.copyFrom (encrypted)); + builder.setSignature (ByteString.copyFrom (signature)); + for ( NCExtendedKeyAccountManager am : accounts.values () ) + { + WalletFormat.SimpleWallet.Account.Builder ab = WalletFormat.SimpleWallet.Account.newBuilder (); + ab.setName (am.getName ()); + ab.setCreated (am.getCreated () / 1000); + ab.setFirstIndex (am.getFirstIndex ()); + ab.setPublicKey (am.getMaster ().getReadOnly ().serialize (true)); + builder.addAccounts (ab.build ()); + } + builder.build ().writeTo (out); + } + finally + { + out.close (); + } + } +} diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/Wallet.java b/api/src/main/java/com/bitsofproof/supernode/wallet/Wallet.java new file mode 100644 index 00000000..f4f2ab36 --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/Wallet.java @@ -0,0 +1,10 @@ +package com.bitsofproof.supernode.wallet; + +import com.bitsofproof.supernode.common.ValidationException; + +public interface Wallet +{ + public AccountManager getAccountManager (String name); + + public AccountManager createAccountManager (String name) throws ValidationException; +} \ No newline at end of file diff --git a/api/src/main/proto/BCSAPIMessage.proto b/api/src/main/proto/BCSAPIMessage.proto index 0d65fa37..515c6724 100644 --- a/api/src/main/proto/BCSAPIMessage.proto +++ b/api/src/main/proto/BCSAPIMessage.proto @@ -102,3 +102,17 @@ message AccountRequest optional uint64 after = 4; optional uint32 firstIndex = 5; } + +message SimpleWallet { + required uint32 bcsapiversion = 1; + required bytes encryptedSeed = 2; + required bytes signature = 3; + message Account + { + required string publicKey = 1; + required uint64 created = 2; + required string name = 3; + optional uint32 firstIndex = 4; + } + repeated Account accounts = 4; +} \ No newline at end of file diff --git a/api/src/main/proto/WalletFormat.proto b/api/src/main/proto/WalletFormat.proto new file mode 100644 index 00000000..46c477ba --- /dev/null +++ b/api/src/main/proto/WalletFormat.proto @@ -0,0 +1,31 @@ +/* + * Copyright 2013 bits of proof zrt. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.bitsofproof.supernode.wallet; + +message SimpleWallet { + required uint32 bcsapiversion = 1; + required bytes encryptedSeed = 2; + required bytes signature = 3; + message Account + { + required string publicKey = 1; + required uint64 created = 2; + required string name = 3; + optional uint32 firstIndex = 4; + } + repeated Account accounts = 4; +} \ No newline at end of file diff --git a/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java b/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java index f4b2c7ea..4d364921 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java @@ -40,6 +40,7 @@ import com.bitsofproof.supernode.common.ByteUtils; import com.bitsofproof.supernode.common.ECKeyPair; import com.bitsofproof.supernode.common.ECPublicKey; +import com.bitsofproof.supernode.common.ExtendedKey; import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ValidationException; diff --git a/api/src/test/java/com/bitsofproof/supernode/api/KeySerializerTest.java b/api/src/test/java/com/bitsofproof/supernode/api/KeySerializerTest.java index b9f46dfe..852b8a8b 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/KeySerializerTest.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/KeySerializerTest.java @@ -32,6 +32,8 @@ import com.bitsofproof.supernode.common.ECKeyPair; import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.wallet.AddressConverter; +import com.bitsofproof.supernode.wallet.KeyFormatter; public class KeySerializerTest { diff --git a/pom.xml b/pom.xml index e7a35428..90fd341c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.2.5 + 1.2.6 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/bop-service.sh b/server/bop-service.sh index 53c3c0f8..3ad1ea52 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.5.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.6.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.6.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" diff --git a/server/pom.xml b/server/pom.xml index 4ac84d4c..9df3f297 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.5 + 1.2.6 ../ diff --git a/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java index cdcd97e5..8c5a74c2 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java @@ -18,10 +18,10 @@ import java.util.List; import java.util.Set; -import com.bitsofproof.supernode.api.ExtendedKey; import com.bitsofproof.supernode.common.BloomFilter; import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.common.ByteVector; +import com.bitsofproof.supernode.common.ExtendedKey; import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.model.Blk; import com.bitsofproof.supernode.model.Tx; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index c6a6f8b1..41c60660 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -34,11 +34,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.bitsofproof.supernode.api.ExtendedKey; import com.bitsofproof.supernode.common.BloomFilter; import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.common.ByteUtils; import com.bitsofproof.supernode.common.ByteVector; +import com.bitsofproof.supernode.common.ExtendedKey; import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.ScriptFormat; import com.bitsofproof.supernode.common.ScriptFormat.Token; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index 6fe77226..16f16f73 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -39,11 +39,11 @@ import com.bitsofproof.supernode.api.BCSAPIMessage; import com.bitsofproof.supernode.api.Block; -import com.bitsofproof.supernode.api.ExtendedKey; import com.bitsofproof.supernode.api.Transaction; import com.bitsofproof.supernode.api.TrunkUpdateMessage; import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.common.ByteVector; +import com.bitsofproof.supernode.common.ExtendedKey; import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.common.WireFormat; diff --git a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java new file mode 100644 index 00000000..e1eb5e6d --- /dev/null +++ b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java @@ -0,0 +1,265 @@ +/* + * Copyright 2013 bits of proof zrt. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bitsofproof.supernode.test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.Security; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.bitsofproof.supernode.api.BCSAPI; +import com.bitsofproof.supernode.api.BCSAPIException; +import com.bitsofproof.supernode.api.Block; +import com.bitsofproof.supernode.api.Transaction; +import com.bitsofproof.supernode.api.TransactionListener; +import com.bitsofproof.supernode.api.TrunkListener; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.core.BlockStore; +import com.bitsofproof.supernode.core.Chain; +import com.bitsofproof.supernode.core.Difficulty; +import com.bitsofproof.supernode.wallet.AccountListener; +import com.bitsofproof.supernode.wallet.AccountManager; +import com.bitsofproof.supernode.wallet.ExtendedKeySetWallet; +import com.bitsofproof.supernode.wallet.Wallet; + +@RunWith (SpringJUnit4ClassRunner.class) +@ContextConfiguration (locations = { "/context/memory-store.xml", "/context/EmbeddedBCSAPI.xml" }) +public class APITest +{ + @Autowired + BlockStore store; + + @Autowired + Chain chain; + + @Autowired + BCSAPI api; + + private static final long COIN = 100000000L; + private static final long FEE = COIN / 1000L; + + private static class AccountMonitor implements AccountListener + { + private final Semaphore ready = new Semaphore (0); + private final AccountManager account; + + public AccountMonitor (AccountManager account) + { + this.account = account; + } + + @Override + public void accountChanged (AccountManager account, Transaction t) + { + assertTrue (this.account == account); + ready.release (); + } + + public void expectUpdates (int n) + { + try + { + assertTrue (ready.tryAcquire (n, 1, TimeUnit.SECONDS)); + assertFalse (ready.tryAcquire ()); + } + catch ( InterruptedException e ) + { + } + } + } + + public static class ValidationMonitor implements TrunkListener, TransactionListener + { + private final Semaphore blockValidated = new Semaphore (0); + private final Semaphore transactionValidated = new Semaphore (0); + private String last; + + @Override + public void trunkUpdate (List removed, List added) + { + if ( added != null ) + { + for ( Block b : added ) + { + b.computeHash (); + last = b.getHash (); + blockValidated.release (); + } + } + } + + public String getLast () + { + return last; + } + + public void expectBlockValidations (int n) + { + try + { + assertTrue (blockValidated.tryAcquire (n, 1, TimeUnit.SECONDS)); + assertFalse (blockValidated.tryAcquire ()); + } + catch ( InterruptedException e ) + { + } + } + + public void expectTransactionValidations (int n) + { + try + { + assertTrue (transactionValidated.tryAcquire (n, 1, TimeUnit.SECONDS)); + assertFalse (transactionValidated.tryAcquire ()); + } + catch ( InterruptedException e ) + { + } + } + + @Override + public void process (Transaction t) + { + transactionValidated.release (); + } + } + + @BeforeClass + public static void provider () + { + Security.addProvider (new BouncyCastleProvider ()); + } + + @Test + public void test () throws BCSAPIException, ValidationException, IOException + { + Map blocks = new HashMap (); + + Wallet wallet = new ExtendedKeySetWallet (); + AccountManager alice = wallet.createAccountManager ("Alice"); + AccountManager bob = wallet.createAccountManager ("Bob"); + + AccountMonitor bobMonitor = new AccountMonitor (bob); + AccountMonitor aliceMonitor = new AccountMonitor (alice); + ValidationMonitor validationMonitor = new ValidationMonitor (); + + store.resetStore (chain); + store.cache (chain, 0); + + api.registerTransactionListener (alice); + api.registerTransactionListener (bob); + + alice.addAccountListener (aliceMonitor); + bob.addAccountListener (bobMonitor); + + api.registerTrunkListener (validationMonitor); + api.registerTransactionListener (validationMonitor); + + // check genesis + String genesisHash = chain.getGenesis ().getHash (); + assertTrue (api.getBlock (genesisHash).getHash ().equals (genesisHash)); + + // send 1 block + Block block = createBlock (chain.getGenesis ().getHash (), Transaction.createCoinbase (alice.getNextKey (), 50 * COIN, 1)); + mineBlock (block); + blocks.put (1, block); + + api.sendBlock (block); + + validationMonitor.expectBlockValidations (1); + assertTrue (validationMonitor.getLast ().equals (block.getHash ())); + validationMonitor.expectTransactionValidations (1); + aliceMonitor.expectUpdates (1); + + // send 10 blocks + String hash = blocks.get (1).getHash (); + for ( int i = 0; i < 10; ++i ) + { + block = createBlock (hash, Transaction.createCoinbase (alice.getNextKey (), 5000000000L, i + 2)); + block.setCreateTime (block.getCreateTime () + (i + 1) * 1000); // avoid clash of timestamp with median + mineBlock (block); + blocks.put (i + 2, block); + hash = block.getHash (); + api.sendBlock (block); + } + validationMonitor.expectBlockValidations (10); + assertTrue (validationMonitor.getLast ().equals (hash)); + aliceMonitor.expectUpdates (10); + validationMonitor.expectTransactionValidations (10); + + // spend some + long aliceStartingBalance = alice.getBalance (); + Transaction spend = alice.pay (bob.getNextKey ().getAddress (), 50 * COIN, FEE); + api.sendTransaction (spend); + aliceMonitor.expectUpdates (1); + bobMonitor.expectUpdates (1); + validationMonitor.expectTransactionValidations (1); + assertTrue (bob.getBalance () == 50 * COIN); + assertTrue (alice.getBalance () == aliceStartingBalance - bob.getBalance () - FEE); + + // split + aliceStartingBalance = alice.getBalance (); + spend = alice.split (new long[] { 1 * COIN, 2 * COIN }, FEE); + api.sendTransaction (spend); + aliceMonitor.expectUpdates (1); + validationMonitor.expectTransactionValidations (1); + assertTrue (alice.getBalance () == aliceStartingBalance - FEE); + } + + private Block createBlock (String previous, Transaction coinbase) + { + Block block = new Block (); + block.setCreateTime (System.currentTimeMillis () / 1000); + block.setDifficultyTarget (chain.getGenesis ().getDifficultyTarget ()); + block.setPreviousHash (previous); + block.setVersion (2); + block.setNonce (0); + block.setTransactions (new ArrayList ()); + block.getTransactions ().add (coinbase); + return block; + } + + private void mineBlock (Block b) + { + for ( int nonce = Integer.MIN_VALUE; nonce <= Integer.MAX_VALUE; ++nonce ) + { + b.setNonce (nonce); + b.computeHash (); + BigInteger hashAsInteger = new Hash (b.getHash ()).toBigInteger (); + if ( hashAsInteger.compareTo (Difficulty.getTarget (b.getDifficultyTarget ())) <= 0 ) + { + break; + } + } + } +} \ No newline at end of file diff --git a/server/src/test/java/com/bitsofproof/supernode/test/AddressTest.java b/server/src/test/java/com/bitsofproof/supernode/test/AddressTest.java index 7a35248d..a09e650f 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/AddressTest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/AddressTest.java @@ -24,9 +24,9 @@ import org.junit.Test; -import com.bitsofproof.supernode.api.AddressConverter; import com.bitsofproof.supernode.common.ByteUtils; import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.wallet.AddressConverter; public class AddressTest { From 9fe398b5a7f57979bc8e98add118cb2e56c2fb60 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 7 Sep 2013 14:55:52 +0200 Subject: [PATCH 184/299] removed misleading comment --- .../com/bitsofproof/supernode/test/BitcoinJBlockTest.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/server/src/test/java/com/bitsofproof/supernode/test/BitcoinJBlockTest.java b/server/src/test/java/com/bitsofproof/supernode/test/BitcoinJBlockTest.java index ccce922d..42891bd3 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/BitcoinJBlockTest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/BitcoinJBlockTest.java @@ -41,13 +41,6 @@ import com.bitsofproof.supernode.model.TxIn; import com.bitsofproof.supernode.model.TxOut; -/* - * This test will not run automaticaly with maven since - * it requires significant resources. - * Run it with at least 1GB of heap otherwise memomry database overflows. - * Run it on a strong machine, it does multithreaded validation - * for thousands of signatures. - */ @RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (locations = { "/context/memory-store.xml" }) public class BitcoinJBlockTest From b31f7983dbd83b8b3ef89bb427bf1547c7792102 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 7 Sep 2013 17:37:42 +0200 Subject: [PATCH 185/299] new product names --- README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 512fd16c..01ce0dc0 100644 --- a/README.md +++ b/README.md @@ -11,13 +11,9 @@ Bits of Proof offers professionally hosted server instances for commercial use, * BOP Enterprise Bitcoin Server - for very high volume multiple and hierarchical wallet support. Don't despair with the 'reference client's geeky RPC calls design and poor performance. Use our intuitive, business friendly, high performance API and spend rather to develop your own business case. -* BOP Mobile Bitcoin Server - back mobile wallets or any mobile application sending or receiving Bitcoin. We give you the highest performance mobile Bitcoin service with the smallest memory and traffic footprint on the device. See the example port of the popular Android Bitcoin wallet BitcoinSpinner at https://github.com/bitsofproof/bop-bitcoinspinner +* BopShop - Payment processor to accept Bitcoin on a website or in Person. Available both hosted by BOP or with white label to start your own Bitcoin payment processor business. -* BOP Merchant Bitcoin Server - pay or issue payment requests, get notification on payments. Deal with your own Bitcoin, save commissions. - -* BOP Audit Bitcoin Server - data mine the Bitcoin transaction history and get real-time feed of network events. Best for forensic research, no hassle to get high quality data. - -* BOP ERP Bitcoin Server - initiate and monitor Bitcoin transactions with your ERP system. The next generation of service - stay tuned. +* BeBop - Simple but powerful mobile wallet best integrated with BopShop. _Use our ready to go hosted BOP Server instance! Get an evaluation access, attractive pricing based on resources used or transactions processed from sales@bitsofproof.com_ From fa2bf28d9677c9e598dd693037ce6c0efe17e15e Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 5 Oct 2013 19:41:47 +0200 Subject: [PATCH 186/299] only set use flag in output cache if validating within a block. --- .../bitsofproof/supernode/core/CachedBlockStore.java | 10 +++++++++- .../java/com/bitsofproof/supernode/core/TxHandler.java | 1 - 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 41c60660..695bc9c1 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -1348,7 +1348,15 @@ private void checkInputResolution (TxOutCache resolvedInputs, int height, Tx t) { if ( !i.getSourceHash ().equals (Hash.ZERO_HASH_STRING) ) { - TxOut out = resolvedInputs.use (i.getSourceHash (), i.getIx ()); + TxOut out = null; + if ( height != 0 ) + { + out = resolvedInputs.use (i.getSourceHash (), i.getIx ()); + } + else + { + out = resolvedInputs.get (i.getSourceHash (), i.getIx ()); + } if ( out == null ) { throw new ValidationException ("Transaction refers to unknown or spent output " + i.getSourceHash () + " [" + i.getIx () + "] " diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index 97018adf..cfa3528d 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -322,7 +322,6 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo notifyListener (tx, true); } } - availableOutput.resetUse (); for ( Tx tx : firstSeenInBlock ) { notifyListener (tx, false); From 8f3d787a55984e1fdad8770e8280251dd45b044a Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 5 Oct 2013 21:47:21 +0200 Subject: [PATCH 187/299] bump to 1.2.7 --- api/pom.xml | 2 +- pom.xml | 2 +- server/bop-service.sh | 4 ++-- server/pom.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 168c69d1..8adfe8bb 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.6 + 1.2.7 ../ diff --git a/pom.xml b/pom.xml index 90fd341c..8c87f78e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.2.6 + 1.2.7 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/bop-service.sh b/server/bop-service.sh index 3ad1ea52..279fac7f 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.6.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.7.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.6.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.7.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" diff --git a/server/pom.xml b/server/pom.xml index 9df3f297..89cb0da3 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.6 + 1.2.7 ../ From 88c882ca778315695fb4ce0ce87bcc68661713f2 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 23 Oct 2013 12:24:43 +0200 Subject: [PATCH 188/299] change log pattern to ISO date --- server/src/main/resources/log4j.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/main/resources/log4j.properties b/server/src/main/resources/log4j.properties index 80c9f48f..384535a5 100644 --- a/server/src/main/resources/log4j.properties +++ b/server/src/main/resources/log4j.properties @@ -2,11 +2,11 @@ log4j.rootCategory=WARN, S, F log4j.logger.com.bitsofproof.supernode=DEBUG log4j.appender.S = org.apache.log4j.ConsoleAppender log4j.appender.S.layout = org.apache.log4j.PatternLayout -log4j.appender.S.layout.ConversionPattern = %r [%p] %c{1} %t %m%n +log4j.appender.S.layout.ConversionPattern = %d [%p] %c{1} %t %m%n log4j.appender.S.Threshold = INFO log4j.appender.F = org.apache.log4j.RollingFileAppender log4j.appender.F.file = logs/bop-server.log log4j.appender.F.MaxFileSize=100MB log4j.appender.F.MaxBackupIndex=10 log4j.appender.F.layout = org.apache.log4j.PatternLayout -log4j.appender.F.layout.ConversionPattern = %r [%p] %c{1} %t %m%n +log4j.appender.F.layout.ConversionPattern = %d [%p] %c{1} %t %m%n From 381072e6b4cfc124e86bc21cd176afca3b136472 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 30 Oct 2013 19:35:03 +0100 Subject: [PATCH 189/299] add support for P2SH --- api/pom.xml | 2 +- .../supernode/api/TransactionOutput.java | 12 ++ .../supernode/wallet/AccountManager.java | 8 ++ .../bitsofproof/supernode/wallet/Address.java | 61 ++++++++++ .../supernode/wallet/AddressConverter.java | 93 +++++++++++++++ .../supernode/wallet/BaseAccountManager.java | 109 +++++++++++------- pom.xml | 2 +- server/pom.xml | 2 +- 8 files changed, 247 insertions(+), 42 deletions(-) create mode 100644 api/src/main/java/com/bitsofproof/supernode/wallet/Address.java diff --git a/api/pom.xml b/api/pom.xml index 8adfe8bb..d3e477fd 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.7 + 1.2-SNAPSHOT ../ diff --git a/api/src/main/java/com/bitsofproof/supernode/api/TransactionOutput.java b/api/src/main/java/com/bitsofproof/supernode/api/TransactionOutput.java index d5d0a09c..9443c5bf 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/TransactionOutput.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/TransactionOutput.java @@ -117,6 +117,18 @@ public byte[] getOutputAddress () { } } + else if ( ScriptFormat.isPayToScriptHash (script) ) + { + List tokens; + try + { + tokens = ScriptFormat.parse (script); + return tokens.get (1).data; + } + catch ( ValidationException e ) + { + } + } return null; } diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java index 97b3a0eb..1f3a7ce3 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java @@ -51,12 +51,20 @@ public interface UTXO public Key getNextKey () throws ValidationException; + public Address getNextAddress () throws ValidationException; + public Key getKeyForAddress (byte[] address); public Collection getAddresses (); + public Transaction pay (Address receiver, long amount, long fee) throws ValidationException; + + public Transaction pay (Address receiver, long amount) throws ValidationException; + + @Deprecated public Transaction pay (byte[] receiver, long amount, long fee) throws ValidationException; + @Deprecated public Transaction pay (byte[] receiver, long amount) throws ValidationException; public Transaction split (long[] amounts, long fee) throws ValidationException; diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/Address.java b/api/src/main/java/com/bitsofproof/supernode/wallet/Address.java new file mode 100644 index 00000000..219960da --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/Address.java @@ -0,0 +1,61 @@ +package com.bitsofproof.supernode.wallet; + +import org.bouncycastle.util.Arrays; + +import com.bitsofproof.supernode.common.ValidationException; + +public class Address +{ + public enum Type + { + UNKNOWN, COMMON, P2SH + }; + + public enum Network + { + UNKNOWN, PRODUCTION, TEST + } + + private final Network network; + private final Type type; + private final byte[] address; + + public Address (Network network, Type type, byte[] address) throws ValidationException + { + this.type = type; + if ( address.length != 20 ) + { + throw new ValidationException ("invalid digest length for an address"); + } + this.address = Arrays.clone (address); + this.network = network; + } + + public Network getNetwork () + { + return network; + } + + public Type getType () + { + return type; + } + + public byte[] getAddress () + { + return Arrays.clone (address); + } + + @Override + public String toString () + { + try + { + return AddressConverter.toSatoshiStyle (this); + } + catch ( ValidationException e ) + { + return e.getMessage (); + } + } +} diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/AddressConverter.java b/api/src/main/java/com/bitsofproof/supernode/wallet/AddressConverter.java index 640a1031..b8df385f 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/AddressConverter.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/AddressConverter.java @@ -18,9 +18,56 @@ import com.bitsofproof.supernode.common.ByteUtils; import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.wallet.Address.Network; +import com.bitsofproof.supernode.wallet.Address.Type; public class AddressConverter { + public static Address fromSatoshiStyle (String s) throws ValidationException + { + try + { + Address.Network network = Network.UNKNOWN; + Address.Type type = Type.UNKNOWN; + byte[] raw = ByteUtils.fromBase58 (s); + if ( (raw[0] & 0xff) == 0x0 ) + { + network = Address.Network.PRODUCTION; + type = Address.Type.COMMON; + } + if ( (raw[0] & 0xff) == 5 ) + { + network = Address.Network.PRODUCTION; + type = Address.Type.P2SH; + } + if ( (raw[0] & 0xff) == 0x6f ) + { + network = Address.Network.TEST; + type = Address.Type.COMMON; + } + if ( (raw[0] & 0xff) == 196 ) + { + network = Address.Network.TEST; + type = Address.Type.P2SH; + } + byte[] check = Hash.hash (raw, 0, raw.length - 4); + for ( int i = 0; i < 4; ++i ) + { + if ( check[i] != raw[raw.length - 4 + i] ) + { + throw new ValidationException ("Address checksum mismatch"); + } + } + byte[] keyDigest = new byte[raw.length - 5]; + System.arraycopy (raw, 1, keyDigest, 0, raw.length - 5); + return new Address (network, type, keyDigest); + } + catch ( Exception e ) + { + throw new ValidationException (e); + } + } + public static byte[] fromSatoshiStyle (String s, int addressFlag) throws ValidationException { try @@ -57,4 +104,50 @@ public static String toSatoshiStyle (byte[] keyDigest, int addressFlag) System.arraycopy (check, 0, addressBytes, keyDigest.length + 1, 4); return ByteUtils.toBase58 (addressBytes); } + + public static String toSatoshiStyle (Address address) throws ValidationException + { + byte[] keyDigest = address.getAddress (); + int addressFlag; + if ( address.getNetwork () == Address.Network.PRODUCTION ) + { + if ( address.getType () == Address.Type.COMMON ) + { + addressFlag = 0x0; + } + else if ( address.getType () == Address.Type.P2SH ) + { + addressFlag = 0x5; + } + else + { + throw new ValidationException ("unknown address type"); + } + } + else if ( address.getNetwork () == Address.Network.TEST ) + { + if ( address.getType () == Address.Type.COMMON ) + { + addressFlag = 0x6f; + } + else if ( address.getType () == Address.Type.P2SH ) + { + addressFlag = 196; + } + else + { + throw new ValidationException ("unknown address type"); + } + } + else + { + throw new ValidationException ("unknown network"); + } + byte[] addressBytes = new byte[1 + keyDigest.length + 4]; + addressBytes[0] = (byte) (addressFlag & 0xff); + System.arraycopy (keyDigest, 0, addressBytes, 1, keyDigest.length); + byte[] check = Hash.hash (addressBytes, 0, keyDigest.length + 1); + System.arraycopy (check, 0, addressBytes, keyDigest.length + 1, 4); + return ByteUtils.toBase58 (addressBytes); + } } diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java index cc87b8e0..0b53578e 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java @@ -14,7 +14,6 @@ import java.util.Map; import java.util.Set; -import org.bouncycastle.util.Arrays; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -85,19 +84,19 @@ public boolean isOwnAddress (byte[] address) protected static class TransactionSink { - private final byte[] address; + private final Address address; private final long value; - public TransactionSink (byte[] address, long value) + public TransactionSink (Address address, long value) { super (); - this.address = Arrays.clone (address); + this.address = address; this.value = value; } - public byte[] getAddress () + public Address getAddress () { - return Arrays.clone (address); + return address; } public long getValue () @@ -131,15 +130,24 @@ protected Transaction createSpend (List sources, List sources, List sources, List sources = getSufficientSources (amount, fee, null); if ( sources == null ) { @@ -368,8 +390,8 @@ public Transaction pay (byte[] receiver, long amount, long fee) throws Validatio TransactionSink target = new TransactionSink (receiver, amount); if ( (in - amount - fee) > 0 ) { - TransactionSink change = new TransactionSink (getNextKey ().getAddress (), in - amount - fee); - log.trace ("change to " + ByteUtils.toHex (change.getAddress ()) + " " + change.getValue ()); + TransactionSink change = new TransactionSink (getNextAddress (), in - amount - fee); + log.trace ("change to " + change.getAddress () + " " + change.getValue ()); if ( new SecureRandom ().nextBoolean () ) { sinks.add (target); @@ -390,7 +412,13 @@ public Transaction pay (byte[] receiver, long amount, long fee) throws Validatio } @Override - public Transaction pay (byte[] receiver, long amount) throws ValidationException + public Transaction pay (byte[] receiver, long amount, long fee) throws ValidationException + { + return pay (new Address (Address.Network.PRODUCTION, Address.Type.COMMON, receiver), amount, fee); + } + + @Override + public Transaction pay (Address receiver, long amount) throws ValidationException { long fee = MINIMUM_FEE; long estimate = 0; @@ -410,6 +438,12 @@ public Transaction pay (byte[] receiver, long amount) throws ValidationException return t; } + @Override + public Transaction pay (byte[] receiver, long amount) throws ValidationException + { + return pay (new Address (Address.Network.PRODUCTION, Address.Type.COMMON, receiver), amount); + } + @Override public Transaction split (long[] amounts, long fee) throws ValidationException { @@ -434,12 +468,12 @@ public Transaction split (long[] amounts, long fee) throws ValidationException } for ( long a : amounts ) { - TransactionSink target = new TransactionSink (getNextKey ().getAddress (), a); + TransactionSink target = new TransactionSink (getNextAddress (), a); sinks.add (target); } if ( (in - amount - fee) > 0 ) { - TransactionSink change = new TransactionSink (getNextKey ().getAddress (), in - amount - fee); + TransactionSink change = new TransactionSink (getNextAddress (), in - amount - fee); sinks.add (change); } Collections.shuffle (sinks); @@ -503,14 +537,12 @@ public boolean updateWithTransaction (Transaction t) if ( spend != null ) { change.add (o); - log.trace ("Change " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " - + o.getValue ()); + log.trace ("Change " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + o.getValue ()); } else { receiving.add (o); - log.trace ("Receiving " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " - + o.getValue ()); + log.trace ("Receiving " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + o.getValue ()); } } } @@ -549,8 +581,7 @@ public boolean updateWithTransaction (Transaction t) } if ( out != null ) { - log.trace ("Remove DS " + out.getTxHash () + " [" + out.getIx () + "] (" + ByteUtils.toHex (out.getOutputAddress ()) + ")" - + out.getValue ()); + log.trace ("Remove DS " + out.getTxHash () + " [" + out.getIx () + "] (" + ByteUtils.toHex (out.getOutputAddress ()) + ")" + out.getValue ()); } modified |= out != null; } diff --git a/pom.xml b/pom.xml index 8c87f78e..8a89f949 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.2.7 + 1.2-SNAPSHOT supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/pom.xml b/server/pom.xml index 89cb0da3..5512f57e 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.7 + 1.2-SNAPSHOT ../ From ba3ee29566f6df1cba71ed2ecd5d7523a10c41af Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 1 Nov 2013 10:02:34 +0100 Subject: [PATCH 190/299] make hash public --- .../com/bitsofproof/supernode/wallet/BaseAccountManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java index 0b53578e..c2accc9c 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java @@ -205,7 +205,7 @@ protected void spendNonAddressOutput (TransactionOutput o, ScriptFormat.Writer w throw new ValidationException ("Can not spend this output type"); } - protected static byte[] hashTransaction (Transaction transaction, int inr, int hashType, byte[] script) throws ValidationException + public static byte[] hashTransaction (Transaction transaction, int inr, int hashType, byte[] script) throws ValidationException { Transaction copy = null; try From efb8834a564bf47831c84c385993a06dd9a0555c Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 2 Nov 2013 22:13:55 +0100 Subject: [PATCH 191/299] Added server in a box for tests --- .../supernode/testbox/APIServerInABox.java | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java diff --git a/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java b/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java new file mode 100644 index 00000000..bf33004e --- /dev/null +++ b/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java @@ -0,0 +1,91 @@ +package com.bitsofproof.supernode.testbox; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.ArrayList; + +import com.bitsofproof.supernode.api.BCSAPI; +import com.bitsofproof.supernode.api.Block; +import com.bitsofproof.supernode.api.JMSServerConnector; +import com.bitsofproof.supernode.api.Transaction; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.InMemoryBusConnectionFactory; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.core.BitcoinNetwork; +import com.bitsofproof.supernode.core.Chain; +import com.bitsofproof.supernode.core.Difficulty; +import com.bitsofproof.supernode.core.FixedAddressDiscovery; +import com.bitsofproof.supernode.core.ImplementBCSAPI; +import com.bitsofproof.supernode.core.TxHandler; +import com.bitsofproof.supernode.model.LvlMemoryStore; +import com.bitsofproof.supernode.model.LvlStore; + +public class APIServerInABox +{ + private final BitcoinNetwork network; + private final InMemoryBusConnectionFactory connectionFactory = new InMemoryBusConnectionFactory (); + private final Chain chain; + + public APIServerInABox (Chain chain) throws IOException, ValidationException + { + this.chain = chain; + LvlStore store = new LvlStore (); + store.setStore (new LvlMemoryStore ()); + store.resetStore (chain); + store.cache (chain, 0); + FixedAddressDiscovery discovery = new FixedAddressDiscovery (); + discovery.setConnectTo ("localhost"); + network = new BitcoinNetwork (0); + network.setChain (chain); + network.setStore (store); + network.setListen (false); + network.setDiscovery (discovery); + TxHandler txhandler = new TxHandler (network); + ImplementBCSAPI bcsapi = new ImplementBCSAPI (network, txhandler); + bcsapi.setConnectionFactory (connectionFactory); + bcsapi.init (); + } + + public BCSAPI getAPI () + { + JMSServerConnector api = new JMSServerConnector (); + api.setConnectionFactory (connectionFactory); + api.init (); + return api; + } + + public Block createBlock (String previous, Transaction coinbase) + { + Block block = new Block (); + try + { + Thread.sleep (1001); + } + catch ( InterruptedException e ) + { + } + block.setCreateTime (System.currentTimeMillis () / 1000); + block.setDifficultyTarget (chain.getGenesis ().getDifficultyTarget ()); + block.setPreviousHash (previous); + block.setVersion (2); + block.setNonce (0); + block.setTransactions (new ArrayList ()); + block.getTransactions ().add (coinbase); + return block; + } + + public void mineBlock (Block b) + { + for ( int nonce = Integer.MIN_VALUE; nonce <= Integer.MAX_VALUE; ++nonce ) + { + b.setNonce (nonce); + b.computeHash (); + BigInteger hashAsInteger = new Hash (b.getHash ()).toBigInteger (); + if ( hashAsInteger.compareTo (Difficulty.getTarget (b.getDifficultyTarget ())) <= 0 ) + { + break; + } + } + } + +} From a7a53a6ec3909e9d205cb15fa0f5b8d6c972d869 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 2 Nov 2013 22:26:32 +0100 Subject: [PATCH 192/299] add pay to multiple destinations --- .../supernode/wallet/AccountManager.java | 4 ++ .../supernode/wallet/BaseAccountManager.java | 56 ++++++++++++------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java index 1f3a7ce3..1f88e103 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java @@ -61,6 +61,10 @@ public interface UTXO public Transaction pay (Address receiver, long amount) throws ValidationException; + public Transaction pay (List
receiver, List amount, long fee) throws ValidationException; + + public Transaction pay (List
receiver, List amounts) throws ValidationException; + @Deprecated public Transaction pay (byte[] receiver, long amount, long fee) throws ValidationException; diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java index c2accc9c..572415cc 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java @@ -3,13 +3,13 @@ import java.io.PrintStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -370,43 +370,49 @@ public Address getNextAddress () throws ValidationException @Override public Transaction pay (Address receiver, long amount, long fee) throws ValidationException + { + List
a = new ArrayList
(); + a.add (receiver); + List v = new ArrayList (); + v.add (amount); + return pay (a, v, fee); + } + + @Override + public Transaction pay (List
receiver, List amounts, long fee) throws ValidationException { synchronized ( confirmed ) { - log.trace ("pay to " + receiver.getAddress () + " " + amount + " + " + fee); + long amount = 0; + for ( Long a : amounts ) + { + amount += a; + } + log.trace ("pay " + amount + " + " + fee); List sources = getSufficientSources (amount, fee, null); if ( sources == null ) { throw new ValidationException ("Insufficient funds to pay " + (amount + fee)); } - List sinks = new ArrayList (); - long in = 0; for ( TransactionOutput o : sources ) { log.trace ("using input " + o.getTxHash () + "[" + o.getIx () + "] " + o.getValue ()); in += o.getValue (); } - TransactionSink target = new TransactionSink (receiver, amount); + List sinks = new ArrayList (); + Iterator ai = amounts.iterator (); + for ( Address r : receiver ) + { + sinks.add (new TransactionSink (r, ai.next ())); + } if ( (in - amount - fee) > 0 ) { TransactionSink change = new TransactionSink (getNextAddress (), in - amount - fee); log.trace ("change to " + change.getAddress () + " " + change.getValue ()); - if ( new SecureRandom ().nextBoolean () ) - { - sinks.add (target); - sinks.add (change); - } - else - { - sinks.add (change); - sinks.add (target); - } - } - else - { - sinks.add (target); + sinks.add (change); } + Collections.shuffle (sinks); return createSpend (sources, sinks, fee); } } @@ -419,6 +425,16 @@ public Transaction pay (byte[] receiver, long amount, long fee) throws Validatio @Override public Transaction pay (Address receiver, long amount) throws ValidationException + { + List
a = new ArrayList
(); + a.add (receiver); + List v = new ArrayList (); + v.add (amount); + return pay (a, v); + } + + @Override + public Transaction pay (List
receiver, List amounts) throws ValidationException { long fee = MINIMUM_FEE; long estimate = 0; @@ -427,7 +443,7 @@ public Transaction pay (Address receiver, long amount) throws ValidationExceptio do { fee = Math.max (fee, estimate); - t = pay (receiver, amount, fee); + t = pay (receiver, amounts, fee); estimate = estimateFee (t); if ( fee < estimate ) { From fd7908367544452c4e1b209063079ca14381c64d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1rtfai=20Tam=C3=A1s?= Date: Sun, 3 Nov 2013 11:39:42 +0100 Subject: [PATCH 193/299] Add source jar to maven artifacts --- pom.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pom.xml b/pom.xml index 8a89f949..cc4f3972 100644 --- a/pom.xml +++ b/pom.xml @@ -118,6 +118,10 @@ + + org.apache.maven.plugins + maven-source-plugin + @@ -147,6 +151,18 @@ + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + From b6630898964aee8e3adfea0f3b51c3e2dfd5df48 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sun, 3 Nov 2013 14:30:22 +0100 Subject: [PATCH 194/299] indreoduce switch to recever or payer pays fee --- .../supernode/wallet/AccountManager.java | 6 ++-- .../supernode/wallet/BaseAccountManager.java | 32 ++++++++++++------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java index 1f88e103..93e77e06 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java @@ -59,11 +59,11 @@ public interface UTXO public Transaction pay (Address receiver, long amount, long fee) throws ValidationException; - public Transaction pay (Address receiver, long amount) throws ValidationException; + public Transaction pay (Address receiver, long amount, boolean senderPaysFee) throws ValidationException; - public Transaction pay (List
receiver, List amount, long fee) throws ValidationException; + public Transaction pay (List
receiver, List amount, long fee, boolean senderPaysFee) throws ValidationException; - public Transaction pay (List
receiver, List amounts) throws ValidationException; + public Transaction pay (List
receiver, List amounts, boolean senderPaysFee) throws ValidationException; @Deprecated public Transaction pay (byte[] receiver, long amount, long fee) throws ValidationException; diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java index 572415cc..e053e742 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java @@ -375,11 +375,11 @@ public Transaction pay (Address receiver, long amount, long fee) throws Validati a.add (receiver); List v = new ArrayList (); v.add (amount); - return pay (a, v, fee); + return pay (a, v, fee, true); } @Override - public Transaction pay (List
receiver, List amounts, long fee) throws ValidationException + public Transaction pay (List
receiver, List amounts, long fee, boolean senderPaysfee) throws ValidationException { synchronized ( confirmed ) { @@ -388,11 +388,19 @@ public Transaction pay (List
receiver, List amounts, long fee) th { amount += a; } - log.trace ("pay " + amount + " + " + fee); - List sources = getSufficientSources (amount, fee, null); + log.trace ("pay " + amount + (senderPaysfee ? " " + fee : "")); + List sources; + if ( senderPaysfee ) + { + sources = getSufficientSources (amount, fee, null); + } + else + { + sources = getSufficientSources (amount, 0, null); + } if ( sources == null ) { - throw new ValidationException ("Insufficient funds to pay " + (amount + fee)); + throw new ValidationException ("Insufficient funds to pay " + (amount + (senderPaysfee ? fee : 0))); } long in = 0; for ( TransactionOutput o : sources ) @@ -404,11 +412,11 @@ public Transaction pay (List
receiver, List amounts, long fee) th Iterator ai = amounts.iterator (); for ( Address r : receiver ) { - sinks.add (new TransactionSink (r, ai.next ())); + sinks.add (new TransactionSink (r, ai.next () - (!senderPaysfee ? (fee + receiver.size () - 1) / receiver.size () : 0))); } if ( (in - amount - fee) > 0 ) { - TransactionSink change = new TransactionSink (getNextAddress (), in - amount - fee); + TransactionSink change = new TransactionSink (getNextAddress (), in - amount - (senderPaysfee ? fee : 0)); log.trace ("change to " + change.getAddress () + " " + change.getValue ()); sinks.add (change); } @@ -424,17 +432,17 @@ public Transaction pay (byte[] receiver, long amount, long fee) throws Validatio } @Override - public Transaction pay (Address receiver, long amount) throws ValidationException + public Transaction pay (Address receiver, long amount, boolean senderPaysFee) throws ValidationException { List
a = new ArrayList
(); a.add (receiver); List v = new ArrayList (); v.add (amount); - return pay (a, v); + return pay (a, v, senderPaysFee); } @Override - public Transaction pay (List
receiver, List amounts) throws ValidationException + public Transaction pay (List
receiver, List amounts, boolean senderPaysfee) throws ValidationException { long fee = MINIMUM_FEE; long estimate = 0; @@ -443,7 +451,7 @@ public Transaction pay (List
receiver, List amounts) throws Valid do { fee = Math.max (fee, estimate); - t = pay (receiver, amounts, fee); + t = pay (receiver, amounts, fee, senderPaysfee); estimate = estimateFee (t); if ( fee < estimate ) { @@ -457,7 +465,7 @@ public Transaction pay (List
receiver, List amounts) throws Valid @Override public Transaction pay (byte[] receiver, long amount) throws ValidationException { - return pay (new Address (Address.Network.PRODUCTION, Address.Type.COMMON, receiver), amount); + return pay (new Address (Address.Network.PRODUCTION, Address.Type.COMMON, receiver), amount, true); } @Override From e5e6a50c4367c7fe5bed7b48cfbe531033199812 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sun, 3 Nov 2013 18:10:45 +0100 Subject: [PATCH 195/299] reset account manager storage if sync-ed --- .../wallet/AddressListAccountManager.java | 2 ++ .../supernode/wallet/BaseAccountManager.java | 16 ++++++++++++---- .../wallet/ExtendedKeyAccountManager.java | 2 ++ .../supernode/wallet/KeyListAccountManager.java | 2 ++ 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/AddressListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/AddressListAccountManager.java index 830e6a30..8d5c1f53 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/AddressListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/AddressListAccountManager.java @@ -61,6 +61,7 @@ public void addAddress (byte[] address) @Override public void syncHistory (BCSAPI api) throws BCSAPIException { + reset (); log.trace ("Sync naddr: " + addresses.size ()); api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () { @@ -76,6 +77,7 @@ public void process (Transaction t) @Override public void sync (BCSAPI api) throws BCSAPIException { + reset (); log.trace ("Sync naddr: " + addresses.size ()); api.scanUTXO (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () { diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java index e053e742..ab0337f7 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java @@ -36,10 +36,10 @@ public abstract class BaseAccountManager implements AccountManager private static final long MINIMUM_FEE = 10000; private static final long MAXIMUM_FEE = 1000000; - private final UTXO confirmed = createConfirmedUTXO (); - private final UTXO change = createChangeUTXO (); - private final UTXO receiving = createReceivingUTXO (); - private final UTXO sending = createSendingUTXO (); + private UTXO confirmed = createConfirmedUTXO (); + private UTXO change = createChangeUTXO (); + private UTXO receiving = createReceivingUTXO (); + private UTXO sending = createSendingUTXO (); private long created; @@ -77,6 +77,14 @@ protected UTXO createReceivingUTXO () return new InMemoryUTXO (); } + protected void reset () + { + confirmed = createConfirmedUTXO (); + change = createChangeUTXO (); + receiving = createReceivingUTXO (); + sending = createSendingUTXO (); + } + public boolean isOwnAddress (byte[] address) { return getKeyForAddress (address) != null; diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeyAccountManager.java index d5d235c7..3d2fa13c 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeyAccountManager.java @@ -193,6 +193,7 @@ public void process (Transaction t) @Override public void syncHistory (BCSAPI api) throws BCSAPIException { + reset (); ensureLookAhead (0); log.trace ("Sync nkeys: " + (nextSequence - firstIndex)); api.scanTransactions (getMaster (), firstIndex, lookAhead, getCreated (), processor); @@ -207,6 +208,7 @@ public void syncHistory (BCSAPI api) throws BCSAPIException @Override public void sync (BCSAPI api) throws BCSAPIException { + reset (); ensureLookAhead (0); log.trace ("Sync nkeys: " + (nextSequence - firstIndex)); api.scanUTXO (getMaster (), firstIndex, lookAhead, getCreated (), processor); diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/KeyListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/KeyListAccountManager.java index a461fbc4..0d343947 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/KeyListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/KeyListAccountManager.java @@ -60,6 +60,7 @@ public void addKey (ECKeyPair key) @Override public void syncHistory (BCSAPI api) throws BCSAPIException { + reset (); log.trace ("Sync naddr: " + keys.size ()); api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () { @@ -75,6 +76,7 @@ public void process (Transaction t) @Override public void sync (BCSAPI api) throws BCSAPIException { + reset (); log.trace ("Sync naddr: " + keys.size ()); api.scanUTXO (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () { From db5bc0e5b7c4d106c942d777704ae2a1025da976 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sun, 3 Nov 2013 18:35:16 +0100 Subject: [PATCH 196/299] more generic interface for script redemption --- .../supernode/wallet/BaseAccountManager.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java index ab0337f7..99189e0a 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java @@ -177,19 +177,19 @@ else if ( s.getAddress ().getType () == Address.Type.P2SH ) } int j = 0; - for ( TransactionOutput o : sources ) + for ( TransactionOutput s : sources ) { TransactionInput i = transaction.getInputs ().get (j); ScriptFormat.Writer sw = new ScriptFormat.Writer (); - if ( ScriptFormat.isPayToAddress (o.getScript ()) ) + if ( ScriptFormat.isPayToAddress (s.getScript ()) ) { - byte[] address = o.getOutputAddress (); + byte[] address = s.getOutputAddress (); Key key = getKeyForAddress (address); if ( key == null ) { throw new ValidationException ("Have no key to spend this output"); } - byte[] sig = key.sign (hashTransaction (transaction, j, ScriptFormat.SIGHASH_ALL, o.getScript ())); + byte[] sig = key.sign (hashTransaction (transaction, j, ScriptFormat.SIGHASH_ALL, s.getScript ())); byte[] sigPlusType = new byte[sig.length + 1]; System.arraycopy (sig, 0, sigPlusType, 0, sig.length); sigPlusType[sigPlusType.length - 1] = (byte) (ScriptFormat.SIGHASH_ALL & 0xff); @@ -198,7 +198,7 @@ else if ( s.getAddress ().getType () == Address.Type.P2SH ) } else { - spendNonAddressOutput (o, sw, hashTransaction (transaction, j, ScriptFormat.SIGHASH_ALL, o.getScript ())); + spendNonAddressOutput (j, s, sw, transaction); } i.setScript (sw.toByteArray ()); ++j; @@ -208,7 +208,7 @@ else if ( s.getAddress ().getType () == Address.Type.P2SH ) return transaction; } - protected void spendNonAddressOutput (TransactionOutput o, ScriptFormat.Writer writer, byte[] digest) throws ValidationException + protected void spendNonAddressOutput (int ix, TransactionOutput source, ScriptFormat.Writer writer, Transaction transaction) throws ValidationException { throw new ValidationException ("Can not spend this output type"); } From 7fb86ca04938c78eab89d65e53ca6541808728ca Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sun, 3 Nov 2013 18:41:47 +0100 Subject: [PATCH 197/299] 1.2.8 --- api/pom.xml | 2 +- pom.xml | 2 +- server/bop-service.sh | 4 ++-- server/pom.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index d3e477fd..048ac9fe 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2-SNAPSHOT + 1.2.8 ../ diff --git a/pom.xml b/pom.xml index cc4f3972..bbb3b1d4 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.2-SNAPSHOT + 1.2.8 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/bop-service.sh b/server/bop-service.sh index 279fac7f..7d8727cf 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.7.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.8.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.7.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.8.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" diff --git a/server/pom.xml b/server/pom.xml index 5512f57e..0fb4289a 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2-SNAPSHOT + 1.2.8 ../ From cfa3babec5e4f4848597f3bdf94d4878f778f21a Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sun, 3 Nov 2013 19:03:47 +0100 Subject: [PATCH 198/299] avoid NPE if isProduction is not called. --- .../com/bitsofproof/supernode/api/JMSServerConnector.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java index 143b88c9..2ec6b3ae 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java @@ -336,8 +336,7 @@ public void scanTransactions (ExtendedKey master, int firstIndex, int lookAhead, scanRequest (master, firstIndex, lookAhead, after, listener, "accountRequest"); } - private void scanRequest (Collection match, UpdateMode mode, long after, final TransactionListener listener, String requestQueue) - throws BCSAPIException + private void scanRequest (Collection match, UpdateMode mode, long after, final TransactionListener listener, String requestQueue) throws BCSAPIException { Session session = null; try @@ -416,8 +415,7 @@ public void onMessage (Message message) } } - private void scanRequest (ExtendedKey master, int firstIndex, int lookAhead, long after, final TransactionListener listener, String request) - throws BCSAPIException + private void scanRequest (ExtendedKey master, int firstIndex, int lookAhead, long after, final TransactionListener listener, String request) throws BCSAPIException { if ( !master.isReadOnly () ) { @@ -432,7 +430,7 @@ private void scanRequest (ExtendedKey master, int firstIndex, int lookAhead, lon MessageProducer scanAccountProducer = session.createProducer (session.createQueue (request)); BCSAPIMessage.AccountRequest.Builder builder = BCSAPIMessage.AccountRequest.newBuilder (); builder.setBcsapiversion (1); - builder.setPublicKey (master.serialize (production)); + builder.setPublicKey (master.serialize (isProduction ())); builder.setLookAhead (lookAhead); builder.setAfter (after); m.writeBytes (builder.build ().toByteArray ()); From 681d30d86dc0279c7de2004446fc5b9ec7b1e0f8 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sun, 3 Nov 2013 20:07:56 +0100 Subject: [PATCH 199/299] safe sort of values while picking inputs --- .../com/bitsofproof/supernode/wallet/BaseAccountManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java index 99189e0a..2a24bc25 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java @@ -317,7 +317,7 @@ protected List getSufficientSources (long amount, long fee, S @Override public int compare (TransactionOutput o1, TransactionOutput o2) { - return (int) (o1.getValue () - o2.getValue ()); + return o1.getValue () < o2.getValue () ? -1 : o1.getValue () > o2.getValue () ? 1 : 0; } }); List changelist = new ArrayList (); @@ -329,7 +329,7 @@ public int compare (TransactionOutput o1, TransactionOutput o2) @Override public int compare (TransactionOutput o1, TransactionOutput o2) { - return (int) (o1.getValue () - o2.getValue ()); + return o1.getValue () < o2.getValue () ? -1 : o1.getValue () > o2.getValue () ? 1 : 0; } }); candidates.addAll (changelist); From 8c32c003b94c46f130ce031c823e8001a0ffbcb0 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Tue, 12 Nov 2013 14:37:23 +0100 Subject: [PATCH 200/299] move address script writer to Address class --- .../bitsofproof/supernode/wallet/Address.java | 26 +++++++++++++++++++ .../supernode/wallet/BaseAccountManager.java | 23 +--------------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/Address.java b/api/src/main/java/com/bitsofproof/supernode/wallet/Address.java index 219960da..f32e7350 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/Address.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/Address.java @@ -2,6 +2,8 @@ import org.bouncycastle.util.Arrays; +import com.bitsofproof.supernode.common.ScriptFormat; +import com.bitsofproof.supernode.common.ScriptFormat.Opcode; import com.bitsofproof.supernode.common.ValidationException; public class Address @@ -46,6 +48,30 @@ public byte[] getAddress () return Arrays.clone (address); } + public byte[] getAddressScript () throws ValidationException + { + ScriptFormat.Writer writer = new ScriptFormat.Writer (); + if ( type == Address.Type.COMMON ) + { + writer.writeToken (new ScriptFormat.Token (Opcode.OP_DUP)); + writer.writeToken (new ScriptFormat.Token (Opcode.OP_HASH160)); + writer.writeData (address); + writer.writeToken (new ScriptFormat.Token (Opcode.OP_EQUALVERIFY)); + writer.writeToken (new ScriptFormat.Token (Opcode.OP_CHECKSIG)); + } + else if ( type == Address.Type.P2SH ) + { + writer.writeToken (new ScriptFormat.Token (Opcode.OP_HASH160)); + writer.writeData (address); + writer.writeToken (new ScriptFormat.Token (Opcode.OP_EQUAL)); + } + else + { + throw new ValidationException ("unknown sink address type"); + } + return writer.toByteArray (); + } + @Override public String toString () { diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java index 2a24bc25..78d27593 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java @@ -25,7 +25,6 @@ import com.bitsofproof.supernode.common.ECKeyPair; import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ScriptFormat; -import com.bitsofproof.supernode.common.ScriptFormat.Opcode; import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.common.WireFormat; @@ -136,27 +135,7 @@ protected Transaction createSpend (List sources, List Date: Fri, 15 Nov 2013 08:53:14 +0100 Subject: [PATCH 201/299] refactored to use proper Address class instead of byte [] --- .../bitsofproof/supernode/api/Network.java | 6 ++ .../supernode/api/Transaction.java | 16 ++--- .../supernode/common/ECKeyPair.java | 12 +++- .../supernode/common/ECPublicKey.java | 13 +++- .../supernode/common/ExtendedKey.java | 2 +- .../com/bitsofproof/supernode/common/Key.java | 3 +- .../bitsofproof/supernode/wallet/Address.java | 68 +++++++++++++++---- .../supernode/wallet/AddressConverter.java | 18 ++--- .../supernode/wallet/BaseAccountManager.java | 15 ++-- .../wallet/ExtendedKeyAccountManager.java | 2 +- .../supernode/wallet/KeyFormatter.java | 17 ++--- .../wallet/KeyListAccountManager.java | 2 +- .../supernode/api/ExtendedKeyTest.java | 2 +- .../supernode/api/KeySerializerTest.java | 8 +-- .../supernode/core/CachedBlockStore.java | 4 +- .../supernode/testbox/APIServerInABox.java | 15 ++++ .../bitsofproof/supernode/test/APITest.java | 4 +- 17 files changed, 142 insertions(+), 65 deletions(-) create mode 100644 api/src/main/java/com/bitsofproof/supernode/api/Network.java diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Network.java b/api/src/main/java/com/bitsofproof/supernode/api/Network.java new file mode 100644 index 00000000..8421288a --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/api/Network.java @@ -0,0 +1,6 @@ +package com.bitsofproof.supernode.api; + +public enum Network +{ + UNKNOWN, PRODUCTION, TEST +} \ No newline at end of file diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java b/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java index 91303978..2b0fba0a 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java @@ -21,10 +21,10 @@ import com.bitsofproof.supernode.common.ByteUtils; import com.bitsofproof.supernode.common.Hash; -import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ScriptFormat; -import com.bitsofproof.supernode.common.ScriptFormat.Opcode; +import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.common.WireFormat; +import com.bitsofproof.supernode.wallet.Address; import com.google.protobuf.ByteString; public class Transaction implements Serializable, Cloneable @@ -42,7 +42,7 @@ public class Transaction implements Serializable, Cloneable private List inputs; private List outputs; - public static Transaction createCoinbase (Key receiver, long value, int blockHeight) + public static Transaction createCoinbase (Address address, long value, int blockHeight) throws ValidationException { Transaction cb = new Transaction (); @@ -53,20 +53,14 @@ public static Transaction createCoinbase (Key receiver, long value, int blockHei out.setValue (value); cb.getOutputs ().add (out); - ScriptFormat.Writer writer = new ScriptFormat.Writer (); - writer.writeToken (new ScriptFormat.Token (Opcode.OP_DUP)); - writer.writeToken (new ScriptFormat.Token (Opcode.OP_HASH160)); - writer.writeData (receiver.getAddress ()); - writer.writeToken (new ScriptFormat.Token (Opcode.OP_EQUALVERIFY)); - writer.writeToken (new ScriptFormat.Token (Opcode.OP_CHECKSIG)); - out.setScript (writer.toByteArray ()); + out.setScript (address.getAddressScript ()); TransactionInput in = new TransactionInput (); in.setSourceHash (Hash.ZERO_HASH_STRING); in.setIx (0); cb.getInputs ().add (in); - writer = new ScriptFormat.Writer (); + ScriptFormat.Writer writer = new ScriptFormat.Writer (); writer.writeInt32 (blockHeight); in.setScript (writer.toByteArray ()); diff --git a/api/src/main/java/com/bitsofproof/supernode/common/ECKeyPair.java b/api/src/main/java/com/bitsofproof/supernode/common/ECKeyPair.java index d3d892a0..5cfa91a4 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/ECKeyPair.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/ECKeyPair.java @@ -36,6 +36,7 @@ import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; +import com.bitsofproof.supernode.wallet.Address; public class ECKeyPair implements Key { @@ -117,9 +118,16 @@ public byte[] getPublic () } @Override - public byte[] getAddress () + public Address getAddress () { - return Hash.keyHash (pub); + try + { + return new Address (Address.Type.COMMON, Hash.keyHash (pub)); + } + catch ( ValidationException e ) + { + return null; + } } public ECKeyPair (byte[] p, boolean compressed) throws ValidationException diff --git a/api/src/main/java/com/bitsofproof/supernode/common/ECPublicKey.java b/api/src/main/java/com/bitsofproof/supernode/common/ECPublicKey.java index 754d2ccf..82af2786 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/ECPublicKey.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/ECPublicKey.java @@ -17,6 +17,8 @@ import org.bouncycastle.util.Arrays; +import com.bitsofproof.supernode.wallet.Address; + public class ECPublicKey implements Key { private byte[] pub; @@ -40,9 +42,16 @@ public void setCompressed (boolean compressed) } @Override - public byte[] getAddress () + public Address getAddress () { - return Hash.keyHash (pub); + try + { + return new Address (Address.Type.COMMON, Hash.keyHash (pub)); + } + catch ( ValidationException e ) + { + return null; + } } @Override diff --git a/api/src/main/java/com/bitsofproof/supernode/common/ExtendedKey.java b/api/src/main/java/com/bitsofproof/supernode/common/ExtendedKey.java index 3061b0bd..2fa545d1 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/ExtendedKey.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/ExtendedKey.java @@ -212,7 +212,7 @@ public int getSequence () public int getFingerPrint () { int fingerprint = 0; - byte[] address = master.getAddress (); + byte[] address = master.getAddress ().toByteArray (); for ( int i = 0; i < 4; ++i ) { fingerprint <<= 8; diff --git a/api/src/main/java/com/bitsofproof/supernode/common/Key.java b/api/src/main/java/com/bitsofproof/supernode/common/Key.java index 0399cd61..fe6f34d8 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/Key.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/Key.java @@ -15,6 +15,7 @@ */ package com.bitsofproof.supernode.common; +import com.bitsofproof.supernode.wallet.Address; public interface Key extends Cloneable { @@ -22,7 +23,7 @@ public interface Key extends Cloneable public byte[] getPublic (); - public byte[] getAddress (); + public Address getAddress (); public boolean isCompressed (); diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/Address.java b/api/src/main/java/com/bitsofproof/supernode/wallet/Address.java index f32e7350..c6c7cd48 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/Address.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/Address.java @@ -2,6 +2,8 @@ import org.bouncycastle.util.Arrays; +import com.bitsofproof.supernode.api.Network; +import com.bitsofproof.supernode.common.ByteUtils; import com.bitsofproof.supernode.common.ScriptFormat; import com.bitsofproof.supernode.common.ScriptFormat.Opcode; import com.bitsofproof.supernode.common.ValidationException; @@ -13,29 +15,47 @@ public enum Type UNKNOWN, COMMON, P2SH }; - public enum Network + private final Type type; + private final byte[] bytes; + + private Network network = Network.UNKNOWN;; + + public Network getNetwork () { - UNKNOWN, PRODUCTION, TEST + return network; } - private final Network network; - private final Type type; - private final byte[] address; + public void setNetwork (Network network) + { + this.network = network; + } public Address (Network network, Type type, byte[] address) throws ValidationException { + this.network = network; this.type = type; if ( address.length != 20 ) { throw new ValidationException ("invalid digest length for an address"); } - this.address = Arrays.clone (address); - this.network = network; + this.bytes = Arrays.clone (address); } - public Network getNetwork () + public Address (Type type, byte[] address) throws ValidationException { - return network; + this.type = type; + if ( address.length != 20 ) + { + throw new ValidationException ("invalid digest length for an address"); + } + this.bytes = Arrays.clone (address); + } + + public Address (Network network, Address address) throws ValidationException + { + this.network = network; + this.type = address.type; + this.bytes = Arrays.clone (address.bytes); } public Type getType () @@ -43,9 +63,9 @@ public Type getType () return type; } - public byte[] getAddress () + public byte[] toByteArray () { - return Arrays.clone (address); + return Arrays.clone (bytes); } public byte[] getAddressScript () throws ValidationException @@ -55,14 +75,14 @@ public byte[] getAddressScript () throws ValidationException { writer.writeToken (new ScriptFormat.Token (Opcode.OP_DUP)); writer.writeToken (new ScriptFormat.Token (Opcode.OP_HASH160)); - writer.writeData (address); + writer.writeData (bytes); writer.writeToken (new ScriptFormat.Token (Opcode.OP_EQUALVERIFY)); writer.writeToken (new ScriptFormat.Token (Opcode.OP_CHECKSIG)); } else if ( type == Address.Type.P2SH ) { writer.writeToken (new ScriptFormat.Token (Opcode.OP_HASH160)); - writer.writeData (address); + writer.writeData (bytes); writer.writeToken (new ScriptFormat.Token (Opcode.OP_EQUAL)); } else @@ -72,6 +92,26 @@ else if ( type == Address.Type.P2SH ) return writer.toByteArray (); } + @Override + public int hashCode () + { + return Arrays.hashCode (bytes) + type.ordinal (); + } + + @Override + public boolean equals (Object obj) + { + if ( this == obj ) + { + return true; + } + if ( obj == null || getClass () != obj.getClass () ) + { + return false; + } + return Arrays.areEqual (bytes, ((Address) obj).bytes) && type == ((Address) obj).type; + } + @Override public String toString () { @@ -81,7 +121,7 @@ public String toString () } catch ( ValidationException e ) { - return e.getMessage (); + return network.name () + ":" + type.name () + ":" + ByteUtils.toHex (bytes); } } } diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/AddressConverter.java b/api/src/main/java/com/bitsofproof/supernode/wallet/AddressConverter.java index b8df385f..b968fb8f 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/AddressConverter.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/AddressConverter.java @@ -15,10 +15,10 @@ */ package com.bitsofproof.supernode.wallet; +import com.bitsofproof.supernode.api.Network; import com.bitsofproof.supernode.common.ByteUtils; import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.ValidationException; -import com.bitsofproof.supernode.wallet.Address.Network; import com.bitsofproof.supernode.wallet.Address.Type; public class AddressConverter @@ -27,27 +27,27 @@ public static Address fromSatoshiStyle (String s) throws ValidationException { try { - Address.Network network = Network.UNKNOWN; + Network network = Network.UNKNOWN; Address.Type type = Type.UNKNOWN; byte[] raw = ByteUtils.fromBase58 (s); if ( (raw[0] & 0xff) == 0x0 ) { - network = Address.Network.PRODUCTION; + network = Network.PRODUCTION; type = Address.Type.COMMON; } if ( (raw[0] & 0xff) == 5 ) { - network = Address.Network.PRODUCTION; + network = Network.PRODUCTION; type = Address.Type.P2SH; } if ( (raw[0] & 0xff) == 0x6f ) { - network = Address.Network.TEST; + network = Network.TEST; type = Address.Type.COMMON; } if ( (raw[0] & 0xff) == 196 ) { - network = Address.Network.TEST; + network = Network.TEST; type = Address.Type.P2SH; } byte[] check = Hash.hash (raw, 0, raw.length - 4); @@ -107,9 +107,9 @@ public static String toSatoshiStyle (byte[] keyDigest, int addressFlag) public static String toSatoshiStyle (Address address) throws ValidationException { - byte[] keyDigest = address.getAddress (); + byte[] keyDigest = address.toByteArray (); int addressFlag; - if ( address.getNetwork () == Address.Network.PRODUCTION ) + if ( address.getNetwork () == Network.PRODUCTION ) { if ( address.getType () == Address.Type.COMMON ) { @@ -124,7 +124,7 @@ else if ( address.getType () == Address.Type.P2SH ) throw new ValidationException ("unknown address type"); } } - else if ( address.getNetwork () == Address.Network.TEST ) + else if ( address.getNetwork () == Network.TEST ) { if ( address.getType () == Address.Type.COMMON ) { diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java index 78d27593..c28be3a1 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java @@ -352,7 +352,7 @@ public int compare (TransactionOutput o1, TransactionOutput o2) @Override public Address getNextAddress () throws ValidationException { - return new Address (Address.Network.PRODUCTION, Address.Type.COMMON, getNextKey ().getAddress ()); + return getNextKey ().getAddress (); } @Override @@ -415,7 +415,7 @@ public Transaction pay (List
receiver, List amounts, long fee, bo @Override public Transaction pay (byte[] receiver, long amount, long fee) throws ValidationException { - return pay (new Address (Address.Network.PRODUCTION, Address.Type.COMMON, receiver), amount, fee); + return pay (new Address (Address.Type.COMMON, receiver), amount, fee); } @Override @@ -452,7 +452,7 @@ public Transaction pay (List
receiver, List amounts, boolean send @Override public Transaction pay (byte[] receiver, long amount) throws ValidationException { - return pay (new Address (Address.Network.PRODUCTION, Address.Type.COMMON, receiver), amount, true); + return pay (new Address (Address.Type.COMMON, receiver), amount, true); } @Override @@ -548,12 +548,14 @@ public boolean updateWithTransaction (Transaction t) if ( spend != null ) { change.add (o); - log.trace ("Change " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + o.getValue ()); + log.trace ("Change " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + + o.getValue ()); } else { receiving.add (o); - log.trace ("Receiving " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + o.getValue ()); + log.trace ("Receiving " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + + o.getValue ()); } } } @@ -592,7 +594,8 @@ public boolean updateWithTransaction (Transaction t) } if ( out != null ) { - log.trace ("Remove DS " + out.getTxHash () + " [" + out.getIx () + "] (" + ByteUtils.toHex (out.getOutputAddress ()) + ")" + out.getValue ()); + log.trace ("Remove DS " + out.getTxHash () + " [" + out.getIx () + "] (" + ByteUtils.toHex (out.getOutputAddress ()) + ")" + + out.getValue ()); } modified |= out != null; } diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeyAccountManager.java index 3d2fa13c..91754091 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeyAccountManager.java @@ -103,7 +103,7 @@ private void ensureLookAhead (int from) catch ( ValidationException e ) { } - keyIDForAddress.put (new ByteVector (key.getAddress ()), keyIDForAddress.size () + firstIndex); + keyIDForAddress.put (new ByteVector (key.getAddress ().toByteArray ()), keyIDForAddress.size () + firstIndex); } } diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/KeyFormatter.java b/api/src/main/java/com/bitsofproof/supernode/wallet/KeyFormatter.java index a0f5d435..dc10ba5d 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/KeyFormatter.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/KeyFormatter.java @@ -32,6 +32,7 @@ import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.crypto.generators.SCrypt; +import com.bitsofproof.supernode.api.Network; import com.bitsofproof.supernode.common.ByteUtils; import com.bitsofproof.supernode.common.ECKeyPair; import com.bitsofproof.supernode.common.Hash; @@ -43,13 +44,13 @@ */ public class KeyFormatter { - private final int addressFlag; + private final Network network; private final String passphrase; - public KeyFormatter (String passphrase, int addressFlag) + public KeyFormatter (String passphrase, Network network) { this.passphrase = passphrase; - this.addressFlag = addressFlag; + this.network = network; } public boolean hasPassPhrase () @@ -57,9 +58,9 @@ public boolean hasPassPhrase () return passphrase != null; } - public int getAddressFlag () + public Network getNetwork () { - return addressFlag; + return network; } public String serializeKey (Key key) throws ValidationException @@ -243,7 +244,7 @@ private ECKeyPair parseBIP38NoEC (byte[] store, boolean compressed) throws Valid } ECKeyPair kp = new ECKeyPair (decrypted, compressed); - byte[] acs = Hash.hash (AddressConverter.toSatoshiStyle (kp.getAddress (), addressFlag).getBytes ("US-ASCII")); + byte[] acs = Hash.hash (AddressConverter.toSatoshiStyle (new Address (network, kp.getAddress ())).getBytes ("US-ASCII")); byte[] check = new byte[4]; System.arraycopy (acs, 0, check, 0, 4); if ( !Arrays.equals (check, addressHash) ) @@ -341,7 +342,7 @@ private ECKeyPair parseBIP38EC (byte[] store, boolean compressed, boolean hasLot new BigInteger (1, passfactor).multiply (new BigInteger (1, Hash.hash (seed))).remainder (SECNamedCurves.getByName ("secp256k1").getN ()); kp = new ECKeyPair (priv, compressed); - byte[] acs = Hash.hash (AddressConverter.toSatoshiStyle (kp.getAddress (), addressFlag).getBytes ("US-ASCII")); + byte[] acs = Hash.hash (AddressConverter.toSatoshiStyle (new Address (network, kp.getAddress ())).getBytes ("US-ASCII")); byte[] check = new byte[4]; System.arraycopy (acs, 0, check, 0, 4); if ( !Arrays.equals (check, addressHash) ) @@ -395,7 +396,7 @@ private byte[] bytesBIP38 (Key key) throws ValidationException byte[] xor = new byte[32]; try { - byte[] ac = Hash.hash (AddressConverter.toSatoshiStyle (key.getAddress (), addressFlag).getBytes ("US-ASCII")); + byte[] ac = Hash.hash (AddressConverter.toSatoshiStyle (new Address (network, key.getAddress ())).getBytes ("US-ASCII")); System.arraycopy (ac, 0, addressHash, 0, 4); System.arraycopy (ac, 0, store, 3, 4); byte[] derived = SCrypt.generate (passphrase.getBytes ("UTF-8"), addressHash, 16384, 8, 8, 64); diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/KeyListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/KeyListAccountManager.java index 0d343947..44dc479f 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/KeyListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/KeyListAccountManager.java @@ -54,7 +54,7 @@ public Key getNextKey () throws ValidationException public void addKey (ECKeyPair key) { keys.add (key); - keyByAddress.put (new ByteVector (key.getAddress ()), key); + keyByAddress.put (new ByteVector (key.getAddress ().toByteArray ()), key); } @Override diff --git a/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java b/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java index 4d364921..feeb8487 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/ExtendedKeyTest.java @@ -66,7 +66,7 @@ public void testGenerator () throws ValidationException Key readOnly = ekpublic.getKey (i); assertTrue (Arrays.equals (fullControl.getPublic (), readOnly.getPublic ())); - assertTrue (Arrays.equals (fullControl.getAddress (), readOnly.getAddress ())); + assertTrue (fullControl.getAddress ().equals (readOnly.getAddress ())); byte[] toSign = new byte[100]; random.nextBytes (toSign); diff --git a/api/src/test/java/com/bitsofproof/supernode/api/KeySerializerTest.java b/api/src/test/java/com/bitsofproof/supernode/api/KeySerializerTest.java index 852b8a8b..ac0a0e89 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/KeySerializerTest.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/KeySerializerTest.java @@ -65,7 +65,7 @@ private JSONArray readObjectArray (String resource) throws IOException, JSONExce @Test public void wifTest () throws IOException, JSONException, ValidationException { - KeyFormatter formatter = new KeyFormatter (null, 0x0); + KeyFormatter formatter = new KeyFormatter (null, Network.PRODUCTION); JSONArray testData = readObjectArray (WIF); for ( int i = 0; i < testData.length (); ++i ) { @@ -86,7 +86,7 @@ public void bip38NoECTest () throws ValidationException, IOException, JSONExcept { final JSONArray test = testData.getJSONArray (i); - KeyFormatter formatter = new KeyFormatter (test.getString (2), 0x0); + KeyFormatter formatter = new KeyFormatter (test.getString (2), Network.PRODUCTION); ECKeyPair kp = formatter.parseSerializedKey (test.getString (0)); String decrypted = ECKeyPair.serializeWIF (kp); @@ -104,7 +104,7 @@ public void bip38ECTest () throws ValidationException, IOException, JSONExceptio { final JSONArray test = testData.getJSONArray (i); - KeyFormatter formatter = new KeyFormatter (test.getString (2), 0x0); + KeyFormatter formatter = new KeyFormatter (test.getString (2), Network.PRODUCTION); ECKeyPair kp = formatter.parseSerializedKey (test.getString (0)); String decrypted = ECKeyPair.serializeWIF (kp); assertTrue (test.getString (1).equals (decrypted)); @@ -125,7 +125,7 @@ public void bip38RandomTest () throws ValidationException } String passphrase = p.toString (); - KeyFormatter formatter = new KeyFormatter (passphrase, 0x0); + KeyFormatter formatter = new KeyFormatter (passphrase, Network.PRODUCTION); ECKeyPair kp = ECKeyPair.createNew (i % 2 == 0); String serialized = formatter.serializeKey (kp); ECKeyPair kp2 = formatter.parseSerializedKey (serialized); diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 695bc9c1..ff350d6b 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -437,7 +437,7 @@ public void filterTransactions (boolean utxo, Set matchSet, Extended lookAhead = Math.min (Math.max (10, lookAhead), 1000); for ( int i = firstIndex; i < firstIndex + lookAhead; ++i ) { - ByteVector address = new ByteVector (ek.getKey (i).getAddress ()); + ByteVector address = new ByteVector (ek.getKey (i).getAddress ().toByteArray ()); matchSet.add (address); addressSet.put (address, i); } @@ -497,7 +497,7 @@ public void filterTransactions (boolean utxo, Set matchSet, Extended } for ( int i = addressSet.size (); i < lastUsedAddress + lookAhead - firstIndex; ++i ) { - ByteVector address = new ByteVector (ek.getKey (i).getAddress ()); + ByteVector address = new ByteVector (ek.getKey (i).getAddress ().toByteArray ()); matchSet.add (address); addressSet.put (address, i); } diff --git a/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java b/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java index bf33004e..eb6d40b8 100644 --- a/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java +++ b/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java @@ -46,6 +46,21 @@ public APIServerInABox (Chain chain) throws IOException, ValidationException bcsapi.init (); } + public BitcoinNetwork getNetwork () + { + return network; + } + + public InMemoryBusConnectionFactory getConnectionFactory () + { + return connectionFactory; + } + + public Chain getChain () + { + return chain; + } + public BCSAPI getAPI () { JMSServerConnector api = new JMSServerConnector (); diff --git a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java index e1eb5e6d..8fa0995a 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java @@ -190,7 +190,7 @@ public void test () throws BCSAPIException, ValidationException, IOException assertTrue (api.getBlock (genesisHash).getHash ().equals (genesisHash)); // send 1 block - Block block = createBlock (chain.getGenesis ().getHash (), Transaction.createCoinbase (alice.getNextKey (), 50 * COIN, 1)); + Block block = createBlock (chain.getGenesis ().getHash (), Transaction.createCoinbase (alice.getNextKey ().getAddress (), 50 * COIN, 1)); mineBlock (block); blocks.put (1, block); @@ -205,7 +205,7 @@ public void test () throws BCSAPIException, ValidationException, IOException String hash = blocks.get (1).getHash (); for ( int i = 0; i < 10; ++i ) { - block = createBlock (hash, Transaction.createCoinbase (alice.getNextKey (), 5000000000L, i + 2)); + block = createBlock (hash, Transaction.createCoinbase (alice.getNextKey ().getAddress (), 5000000000L, i + 2)); block.setCreateTime (block.getCreateTime () + (i + 1) * 1000); // avoid clash of timestamp with median mineBlock (block); blocks.put (i + 2, block); From 550d27e27b8b3d32613db5d9a14f2475272fcd02 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 15 Nov 2013 09:03:38 +0100 Subject: [PATCH 202/299] add getReadOnly to Key --- .../java/com/bitsofproof/supernode/common/ECKeyPair.java | 6 ++++++ .../java/com/bitsofproof/supernode/common/ECPublicKey.java | 5 +++++ api/src/main/java/com/bitsofproof/supernode/common/Key.java | 2 ++ 3 files changed, 13 insertions(+) diff --git a/api/src/main/java/com/bitsofproof/supernode/common/ECKeyPair.java b/api/src/main/java/com/bitsofproof/supernode/common/ECKeyPair.java index 5cfa91a4..653c196c 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/ECKeyPair.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/ECKeyPair.java @@ -117,6 +117,12 @@ public byte[] getPublic () return Arrays.clone (pub); } + @Override + public Key getReadOnly () + { + return new ECPublicKey (pub, compressed); + } + @Override public Address getAddress () { diff --git a/api/src/main/java/com/bitsofproof/supernode/common/ECPublicKey.java b/api/src/main/java/com/bitsofproof/supernode/common/ECPublicKey.java index 82af2786..ce58f0a4 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/ECPublicKey.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/ECPublicKey.java @@ -86,4 +86,9 @@ public boolean verify (byte[] hash, byte[] signature) return ECKeyPair.verify (hash, signature, pub); } + @Override + public Key getReadOnly () + { + return this; + } } diff --git a/api/src/main/java/com/bitsofproof/supernode/common/Key.java b/api/src/main/java/com/bitsofproof/supernode/common/Key.java index fe6f34d8..8ab0b1e6 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/Key.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/Key.java @@ -27,6 +27,8 @@ public interface Key extends Cloneable public boolean isCompressed (); + public Key getReadOnly (); + public Key clone () throws CloneNotSupportedException; public byte[] sign (byte[] data) throws ValidationException; From 87d5d65a26cabdc1f2725080b001a8ee11073a7a Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 16 Nov 2013 18:00:20 +0100 Subject: [PATCH 203/299] more robust server in a box and in memory bus --- .../common/InMemoryBusConnectionFactory.java | 203 ++++++++++++------ .../supernode/core/CachedBlockStore.java | 4 + .../core/ImplementTxOutCacheDelta.java | 7 + .../bitsofproof/supernode/core/TxHandler.java | 7 + .../supernode/core/TxOutCache.java | 1 + .../supernode/testbox/APIServerInABox.java | 24 ++- .../bitsofproof/supernode/test/APITest.java | 6 +- 7 files changed, 182 insertions(+), 70 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java b/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java index 47c66d95..49b0ae92 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java @@ -22,7 +22,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -525,15 +525,37 @@ public void delete () throws JMSException } } - private static final Map> consumer = new HashMap> (); + private static class MessageWithDestination + { + private final String destination; + private final Message message; + + public String getDestination () + { + return destination; + } + + public Message getMessage () + { + return message; + } + + public MessageWithDestination (String destination, Message message) + { + this.destination = destination; + this.message = message; + } + } private static class MockProducer implements MessageProducer { private final String name; + private final LinkedBlockingQueue queue; - public MockProducer (String name) + public MockProducer (String name, LinkedBlockingQueue queue) { this.name = name; + this.queue = queue; } @Override @@ -628,26 +650,14 @@ public void send (Destination destination, Message message, int deliveryMode, in private void sendToConsumer (Message message) { - synchronized ( consumer ) - { - List cl = consumer.get (name); - if ( cl != null ) - { - for ( MockConsumer c : cl ) - { - c.getQueue ().add (message); - } - } - } + queue.add (new MessageWithDestination (name, message)); } } - private static Executor consumerExecutor = Executors.newCachedThreadPool (); - - private static class MockConsumer implements MessageConsumer, Runnable + private static class MockConsumer implements MessageConsumer { - private final LinkedBlockingQueue queue = new LinkedBlockingQueue (); - private boolean open = true; + private LinkedBlockingQueue queue; + private MessageListener listener; public LinkedBlockingQueue getQueue () { @@ -667,31 +677,34 @@ public MessageListener getMessageListener () throws JMSException } @Override - public void setMessageListener (final MessageListener listener) throws JMSException + public synchronized void setMessageListener (final MessageListener listener) throws JMSException { - consumerExecutor.execute (new Runnable () + if ( queue != null ) { - @Override - public void run () - { - while ( open ) - { - try - { - Message m = receive (); - listener.onMessage (m); - } - catch ( JMSException e ) - { - } - } - } - }); + throw new JMSException ("Use either setListener or receive"); + } + this.listener = listener; + } + + private MessageListener getListener () + { + return listener; } @Override public Message receive () throws JMSException { + synchronized ( this ) + { + if ( listener != null ) + { + throw new JMSException ("Use either setListener or receive"); + } + if ( queue == null ) + { + queue = new LinkedBlockingQueue (); + } + } try { return queue.take (); @@ -705,6 +718,21 @@ public Message receive () throws JMSException @Override public Message receive (long timeout) throws JMSException { + synchronized ( this ) + { + if ( listener != null ) + { + throw new JMSException ("Use either setListener or receive"); + } + if ( queue == null ) + { + queue = new LinkedBlockingQueue (); + } + } + if ( queue == null ) + { + queue = new LinkedBlockingQueue (); + } try { return queue.poll (timeout, TimeUnit.MILLISECONDS); @@ -718,25 +746,77 @@ public Message receive (long timeout) throws JMSException @Override public Message receiveNoWait () throws JMSException { + synchronized ( this ) + { + if ( listener != null ) + { + throw new JMSException ("Use either setListener or receive"); + } + if ( queue == null ) + { + queue = new LinkedBlockingQueue (); + } + } return queue.poll (); } @Override public void close () throws JMSException { - open = false; + listener = null; + queue = null; + } + } + + private static class MockSession implements Session, Runnable + { + private final LinkedBlockingQueue queue = new LinkedBlockingQueue<> (); + private volatile boolean run = true; + private final Map> consumer; + + public MockSession (Map> consumer) + { + this.consumer = consumer; } @Override public void run () { + MessageWithDestination md = null; + do + { + try + { + md = queue.poll (10, TimeUnit.MILLISECONDS); + if ( md != null ) + { + synchronized ( consumer ) + { + List cl = consumer.get (md.getDestination ()); + if ( cl != null ) + { + for ( MockConsumer c : cl ) + { + if ( c.getListener () != null ) + { + c.getListener ().onMessage (md.getMessage ()); + } + else if ( c.getQueue () != null ) + { + c.getQueue ().put (md.getMessage ()); + } + } + } + } + } - } - } + } + catch ( InterruptedException e ) + { + } + } while ( md != null || run ); - private static class MockSession implements Session - { - private final List sessionConsumer = new ArrayList (); + } @Override public BytesMessage createBytesMessage () throws JMSException @@ -811,10 +891,7 @@ public void rollback () throws JMSException @Override public void close () throws JMSException { - for ( MessageConsumer consumer : sessionConsumer ) - { - consumer.close (); - } + run = false; } @Override @@ -833,11 +910,6 @@ public void setMessageListener (MessageListener listener) throws JMSException { } - @Override - public void run () - { - } - @Override public MessageProducer createProducer (Destination destination) throws JMSException { @@ -854,8 +926,7 @@ public MessageProducer createProducer (Destination destination) throws JMSExcept { name = ((MockTemporaryQueue) destination).getQueueName (); } - - return new MockProducer (name); + return new MockProducer (name, queue); } @Override @@ -874,11 +945,10 @@ public MessageConsumer createConsumer (Destination destination) throws JMSExcept { name = ((MockTemporaryQueue) destination).getQueueName (); } - final MockConsumer c = new MockConsumer (); - sessionConsumer.add (c); + MockConsumer c = new MockConsumer (); synchronized ( consumer ) { - ArrayList cl = consumer.get (name); + List cl = consumer.get (name); if ( cl == null ) { cl = new ArrayList (); @@ -958,12 +1028,15 @@ public void unsubscribe (String name) throws JMSException private class MockConnection implements Connection { private final List sessions = new ArrayList (); + private final ExecutorService consumerExecutor = Executors.newCachedThreadPool (); + private final Map> consumer = new HashMap<> (); @Override public Session createSession (boolean transacted, int acknowledgeMode) throws JMSException { - Session session = new MockSession (); + Session session = new MockSession (consumer); sessions.add (session); + consumerExecutor.execute (session); return session; } @@ -1030,15 +1103,25 @@ public ConnectionConsumer createDurableConnectionConsumer (Topic topic, String s } + private static Connection connection; + @Override public Connection createConnection () throws JMSException { - return new MockConnection (); + if ( connection == null ) + { + connection = new MockConnection (); + } + return connection; } @Override public Connection createConnection (String userName, String password) throws JMSException { - return new MockConnection (); + if ( connection == null ) + { + connection = new MockConnection (); + } + return connection; } } diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index ff350d6b..fd42c3ff 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -342,6 +342,10 @@ public void cache (Chain chain, int size) throws ValidationException try { lock.writeLock ().lock (); + cachedBlocks.clear (); + cachedHeads.clear (); + blockChain.clear (); + cachedUTXO.clear (); log.trace ("Cache heads..."); cacheHeads (); diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementTxOutCacheDelta.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementTxOutCacheDelta.java index bb7c9e77..c6d44708 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementTxOutCacheDelta.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementTxOutCacheDelta.java @@ -34,6 +34,13 @@ public ImplementTxOutCacheDelta (TxOutCache cache) delegate = cache; } + @Override + public void clear () + { + reset (); + delegate.clear (); + } + public void reset () { used.clear (); diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index cfa3528d..3242ad3a 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -82,6 +82,13 @@ public void addTransactionListener (TxListener listener) transactionListener.add (listener); } + public void clear () + { + unconfirmed.clear (); + availableOutput.clear (); + dependencyOrderedSet.clear (); + } + public TxHandler (final BitcoinNetwork network) { this.network = network; diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxOutCache.java b/server/src/main/java/com/bitsofproof/supernode/core/TxOutCache.java index 11c2f1aa..895f3042 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxOutCache.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxOutCache.java @@ -29,4 +29,5 @@ public interface TxOutCache public void remove (String hash, Long ix); + public void clear (); } \ No newline at end of file diff --git a/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java b/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java index eb6d40b8..d1ce9c35 100644 --- a/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java +++ b/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java @@ -25,14 +25,15 @@ public class APIServerInABox private final BitcoinNetwork network; private final InMemoryBusConnectionFactory connectionFactory = new InMemoryBusConnectionFactory (); private final Chain chain; + private final TxHandler txhandler; + private final ImplementBCSAPI bcsapi; + private final JMSServerConnector api; public APIServerInABox (Chain chain) throws IOException, ValidationException { this.chain = chain; LvlStore store = new LvlStore (); store.setStore (new LvlMemoryStore ()); - store.resetStore (chain); - store.cache (chain, 0); FixedAddressDiscovery discovery = new FixedAddressDiscovery (); discovery.setConnectTo ("localhost"); network = new BitcoinNetwork (0); @@ -40,10 +41,21 @@ public APIServerInABox (Chain chain) throws IOException, ValidationException network.setStore (store); network.setListen (false); network.setDiscovery (discovery); - TxHandler txhandler = new TxHandler (network); - ImplementBCSAPI bcsapi = new ImplementBCSAPI (network, txhandler); + txhandler = new TxHandler (network); + api = new JMSServerConnector (); + api.setConnectionFactory (connectionFactory); + bcsapi = new ImplementBCSAPI (network, txhandler); bcsapi.setConnectionFactory (connectionFactory); + reset (); bcsapi.init (); + api.init (); + } + + public void reset () throws ValidationException + { + network.getStore ().resetStore (chain); + network.getStore ().cache (chain, 0); + txhandler.clear (); } public BitcoinNetwork getNetwork () @@ -63,9 +75,7 @@ public Chain getChain () public BCSAPI getAPI () { - JMSServerConnector api = new JMSServerConnector (); - api.setConnectionFactory (connectionFactory); - api.init (); + return api; } diff --git a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java index 8fa0995a..b30c0884 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java @@ -89,7 +89,7 @@ public void expectUpdates (int n) { try { - assertTrue (ready.tryAcquire (n, 1, TimeUnit.SECONDS)); + assertTrue (ready.tryAcquire (n, 5, TimeUnit.SECONDS)); assertFalse (ready.tryAcquire ()); } catch ( InterruptedException e ) @@ -127,7 +127,7 @@ public void expectBlockValidations (int n) { try { - assertTrue (blockValidated.tryAcquire (n, 1, TimeUnit.SECONDS)); + assertTrue (blockValidated.tryAcquire (n, 5, TimeUnit.SECONDS)); assertFalse (blockValidated.tryAcquire ()); } catch ( InterruptedException e ) @@ -139,7 +139,7 @@ public void expectTransactionValidations (int n) { try { - assertTrue (transactionValidated.tryAcquire (n, 1, TimeUnit.SECONDS)); + assertTrue (transactionValidated.tryAcquire (n, 5, TimeUnit.SECONDS)); assertFalse (transactionValidated.tryAcquire ()); } catch ( InterruptedException e ) From 6dc673dfbb27f6dc5b7f8fbd7df492aed4209230 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 16 Nov 2013 18:22:20 +0100 Subject: [PATCH 204/299] single queue of messages for all sessions in in memory bus --- .../supernode/common/InMemoryBusConnectionFactory.java | 8 +++++--- .../test/java/com/bitsofproof/supernode/test/APITest.java | 6 +++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java b/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java index 49b0ae92..331d398e 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java @@ -770,13 +770,14 @@ public void close () throws JMSException private static class MockSession implements Session, Runnable { - private final LinkedBlockingQueue queue = new LinkedBlockingQueue<> (); + private final LinkedBlockingQueue queue; private volatile boolean run = true; private final Map> consumer; - public MockSession (Map> consumer) + public MockSession (LinkedBlockingQueue queue, Map> consumer) { this.consumer = consumer; + this.queue = queue; } @Override @@ -1030,11 +1031,12 @@ private class MockConnection implements Connection private final List sessions = new ArrayList (); private final ExecutorService consumerExecutor = Executors.newCachedThreadPool (); private final Map> consumer = new HashMap<> (); + private final LinkedBlockingQueue queue = new LinkedBlockingQueue<> (); @Override public Session createSession (boolean transacted, int acknowledgeMode) throws JMSException { - Session session = new MockSession (consumer); + Session session = new MockSession (queue, consumer); sessions.add (session); consumerExecutor.execute (session); return session; diff --git a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java index b30c0884..8fa0995a 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java @@ -89,7 +89,7 @@ public void expectUpdates (int n) { try { - assertTrue (ready.tryAcquire (n, 5, TimeUnit.SECONDS)); + assertTrue (ready.tryAcquire (n, 1, TimeUnit.SECONDS)); assertFalse (ready.tryAcquire ()); } catch ( InterruptedException e ) @@ -127,7 +127,7 @@ public void expectBlockValidations (int n) { try { - assertTrue (blockValidated.tryAcquire (n, 5, TimeUnit.SECONDS)); + assertTrue (blockValidated.tryAcquire (n, 1, TimeUnit.SECONDS)); assertFalse (blockValidated.tryAcquire ()); } catch ( InterruptedException e ) @@ -139,7 +139,7 @@ public void expectTransactionValidations (int n) { try { - assertTrue (transactionValidated.tryAcquire (n, 5, TimeUnit.SECONDS)); + assertTrue (transactionValidated.tryAcquire (n, 1, TimeUnit.SECONDS)); assertFalse (transactionValidated.tryAcquire ()); } catch ( InterruptedException e ) From 80d6c6956374225f05d1e26a7b602b8d231eff8b Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 16 Nov 2013 18:55:24 +0100 Subject: [PATCH 205/299] remove empty message dispatcher --- .../com/bitsofproof/supernode/api/JMSServerConnector.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java index 2ec6b3ae..0bfffaef 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java @@ -182,7 +182,7 @@ private void removeTopicListener (String topic, Object inner) dispatcher.removeListener (inner); if ( !dispatcher.isListened () ) { - messageDispatcher.remove (dispatcher); + messageDispatcher.remove (topic); try { dispatcher.getConsumer ().close (); @@ -336,7 +336,8 @@ public void scanTransactions (ExtendedKey master, int firstIndex, int lookAhead, scanRequest (master, firstIndex, lookAhead, after, listener, "accountRequest"); } - private void scanRequest (Collection match, UpdateMode mode, long after, final TransactionListener listener, String requestQueue) throws BCSAPIException + private void scanRequest (Collection match, UpdateMode mode, long after, final TransactionListener listener, String requestQueue) + throws BCSAPIException { Session session = null; try @@ -415,7 +416,8 @@ public void onMessage (Message message) } } - private void scanRequest (ExtendedKey master, int firstIndex, int lookAhead, long after, final TransactionListener listener, String request) throws BCSAPIException + private void scanRequest (ExtendedKey master, int firstIndex, int lookAhead, long after, final TransactionListener listener, String request) + throws BCSAPIException { if ( !master.isReadOnly () ) { From a9ad79b3775681dace25017d17d530b7348c65ca Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 16 Nov 2013 19:00:45 +0100 Subject: [PATCH 206/299] 1.2.9 --- api/pom.xml | 2 +- pom.xml | 2 +- server/bop-service.sh | 4 ++-- server/pom.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 048ac9fe..7fae5e56 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.8 + 1.2.9 ../ diff --git a/pom.xml b/pom.xml index bbb3b1d4..056538e3 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.2.8 + 1.2.9 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/bop-service.sh b/server/bop-service.sh index 7d8727cf..42958213 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.8.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.9.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.8.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.9.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" diff --git a/server/pom.xml b/server/pom.xml index 0fb4289a..034c5b3f 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.8 + 1.2.9 ../ From 78f49d70211bdfe3e29dc5ee44b520b881a2f49c Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 20 Nov 2013 20:59:56 +0100 Subject: [PATCH 207/299] fix dependency ordered set to return 0 only for identical transactions. --- .../src/main/java/com/bitsofproof/supernode/core/TxHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index 3242ad3a..e914d6ce 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -71,7 +71,7 @@ public int compare (Tx a, Tx b) return 1; } } - return 0; + return a.getHash ().compareTo (b.getHash ()); } }); From 9b0a26387f33275440645e1abe565c114e127fd1 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 20 Nov 2013 21:30:06 +0100 Subject: [PATCH 208/299] log uncaught exception in message listener --- .../common/InMemoryBusConnectionFactory.java | 57 ++++++++++++++----- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java b/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java index 331d398e..a87ccb4b 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java @@ -52,8 +52,13 @@ import javax.jms.Topic; import javax.jms.TopicSubscriber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class InMemoryBusConnectionFactory implements ConnectionFactory { + private static final Logger log = LoggerFactory.getLogger (InMemoryBusConnectionFactory.class); + private static class MockBytesMessage implements BytesMessage { Destination destination; @@ -658,8 +663,14 @@ private static class MockConsumer implements MessageConsumer { private LinkedBlockingQueue queue; private MessageListener listener; + private final List peers; + + public MockConsumer (List peers) + { + this.peers = peers; + } - public LinkedBlockingQueue getQueue () + public synchronized LinkedBlockingQueue getQueue () { return queue; } @@ -671,9 +682,9 @@ public String getMessageSelector () throws JMSException } @Override - public MessageListener getMessageListener () throws JMSException + public synchronized MessageListener getMessageListener () throws JMSException { - return null; + return listener; } @Override @@ -686,7 +697,7 @@ public synchronized void setMessageListener (final MessageListener listener) thr this.listener = listener; } - private MessageListener getListener () + private synchronized MessageListener getListener () { return listener; } @@ -729,10 +740,6 @@ public Message receive (long timeout) throws JMSException queue = new LinkedBlockingQueue (); } } - if ( queue == null ) - { - queue = new LinkedBlockingQueue (); - } try { return queue.poll (timeout, TimeUnit.MILLISECONDS); @@ -761,8 +768,12 @@ public Message receiveNoWait () throws JMSException } @Override - public void close () throws JMSException + public synchronized void close () throws JMSException { + synchronized ( peers ) + { + peers.remove (this); + } listener = null; queue = null; } @@ -788,19 +799,30 @@ public void run () { try { - md = queue.poll (10, TimeUnit.MILLISECONDS); + md = queue.poll (1, TimeUnit.SECONDS); if ( md != null ) { + List cl; synchronized ( consumer ) { - List cl = consumer.get (md.getDestination ()); - if ( cl != null ) + cl = consumer.get (md.getDestination ()); + } + if ( cl != null ) + { + synchronized ( cl ) { for ( MockConsumer c : cl ) { if ( c.getListener () != null ) { - c.getListener ().onMessage (md.getMessage ()); + try + { + c.getListener ().onMessage (md.getMessage ()); + } + catch ( Exception e ) + { + log.error ("Uncaught exception in message listener", e); + } } else if ( c.getQueue () != null ) { @@ -946,15 +968,20 @@ public MessageConsumer createConsumer (Destination destination) throws JMSExcept { name = ((MockTemporaryQueue) destination).getQueueName (); } - MockConsumer c = new MockConsumer (); + + List cl; synchronized ( consumer ) { - List cl = consumer.get (name); + cl = consumer.get (name); if ( cl == null ) { cl = new ArrayList (); consumer.put (name, cl); } + } + MockConsumer c = new MockConsumer (cl); + synchronized ( cl ) + { cl.add (c); } return c; From 64677ac73b49c1d2ebaf9dc92619c10936b7c3ca Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 20 Nov 2013 21:34:02 +0100 Subject: [PATCH 209/299] add a new checkpoint --- .../java/com/bitsofproof/supernode/core/CachedBlockStore.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index fd42c3ff..1b4c3ed8 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -77,8 +77,9 @@ public abstract class CachedBlockStore implements BlockStore checkPoints.put (210000, "000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e"); checkPoints.put (222222, "00000000000000b8b49d0b61b14994b5c0a511c4b48a1e251ff2b479b2e6f678"); checkPoints.put (232000, "000000000000018f47636e1c3a946db77624880ae484ffb0233f5aac6316b3bb"); + checkPoints.put (270000, "0000000000000002a775aec59dc6a9e4bb1c025cf1b8c2195dd9dc3998c827c5"); - lastCheckPoint = 232000; + lastCheckPoint = 270000; } protected Chain chain; From d7084d6dd8d0ab4e559ceff202c4a16568b4f8d7 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 20 Nov 2013 21:36:58 +0100 Subject: [PATCH 210/299] 1.2.10 --- api/pom.xml | 2 +- pom.xml | 2 +- server/bop-service.sh | 4 ++-- server/pom.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 7fae5e56..fc345d49 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.9 + 1.2.10 ../ diff --git a/pom.xml b/pom.xml index 056538e3..3ddef936 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.2.9 + 1.2.10 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/bop-service.sh b/server/bop-service.sh index 42958213..f7d6a86e 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.9.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.10.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.9.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.10.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" diff --git a/server/pom.xml b/server/pom.xml index 034c5b3f..53759b80 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.9 + 1.2.10 ../ From 158121082fd9da9c205aceece6405fa3fb116fd4 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 20 Nov 2013 21:51:51 +0100 Subject: [PATCH 211/299] ask for mempool if in slave mode --- .../java/com/bitsofproof/supernode/core/BitcoinPeer.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/BitcoinPeer.java b/server/src/main/java/com/bitsofproof/supernode/core/BitcoinPeer.java index ad94f0f7..8ad38455 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/BitcoinPeer.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/BitcoinPeer.java @@ -289,6 +289,11 @@ public void process (BitcoinPeer.Message m, BitcoinPeer peer) + getNetwork ().getNumberOfConnections ()); network.addPeer (peer); peer.send (peer.createMessage ("getaddr")); + if ( network.getChain ().isSlave () ) + { + MempoolMessage mp = (MempoolMessage) peer.createMessage ("mempool"); + peer.send (mp); + } } }); } From 1dbb7a6c868ac4b784e0bd84944cb837311af4b5 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 20 Nov 2013 21:54:40 +0100 Subject: [PATCH 212/299] send mempool in dependency order --- .../com/bitsofproof/supernode/core/TxHandler.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index e914d6ce..92bb0883 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -84,9 +84,12 @@ public void addTransactionListener (TxListener listener) public void clear () { - unconfirmed.clear (); - availableOutput.clear (); - dependencyOrderedSet.clear (); + synchronized ( unconfirmed ) + { + unconfirmed.clear (); + availableOutput.clear (); + dependencyOrderedSet.clear (); + } } public TxHandler (final BitcoinNetwork network) @@ -150,7 +153,7 @@ public void process (final MempoolMessage m, final BitcoinPeer peer) InvMessage tm = (InvMessage) peer.createMessage ("inv"); synchronized ( unconfirmed ) { - for ( Tx tx : unconfirmed.values () ) + for ( Tx tx : dependencyOrderedSet ) { tm.getTransactionHashes ().add (new Hash (tx.getHash ()).toByteArray ()); } From 1dbe3f9b028333c54122fdaa081e0fa3a713dea7 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 20 Nov 2013 22:19:42 +0100 Subject: [PATCH 213/299] ask for inputs if can not resolve them in slave mode --- .../bitsofproof/supernode/core/TxHandler.java | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index 92bb0883..e190ae44 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -178,10 +178,34 @@ public void run (TxOutCache cache) throws ValidationException { if ( network.getStore ().getTransaction (t.getHash ()) == null ) { - network.getStore ().validateTransaction (t, availableOutput); - cacheTransaction (t); - sendTransaction (t, peer); - notifyListener (t, false); + if ( network.getChain ().isSlave () && peer != null ) + { + try + { + network.getStore ().resolveTransactionInputs (t, availableOutput); + cacheTransaction (t); + sendTransaction (t, peer); + notifyListener (t, false); + } + catch ( ValidationException e ) + { + log.trace ("asking for inputs of " + t.getHash ()); + GetDataMessage get = (GetDataMessage) peer.createMessage ("getdata"); + for ( TxIn in : t.getInputs () ) + { + get.getTransactions ().add (new Hash (in.getSourceHash ()).toByteArray ()); + } + get.getTransactions ().add (new Hash (t.getHash ()).toByteArray ()); + peer.send (get); + } + } + else + { + network.getStore ().validateTransaction (t, availableOutput); + cacheTransaction (t); + sendTransaction (t, peer); + notifyListener (t, false); + } } } } From 9a44e0cb65cb1f2ca1d8111d670cfeece47453c5 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 20 Nov 2013 22:26:41 +0100 Subject: [PATCH 214/299] 1.3 --- api/pom.xml | 2 +- pom.xml | 2 +- server/bop-service.sh | 4 ++-- server/pom.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index fc345d49..416514a8 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.10 + 1.3-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index 3ddef936..cc960ba3 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.2.10 + 1.3-SNAPSHOT supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/bop-service.sh b/server/bop-service.sh index f7d6a86e..2941f031 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.10.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.3-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.2.10.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.3-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" diff --git a/server/pom.xml b/server/pom.xml index 53759b80..6392dc23 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.2.10 + 1.3-SNAPSHOT ../ From 7bea3fd5ab7cbd67f553e60b6298192590aa4e7c Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 20 Nov 2013 22:28:29 +0100 Subject: [PATCH 215/299] 1.3.0 --- api/pom.xml | 2 +- pom.xml | 2 +- server/bop-service.sh | 4 ++-- server/pom.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 416514a8..d1eaf067 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.3-SNAPSHOT + 1.3.0 ../ diff --git a/pom.xml b/pom.xml index cc960ba3..0f0ab540 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.3-SNAPSHOT + 1.3.0 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/bop-service.sh b/server/bop-service.sh index 2941f031..d7c59557 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.3-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.3.0.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.3-SNAPSHOT.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.3.0.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" diff --git a/server/pom.xml b/server/pom.xml index 6392dc23..8ef30e1d 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.3-SNAPSHOT + 1.3.0 ../ From e873c2416de73371de1f3c4694683f57651d75bf Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 20 Nov 2013 22:59:32 +0100 Subject: [PATCH 216/299] do not re-check if master can not find tx. --- .../bitsofproof/supernode/core/TxHandler.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index e190ae44..03ab3d8e 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -19,6 +19,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -51,6 +52,7 @@ public class TxHandler implements TrunkListener private final Map unconfirmed = Collections.synchronizedMap (new HashMap ()); private ImplementTxOutCacheDelta availableOutput = null; + private final Set recheck = new HashSet (); private final Set dependencyOrderedSet = new TreeSet (new Comparator () { @@ -186,17 +188,22 @@ public void run (TxOutCache cache) throws ValidationException cacheTransaction (t); sendTransaction (t, peer); notifyListener (t, false); + recheck.remove (t.getHash ()); } catch ( ValidationException e ) { - log.trace ("asking for inputs of " + t.getHash ()); - GetDataMessage get = (GetDataMessage) peer.createMessage ("getdata"); - for ( TxIn in : t.getInputs () ) + if ( !recheck.contains (t.getHash ()) ) { - get.getTransactions ().add (new Hash (in.getSourceHash ()).toByteArray ()); + log.trace ("asking for inputs of " + t.getHash ()); + GetDataMessage get = (GetDataMessage) peer.createMessage ("getdata"); + for ( TxIn in : t.getInputs () ) + { + get.getTransactions ().add (new Hash (in.getSourceHash ()).toByteArray ()); + } + get.getTransactions ().add (new Hash (t.getHash ()).toByteArray ()); + peer.send (get); + recheck.add (t.getHash ()); } - get.getTransactions ().add (new Hash (t.getHash ()).toByteArray ()); - peer.send (get); } } else From 6097c2f104a4bc03c14a4419d21604ffdf519d05 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 21 Nov 2013 00:21:23 +0100 Subject: [PATCH 217/299] debug why double spend --- .../main/java/com/bitsofproof/supernode/core/TxHandler.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index 03ab3d8e..93bfb049 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -358,6 +358,10 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo catch ( ValidationException e ) { log.debug ("Double spend: " + tx.getHash ()); + for ( TxIn in : tx.getInputs () ) + { + log.debug (tx.getHash () + " referred to:" + in.getSourceHash ()); + } unconfirmed.remove (tx.getHash ()); txi.remove (); notifyListener (tx, true); From ca21fc6ebc8e70907039de4698d2927697b8b6b8 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 21 Nov 2013 00:48:26 +0100 Subject: [PATCH 218/299] simplify dependency order to incoming order --- .../bitsofproof/supernode/core/TxHandler.java | 51 +++++++------------ 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index 93bfb049..ca5dfce5 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -17,14 +17,13 @@ import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TreeSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,28 +53,7 @@ public class TxHandler implements TrunkListener private ImplementTxOutCacheDelta availableOutput = null; private final Set recheck = new HashSet (); - private final Set dependencyOrderedSet = new TreeSet (new Comparator () - { - @Override - public int compare (Tx a, Tx b) - { - for ( TxIn in : b.getInputs () ) - { - if ( in.getSourceHash ().equals (a.getHash ()) ) - { - return -1; - } - } - for ( TxIn in : a.getInputs () ) - { - if ( in.getSourceHash ().equals (b.getHash ()) ) - { - return 1; - } - } - return a.getHash ().compareTo (b.getHash ()); - } - }); + private final List incomingOrder = new LinkedList (); private final List transactionListener = new ArrayList (); @@ -90,7 +68,7 @@ public void clear () { unconfirmed.clear (); availableOutput.clear (); - dependencyOrderedSet.clear (); + incomingOrder.clear (); } } @@ -155,7 +133,7 @@ public void process (final MempoolMessage m, final BitcoinPeer peer) InvMessage tm = (InvMessage) peer.createMessage ("inv"); synchronized ( unconfirmed ) { - for ( Tx tx : dependencyOrderedSet ) + for ( Tx tx : incomingOrder ) { tm.getTransactionHashes ().add (new Hash (tx.getHash ()).toByteArray ()); } @@ -246,7 +224,7 @@ private void cacheTransaction (Tx tx) availableOutput.remove (in.getSourceHash (), in.getIx ()); } - dependencyOrderedSet.add (tx); + incomingOrder.add (tx); } } @@ -311,7 +289,7 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo continue; } unconfirmed.put (tx.getHash (), tx); - dependencyOrderedSet.add (tx); + incomingOrder.add (tx); } } log.debug ("Pool size: " + unconfirmed.size ()); @@ -324,7 +302,16 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo if ( unconfirmed.containsKey (tx.getHash ()) ) { unconfirmed.remove (tx.getHash ()); - dependencyOrderedSet.remove (tx); + Iterator txi = incomingOrder.iterator (); + while ( txi.hasNext () ) + { + Tx t = txi.next (); + if ( t.getHash ().equals (tx.getHash ()) ) + { + txi.remove (); + break; + } + } notifyListener (tx, false); } else @@ -338,7 +325,7 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo availableOutput.reset (); - Iterator txi = dependencyOrderedSet.iterator (); + Iterator txi = incomingOrder.iterator (); while ( txi.hasNext () ) { Tx tx = txi.next (); @@ -358,10 +345,6 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo catch ( ValidationException e ) { log.debug ("Double spend: " + tx.getHash ()); - for ( TxIn in : tx.getInputs () ) - { - log.debug (tx.getHash () + " referred to:" + in.getSourceHash ()); - } unconfirmed.remove (tx.getHash ()); txi.remove (); notifyListener (tx, true); From 4969a529d6c001f041a019cf483a5796f746ffda Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 21 Nov 2013 09:30:00 +0100 Subject: [PATCH 219/299] faster remove from incomingOrder --- .../com/bitsofproof/supernode/core/TxHandler.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index ca5dfce5..a6684b93 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -301,17 +301,7 @@ public void trunkUpdate (final List removedBlocks, final List addedBlo tx.setBlockHash (blk.getHash ()); if ( unconfirmed.containsKey (tx.getHash ()) ) { - unconfirmed.remove (tx.getHash ()); - Iterator txi = incomingOrder.iterator (); - while ( txi.hasNext () ) - { - Tx t = txi.next (); - if ( t.getHash ().equals (tx.getHash ()) ) - { - txi.remove (); - break; - } - } + incomingOrder.remove (unconfirmed.remove (tx.getHash ())); notifyListener (tx, false); } else From 4aa5ac52bb893c51285f9da8bfb580d45c314e19 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 21 Nov 2013 09:59:56 +0100 Subject: [PATCH 220/299] missed to set lock on catchup --- .../java/com/bitsofproof/supernode/core/CachedBlockStore.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 1b4c3ed8..56d18163 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -378,6 +378,8 @@ public void catchUp (String hash, List added, List removed) throws Val { try { + lock.readLock ().lock (); + String trunk = hash; while ( !isOnTrunk (trunk) ) { From 42aa1c74918f771afa2b92554952e20eb7caf012 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 21 Nov 2013 10:10:21 +0100 Subject: [PATCH 221/299] do not process same tx in several threads. --- .../supernode/core/ImplementBCSAPI.java | 2 +- .../bitsofproof/supernode/core/TxHandler.java | 63 +++++++++++-------- 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index 16f16f73..a3abc874 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -426,7 +426,7 @@ public void onMessage (Message arg0) { reply (o.getJMSReplyTo (), null); } - log.debug ("blockRequest for " + hash + " replied"); + log.debug ("catchUpRequest for " + hash + " replied"); } catch ( Exception e ) { diff --git a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java index a6684b93..d7a5a2ad 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/TxHandler.java @@ -52,6 +52,7 @@ public class TxHandler implements TrunkListener private final Map unconfirmed = Collections.synchronizedMap (new HashMap ()); private ImplementTxOutCacheDelta availableOutput = null; private final Set recheck = new HashSet (); + private final Set processing = Collections.synchronizedSet (new HashSet ()); private final List incomingOrder = new LinkedList (); @@ -154,45 +155,53 @@ public void validateCacheAndSend (final Tx t, final BitcoinPeer peer) throws Val @Override public void run (TxOutCache cache) throws ValidationException { - if ( !unconfirmed.containsKey (t.getHash ()) ) + try { - if ( network.getStore ().getTransaction (t.getHash ()) == null ) + if ( !processing.contains (t.getHash ()) && !unconfirmed.containsKey (t.getHash ()) ) { - if ( network.getChain ().isSlave () && peer != null ) + processing.add (t.getHash ()); + if ( network.getStore ().getTransaction (t.getHash ()) == null ) { - try + if ( network.getChain ().isSlave () && peer != null ) { - network.getStore ().resolveTransactionInputs (t, availableOutput); - cacheTransaction (t); - sendTransaction (t, peer); - notifyListener (t, false); - recheck.remove (t.getHash ()); - } - catch ( ValidationException e ) - { - if ( !recheck.contains (t.getHash ()) ) + try + { + network.getStore ().resolveTransactionInputs (t, availableOutput); + cacheTransaction (t); + sendTransaction (t, peer); + notifyListener (t, false); + recheck.remove (t.getHash ()); + } + catch ( ValidationException e ) { - log.trace ("asking for inputs of " + t.getHash ()); - GetDataMessage get = (GetDataMessage) peer.createMessage ("getdata"); - for ( TxIn in : t.getInputs () ) + if ( !recheck.contains (t.getHash ()) ) { - get.getTransactions ().add (new Hash (in.getSourceHash ()).toByteArray ()); + log.trace ("asking for inputs of " + t.getHash ()); + GetDataMessage get = (GetDataMessage) peer.createMessage ("getdata"); + for ( TxIn in : t.getInputs () ) + { + get.getTransactions ().add (new Hash (in.getSourceHash ()).toByteArray ()); + } + get.getTransactions ().add (new Hash (t.getHash ()).toByteArray ()); + peer.send (get); + recheck.add (t.getHash ()); } - get.getTransactions ().add (new Hash (t.getHash ()).toByteArray ()); - peer.send (get); - recheck.add (t.getHash ()); } } - } - else - { - network.getStore ().validateTransaction (t, availableOutput); - cacheTransaction (t); - sendTransaction (t, peer); - notifyListener (t, false); + else + { + network.getStore ().validateTransaction (t, availableOutput); + cacheTransaction (t); + sendTransaction (t, peer); + notifyListener (t, false); + } } } } + finally + { + processing.remove (t.getHash ()); + } } }); if ( exception != null ) From 81897398a0ee5694e821cfc78591c81451dc25e1 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 21 Nov 2013 10:22:46 +0100 Subject: [PATCH 222/299] log catchup processing --- .../java/com/bitsofproof/supernode/core/CachedBlockStore.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 56d18163..42b2ba35 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -383,6 +383,7 @@ public void catchUp (String hash, List added, List removed) throws Val String trunk = hash; while ( !isOnTrunk (trunk) ) { + log.debug ("need to remove: " + trunk); CachedBlock b = cachedBlocks.get (trunk); removed.add (retrieveBlock (b)); b = b.previous; @@ -396,6 +397,7 @@ public void catchUp (String hash, List added, List removed) throws Val { break; } + log.debug ("need to add: " + trunk); added.add (retrieveBlock (q)); p = q.previous; } From da5440de997c8979f5992f23abacc2203c11587c Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 21 Nov 2013 10:32:40 +0100 Subject: [PATCH 223/299] fix catchUp request loop --- .../com/bitsofproof/supernode/core/CachedBlockStore.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 42b2ba35..9895fbec 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -390,8 +390,7 @@ public void catchUp (String hash, List added, List removed) throws Val trunk = b.hash; } CachedBlock q = currentHead.getLast (); - CachedBlock p = q.previous; - while ( p != null ) + while ( q != null ) { if ( q.hash.equals (trunk) ) { @@ -399,7 +398,7 @@ public void catchUp (String hash, List added, List removed) throws Val } log.debug ("need to add: " + trunk); added.add (retrieveBlock (q)); - p = q.previous; + q = q.previous; } Collections.reverse (added); } From c5fad87b1553d214f5451c1b19e309b7192d30d0 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 21 Nov 2013 10:38:12 +0100 Subject: [PATCH 224/299] corrected catch-up debug --- .../java/com/bitsofproof/supernode/core/CachedBlockStore.java | 2 +- .../java/com/bitsofproof/supernode/core/ImplementBCSAPI.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java index 9895fbec..072ed0d5 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java @@ -396,7 +396,7 @@ public void catchUp (String hash, List added, List removed) throws Val { break; } - log.debug ("need to add: " + trunk); + log.debug ("need to add: " + q.hash); added.add (retrieveBlock (q)); q = q.previous; } diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index a3abc874..10130d67 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -430,7 +430,7 @@ public void onMessage (Message arg0) } catch ( Exception e ) { - log.trace ("Rejected invalid block request ", e); + log.debug ("Rejected invalid block request ", e); } } }); From f904fde0d5fcca01238498182eb1e7f6a14badbe Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 21 Nov 2013 10:38:39 +0100 Subject: [PATCH 225/299] another debug fix --- .../java/com/bitsofproof/supernode/core/ImplementBCSAPI.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index 10130d67..63a73902 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -430,7 +430,7 @@ public void onMessage (Message arg0) } catch ( Exception e ) { - log.debug ("Rejected invalid block request ", e); + log.debug ("Rejected invalid catchUpRequest request ", e); } } }); From 9c0d51b546abe0d529c5ddea3e630efb2c8b7891 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 21 Nov 2013 10:48:21 +0100 Subject: [PATCH 226/299] increase timeout --- .../java/com/bitsofproof/supernode/api/JMSServerConnector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java index 0bfffaef..3bb58c03 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java @@ -54,7 +54,7 @@ public class JMSServerConnector implements BCSAPI private final Map messageDispatcher = new HashMap (); - private long timeout = 2 * 60 * 1000; // 2 min + private long timeout = 10 * 60 * 1000; // 2 min public void setTimeout (long timeout) { From 5690402a2cf7c98269e050fb1c7d5175c852eec6 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 21 Nov 2013 17:29:50 +0100 Subject: [PATCH 227/299] 1.3.1 --- api/pom.xml | 2 +- pom.xml | 2 +- server/bop-service.sh | 4 ++-- server/pom.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index d1eaf067..df83649d 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.3.0 + 1.3.1 ../ diff --git a/pom.xml b/pom.xml index 0f0ab540..e2b519ec 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.3.0 + 1.3.1 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom diff --git a/server/bop-service.sh b/server/bop-service.sh index d7c59557..2fd74ca7 100755 --- a/server/bop-service.sh +++ b/server/bop-service.sh @@ -95,9 +95,9 @@ start() { fi if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then - nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.3.0.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.3.1.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & else - sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.3.0.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & + sudo -n -u ${BOP_USER} nohup java ${JVM_FLAGS} -jar ${BOP_BASE}/server/target/bitsofproof-server-1.3.1.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null & fi echo $! > "${PID_FILE}" diff --git a/server/pom.xml b/server/pom.xml index 8ef30e1d..fc4187d1 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.3.0 + 1.3.1 ../ From 60c399c794b859e0a2b0d6c4a2d9f5ef9332f70d Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Thu, 21 Nov 2013 22:24:13 +0100 Subject: [PATCH 228/299] add mining to server in the box --- .../common/InMemoryBusConnectionFactory.java | 9 +- .../supernode/testbox/APIServerInABox.java | 76 +++++++++++++++ .../supernode/test/ServerInABoxTest.java | 96 +++++++++++++++++++ 3 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java diff --git a/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java b/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java index a87ccb4b..52137285 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java @@ -25,6 +25,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import javax.jms.BytesMessage; @@ -664,6 +665,7 @@ private static class MockConsumer implements MessageConsumer private LinkedBlockingQueue queue; private MessageListener listener; private final List peers; + private final Semaphore initialized = new Semaphore (0); public MockConsumer (List peers) { @@ -672,6 +674,7 @@ public MockConsumer (List peers) public synchronized LinkedBlockingQueue getQueue () { + initialized.acquireUninterruptibly (); return queue; } @@ -695,6 +698,7 @@ public synchronized void setMessageListener (final MessageListener listener) thr throw new JMSException ("Use either setListener or receive"); } this.listener = listener; + initialized.release (); } private synchronized MessageListener getListener () @@ -714,6 +718,7 @@ public Message receive () throws JMSException if ( queue == null ) { queue = new LinkedBlockingQueue (); + initialized.release (); } } try @@ -738,6 +743,7 @@ public Message receive (long timeout) throws JMSException if ( queue == null ) { queue = new LinkedBlockingQueue (); + initialized.release (); } } try @@ -762,6 +768,7 @@ public Message receiveNoWait () throws JMSException if ( queue == null ) { queue = new LinkedBlockingQueue (); + initialized.release (); } } return queue.poll (); @@ -824,7 +831,7 @@ public void run () log.error ("Uncaught exception in message listener", e); } } - else if ( c.getQueue () != null ) + else { c.getQueue ().put (md.getMessage ()); } diff --git a/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java b/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java index d1ce9c35..03084d89 100644 --- a/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java +++ b/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java @@ -3,8 +3,13 @@ import java.io.IOException; import java.math.BigInteger; import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.bitsofproof.supernode.api.BCSAPI; +import com.bitsofproof.supernode.api.BCSAPIException; import com.bitsofproof.supernode.api.Block; import com.bitsofproof.supernode.api.JMSServerConnector; import com.bitsofproof.supernode.api.Transaction; @@ -17,17 +22,22 @@ import com.bitsofproof.supernode.core.FixedAddressDiscovery; import com.bitsofproof.supernode.core.ImplementBCSAPI; import com.bitsofproof.supernode.core.TxHandler; +import com.bitsofproof.supernode.core.TxListener; import com.bitsofproof.supernode.model.LvlMemoryStore; import com.bitsofproof.supernode.model.LvlStore; +import com.bitsofproof.supernode.model.Tx; +import com.bitsofproof.supernode.wallet.Address; public class APIServerInABox { + private static final Logger log = LoggerFactory.getLogger (APIServerInABox.class); private final BitcoinNetwork network; private final InMemoryBusConnectionFactory connectionFactory = new InMemoryBusConnectionFactory (); private final Chain chain; private final TxHandler txhandler; private final ImplementBCSAPI bcsapi; private final JMSServerConnector api; + private Address newCoinsAddress; public APIServerInABox (Chain chain) throws IOException, ValidationException { @@ -58,6 +68,72 @@ public void reset () throws ValidationException txhandler.clear (); } + public void setNewCoinsAddress (Address a) + { + newCoinsAddress = a; + } + + public void mine (long milisBetweenBlocks, int nblocks) + { + String previousHash = chain.getGenesis ().getHash (); + final List current = new ArrayList (); + txhandler.addTransactionListener (new TxListener () + { + @Override + public void process (Tx transaction, boolean doubleSpend) + { + Transaction t = Transaction.fromWireDump (transaction.toWireDump ()); + synchronized ( current ) + { + while ( current.isEmpty () ) + { + try + { + current.wait (); + } + catch ( InterruptedException e ) + { + } + } + current.get (0).getTransactions ().add (t); + } + } + }); + for ( int blockHeight = 1; blockHeight <= nblocks; ++blockHeight ) + { + Transaction coinbase = null; + try + { + coinbase = Transaction.createCoinbase (newCoinsAddress, 50 * 100000000L, blockHeight); + Block block = createBlock (previousHash, coinbase); + synchronized ( current ) + { + current.clear (); + current.add (block); + current.notify (); + + try + { + current.wait (milisBetweenBlocks); + } + catch ( InterruptedException e ) + { + return; + } + + mineBlock (block); + previousHash = block.getHash (); + } + api.sendBlock (block); + } + catch ( ValidationException | BCSAPIException e ) + { + log.error ("Server in a box ", e); + } + } + + } + public BitcoinNetwork getNetwork () { return network; diff --git a/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java b/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java new file mode 100644 index 00000000..9e532de5 --- /dev/null +++ b/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java @@ -0,0 +1,96 @@ +package com.bitsofproof.supernode.test; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.Semaphore; + +import org.junit.BeforeClass; +import org.junit.Test; + +import com.bitsofproof.supernode.api.BCSAPI; +import com.bitsofproof.supernode.api.BCSAPIException; +import com.bitsofproof.supernode.api.Block; +import com.bitsofproof.supernode.api.Transaction; +import com.bitsofproof.supernode.api.TrunkListener; +import com.bitsofproof.supernode.common.ECKeyPair; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.core.UnitTestChain; +import com.bitsofproof.supernode.testbox.APIServerInABox; +import com.bitsofproof.supernode.wallet.AccountListener; +import com.bitsofproof.supernode.wallet.AccountManager; +import com.bitsofproof.supernode.wallet.Address; +import com.bitsofproof.supernode.wallet.AddressListAccountManager; +import com.bitsofproof.supernode.wallet.KeyListAccountManager; + +public class ServerInABoxTest +{ + private static APIServerInABox box; + private static BCSAPI api; + + @BeforeClass + public static void init () throws IOException, ValidationException + { + box = new APIServerInABox (new UnitTestChain ()); + api = box.getAPI (); + } + + @Test + public void run () throws BCSAPIException + { + final Address target = ECKeyPair.createNew (true).getAddress (); + AddressListAccountManager addm = new AddressListAccountManager (); + addm.addAddress (target.toByteArray ()); + api.registerTransactionListener (addm); + + ECKeyPair miner = ECKeyPair.createNew (true); + Address a = miner.getAddress (); + box.setNewCoinsAddress (a); + final KeyListAccountManager am = new KeyListAccountManager (); + am.addKey (miner); + api.registerTransactionListener (am); + + final Semaphore nb = new Semaphore (0); + api.registerTrunkListener (new TrunkListener () + { + @Override + public void trunkUpdate (List removed, List added) + { + try + { + api.sendTransaction (am.pay (target, 10 * 100000000L, 0)); + } + catch ( ValidationException | BCSAPIException e ) + { + } + nb.release (); + } + }); + + final Semaphore nb2 = new Semaphore (0); + am.addAccountListener (new AccountListener () + { + @Override + public void accountChanged (AccountManager account, Transaction t) + { + nb2.release (); + } + }); + final Semaphore nb3 = new Semaphore (0); + addm.addAccountListener (new AccountListener () + { + @Override + public void accountChanged (AccountManager account, Transaction t) + { + nb3.release (); + } + }); + box.mine (1000, 10); + nb.acquireUninterruptibly (10); + nb2.acquireUninterruptibly (10); + nb3.acquireUninterruptibly (10); + assertTrue (am.getBalance () == 10 * 40 * 100000000L); + assertTrue (addm.getBalance () == 10 * 10 * 100000000L); + } +} From ce7bbc16a60753d4d9a48b9612b8f8c72aa84913 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 22 Nov 2013 07:48:17 +0100 Subject: [PATCH 229/299] removing unused code --- server/scripts/drop.sql | 17 -- .../supernode/model/PooledDatasource.java | 181 ------------------ 2 files changed, 198 deletions(-) delete mode 100644 server/scripts/drop.sql delete mode 100644 server/src/main/java/com/bitsofproof/supernode/model/PooledDatasource.java diff --git a/server/scripts/drop.sql b/server/scripts/drop.sql deleted file mode 100644 index 14a8ba13..00000000 --- a/server/scripts/drop.sql +++ /dev/null @@ -1,17 +0,0 @@ -drop table peer; - -drop table tx_txin; - -drop table tx_txout; - -drop table txin; - -drop table txout; - -drop table blk_tx; - -drop table tx; - -drop table blk; - -drop table head; diff --git a/server/src/main/java/com/bitsofproof/supernode/model/PooledDatasource.java b/server/src/main/java/com/bitsofproof/supernode/model/PooledDatasource.java deleted file mode 100644 index c50fad84..00000000 --- a/server/src/main/java/com/bitsofproof/supernode/model/PooledDatasource.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2013 bits of proof zrt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.bitsofproof.supernode.model; - -import java.io.PrintWriter; -import java.sql.Connection; -import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import javax.sql.ConnectionEvent; -import javax.sql.ConnectionEventListener; -import javax.sql.ConnectionPoolDataSource; -import javax.sql.DataSource; -import javax.sql.PooledConnection; -import javax.sql.StatementEventListener; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class PooledDatasource implements DataSource -{ - private static final Logger log = LoggerFactory.getLogger (PooledDatasource.class); - private ConnectionPoolDataSource dataSource; - - public ConnectionPoolDataSource getDataSource () - { - return dataSource; - } - - public void setDataSource (ConnectionPoolDataSource dataSource) - { - this.dataSource = dataSource; - } - - @Override - public PrintWriter getLogWriter () throws SQLException - { - return dataSource.getLogWriter (); - } - - @Override - public int getLoginTimeout () throws SQLException - { - return dataSource.getLoginTimeout (); - } - - @Override - public void setLogWriter (PrintWriter arg0) throws SQLException - { - dataSource.setLogWriter (arg0); - } - - @Override - public void setLoginTimeout (int arg0) throws SQLException - { - dataSource.setLoginTimeout (arg0); - } - - @Override - public boolean isWrapperFor (Class arg0) throws SQLException - { - return false; - } - - @Override - public T unwrap (Class arg0) throws SQLException - { - return null; - } - - private final Set pool = Collections.synchronizedSet (new HashSet ()); - - private class MyPooledConnection implements PooledConnection - { - PooledConnection delegate; - - public MyPooledConnection (PooledConnection delegate) - { - this.delegate = delegate; - final MyPooledConnection thisConnection = this; - delegate.addConnectionEventListener (new ConnectionEventListener () - { - - @Override - public void connectionClosed (ConnectionEvent arg0) - { - pool.add (thisConnection); - } - - @Override - public void connectionErrorOccurred (ConnectionEvent arg0) - { - } - }); - } - - @Override - public void addConnectionEventListener (ConnectionEventListener listener) - { - delegate.addConnectionEventListener (listener); - } - - @Override - public void addStatementEventListener (StatementEventListener listener) - { - delegate.addStatementEventListener (listener); - } - - @Override - public void close () throws SQLException - { - delegate.close (); - } - - @Override - public Connection getConnection () throws SQLException - { - return delegate.getConnection (); - } - - @Override - public void removeConnectionEventListener (ConnectionEventListener listener) - { - delegate.removeConnectionEventListener (listener); - } - - @Override - public void removeStatementEventListener (StatementEventListener listener) - { - delegate.removeStatementEventListener (listener); - } - } - - @Override - public Connection getConnection () throws SQLException - { - MyPooledConnection c = null; - synchronized ( pool ) - { - if ( pool.isEmpty () ) - { - log.trace ("creating new connection "); - c = new MyPooledConnection (dataSource.getPooledConnection ()); - } - else - { - c = pool.iterator ().next (); - pool.remove (c); - } - } - return c.getConnection (); - } - - @Override - public Connection getConnection (String arg0, String arg1) throws SQLException - { - throw new SQLException ("not implemented"); - } - - public java.util.logging.Logger getParentLogger () throws SQLFeatureNotSupportedException - { - return java.util.logging.Logger.getLogger (java.util.logging.Logger.GLOBAL_LOGGER_NAME); - } - -} From 95c184d78b0339e7debee0378f025b7ca4c49033 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 22 Nov 2013 10:21:56 +0100 Subject: [PATCH 230/299] use consumer list copy to avoid concurrent modification of consumer list --- .../common/InMemoryBusConnectionFactory.java | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java b/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java index 52137285..b21b6680 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java @@ -812,34 +812,31 @@ public void run () List cl; synchronized ( consumer ) { - cl = consumer.get (md.getDestination ()); + cl = new ArrayList (); + cl.addAll (consumer.get (md.getDestination ())); } if ( cl != null ) { - synchronized ( cl ) + for ( MockConsumer c : cl ) { - for ( MockConsumer c : cl ) + if ( c.getListener () != null ) { - if ( c.getListener () != null ) + try { - try - { - c.getListener ().onMessage (md.getMessage ()); - } - catch ( Exception e ) - { - log.error ("Uncaught exception in message listener", e); - } + c.getListener ().onMessage (md.getMessage ()); } - else + catch ( Exception e ) { - c.getQueue ().put (md.getMessage ()); + log.error ("Uncaught exception in message listener", e); } } + else + { + c.getQueue ().put (md.getMessage ()); + } } } } - } catch ( InterruptedException e ) { From 6f215764282c7894b1ba87b88c586bb5852ff456 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 22 Nov 2013 10:42:05 +0100 Subject: [PATCH 231/299] add release plugin --- pom.xml | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index cc960ba3..e4d718d6 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ 1.16-bop-patch 1.0 1.6.6 - 1.7 + 1.7 1.48 1.1.2.RELEASE 2.9.0 @@ -63,7 +63,7 @@ 20090211 1.2 - + http://maven.bitsofproof.com/maven @@ -76,16 +76,22 @@ - scm:git:git://github.com/bitsofproof/supernode.git + scm:git:git@github.com:bitsofproof/supernode.git + scm:git@github.com:bitsofproof/supernode.git https://github.com/bitsofproof/supernode + HEAD - - - dtrott - http://maven.davidtrott.com/repository - - + + + central + http://repo1.maven.org/maven2 + + + dtrott + http://maven.davidtrott.com/repository + + @@ -122,6 +128,15 @@ org.apache.maven.plugins maven-source-plugin + + org.apache.maven.plugins + maven-release-plugin + 2.4.1 + + @{project.artifactId}-@{project.version} + true + + From f7864213bdf99c6d230ee116bce772d823199801 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 22 Nov 2013 10:44:01 +0100 Subject: [PATCH 232/299] corrected git repo reference --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e4d718d6..ce718c59 100644 --- a/pom.xml +++ b/pom.xml @@ -77,7 +77,7 @@ scm:git:git@github.com:bitsofproof/supernode.git - scm:git@github.com:bitsofproof/supernode.git + scm:git:git@github.com:bitsofproof/supernode.git https://github.com/bitsofproof/supernode HEAD From 705a14aa3532f9042f47b593b634a54b262d8eab Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 22 Nov 2013 10:47:14 +0100 Subject: [PATCH 233/299] 1.3.2-SNAPSHOT do not use snapshots --- api/pom.xml | 2 +- pom.xml | 3 +-- server/pom.xml | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 416514a8..e47c4844 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.3-SNAPSHOT + 1.3.2-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index ce718c59..a011bc14 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.bitsofproof supernode - 1.3-SNAPSHOT + 1.3.2-SNAPSHOT supernode The bitsofproof supernode implementation of the Bitcoin protocol pom @@ -134,7 +134,6 @@ 2.4.1 @{project.artifactId}-@{project.version} - true diff --git a/server/pom.xml b/server/pom.xml index 6392dc23..115b001f 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -9,7 +9,7 @@ com.bitsofproof supernode - 1.3-SNAPSHOT + 1.3.2-SNAPSHOT ../ From 518af462dae653a499a7f8386571b253fcf23564 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 22 Nov 2013 10:59:04 +0100 Subject: [PATCH 234/299] add source plugin version --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index a011bc14..7194675f 100644 --- a/pom.xml +++ b/pom.xml @@ -168,6 +168,7 @@ org.apache.maven.plugins maven-source-plugin + 2.2.1 attach-sources From 1fa1c63408f009daf677a3b76f61ff084a29f2d3 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 22 Nov 2013 11:11:16 +0100 Subject: [PATCH 235/299] miner account updates 2x --- .../java/com/bitsofproof/supernode/test/ServerInABoxTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java b/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java index 9e532de5..60149a27 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java @@ -88,8 +88,9 @@ public void accountChanged (AccountManager account, Transaction t) }); box.mine (1000, 10); nb.acquireUninterruptibly (10); - nb2.acquireUninterruptibly (10); + nb2.acquireUninterruptibly (20); nb3.acquireUninterruptibly (10); + long b = am.getBalance (); assertTrue (am.getBalance () == 10 * 40 * 100000000L); assertTrue (addm.getBalance () == 10 * 10 * 100000000L); } From 5c73b968178b7e627a7776b8a3d4c4774a521487 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 22 Nov 2013 11:21:59 +0100 Subject: [PATCH 236/299] [maven-release-plugin] prepare release supernode-1.3.2 --- api/pom.xml | 5 ++--- pom.xml | 9 ++++----- server/pom.xml | 5 ++--- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index e47c4844..9200568d 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -1,5 +1,4 @@ - + 4.0.0 bitsofproof-server-api bitsofproof-server-api @@ -9,7 +8,7 @@ com.bitsofproof supernode - 1.3.2-SNAPSHOT + 1.3.2 ../ diff --git a/pom.xml b/pom.xml index 7194675f..f3158427 100644 --- a/pom.xml +++ b/pom.xml @@ -1,9 +1,8 @@ - + 4.0.0 com.bitsofproof supernode - 1.3.2-SNAPSHOT + 1.3.2 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom @@ -79,7 +78,7 @@ scm:git:git@github.com:bitsofproof/supernode.git scm:git:git@github.com:bitsofproof/supernode.git https://github.com/bitsofproof/supernode - HEAD + supernode-1.3.2 @@ -158,7 +157,7 @@ - + diff --git a/server/pom.xml b/server/pom.xml index 115b001f..9608d572 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -1,5 +1,4 @@ - + 4.0.0 bitsofproof-server ${project.artifactId} @@ -9,7 +8,7 @@ com.bitsofproof supernode - 1.3.2-SNAPSHOT + 1.3.2 ../ From dc2523d6d03e5f7b87832a9758cc7345ae81c639 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 22 Nov 2013 11:22:04 +0100 Subject: [PATCH 237/299] [maven-release-plugin] prepare for next development iteration --- api/pom.xml | 2 +- pom.xml | 4 ++-- server/pom.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 9200568d..2ea8235e 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 1.3.2 + 1.3.3-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index f3158427..d7270c50 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.bitsofproof supernode - 1.3.2 + 1.3.3-SNAPSHOT supernode The bitsofproof supernode implementation of the Bitcoin protocol pom @@ -78,7 +78,7 @@ scm:git:git@github.com:bitsofproof/supernode.git scm:git:git@github.com:bitsofproof/supernode.git https://github.com/bitsofproof/supernode - supernode-1.3.2 + HEAD diff --git a/server/pom.xml b/server/pom.xml index 9608d572..dd360ab0 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 1.3.2 + 1.3.3-SNAPSHOT ../ From 2f0c46b58e9b53db49d7ffe8a20379039d8fe67e Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 22 Nov 2013 11:28:46 +0100 Subject: [PATCH 238/299] wrong tag name --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7194675f..37fbac3e 100644 --- a/pom.xml +++ b/pom.xml @@ -133,7 +133,7 @@ maven-release-plugin 2.4.1 - @{project.artifactId}-@{project.version} + @{project.version} From 01f50e68332f2dc3a3c76e542f3ea34602689097 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 22 Nov 2013 19:19:30 +0100 Subject: [PATCH 239/299] Miner thread and test --- .../supernode/testbox/APIServerInABox.java | 83 +++++++++---------- .../supernode/test/ServerInABoxTest.java | 83 ++++++++++++------- 2 files changed, 95 insertions(+), 71 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java b/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java index 03084d89..5f44b1c4 100644 --- a/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java +++ b/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java @@ -3,7 +3,9 @@ import java.io.IOException; import java.math.BigInteger; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -73,65 +75,62 @@ public void setNewCoinsAddress (Address a) newCoinsAddress = a; } - public void mine (long milisBetweenBlocks, int nblocks) + public void mine (final int nblocks, final int numberOfTxInBlock, final int timeout) { - String previousHash = chain.getGenesis ().getHash (); - final List current = new ArrayList (); - txhandler.addTransactionListener (new TxListener () + final List mempool = new ArrayList (); + final Set seen = new HashSet (); + + Thread miner = new Thread (new Runnable () { @Override - public void process (Tx transaction, boolean doubleSpend) + public void run () { - Transaction t = Transaction.fromWireDump (transaction.toWireDump ()); - synchronized ( current ) + + txhandler.addTransactionListener (new TxListener () { - while ( current.isEmpty () ) + @Override + public void process (Tx transaction, boolean doubleSpend) { - try - { - current.wait (); - } - catch ( InterruptedException e ) + if ( !transaction.getInputs ().get (0).getSourceHash ().equals (Hash.ZERO_HASH_STRING) && !seen.contains (transaction.getHash ()) ) { + Transaction t = Transaction.fromWireDump (transaction.toWireDump ()); + synchronized ( mempool ) + { + mempool.add (t); + seen.add (transaction.getHash ()); + } } } - current.get (0).getTransactions ().add (t); - } - } - }); - for ( int blockHeight = 1; blockHeight <= nblocks; ++blockHeight ) - { - Transaction coinbase = null; - try - { - coinbase = Transaction.createCoinbase (newCoinsAddress, 50 * 100000000L, blockHeight); - Block block = createBlock (previousHash, coinbase); - synchronized ( current ) + }); + String previousHash = chain.getGenesis ().getHash (); + for ( int blockHeight = 1; blockHeight <= nblocks; ++blockHeight ) { - current.clear (); - current.add (block); - current.notify (); - + Transaction coinbase = null; try { - current.wait (milisBetweenBlocks); + coinbase = Transaction.createCoinbase (newCoinsAddress, 50 * 100000000L, blockHeight); + Block block = createBlock (previousHash, coinbase); + + synchronized ( mempool ) + { + block.getTransactions ().addAll (mempool); + mempool.clear (); + } + + mineBlock (block); + previousHash = block.getHash (); + api.sendBlock (block); } - catch ( InterruptedException e ) + catch ( ValidationException | BCSAPIException e ) { - return; + log.error ("Server in a box ", e); } - - mineBlock (block); - previousHash = block.getHash (); } - api.sendBlock (block); } - catch ( ValidationException | BCSAPIException e ) - { - log.error ("Server in a box ", e); - } - } - + }); + miner.setName ("Miner in the box"); + miner.setDaemon (true); + miner.start (); } public BitcoinNetwork getNetwork () diff --git a/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java b/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java index 60149a27..1704412f 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java @@ -8,6 +8,8 @@ import org.junit.BeforeClass; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.bitsofproof.supernode.api.BCSAPI; import com.bitsofproof.supernode.api.BCSAPIException; @@ -26,6 +28,7 @@ public class ServerInABoxTest { + private static final Logger log = LoggerFactory.getLogger (ServerInABoxTest.class); private static APIServerInABox box; private static BCSAPI api; @@ -37,61 +40,83 @@ public static void init () throws IOException, ValidationException } @Test - public void run () throws BCSAPIException + public void run () throws BCSAPIException, InterruptedException { final Address target = ECKeyPair.createNew (true).getAddress (); - AddressListAccountManager addm = new AddressListAccountManager (); - addm.addAddress (target.toByteArray ()); - api.registerTransactionListener (addm); + AddressListAccountManager targetAccount = new AddressListAccountManager (); + targetAccount.addAddress (target.toByteArray ()); + api.registerTransactionListener (targetAccount); ECKeyPair miner = ECKeyPair.createNew (true); Address a = miner.getAddress (); box.setNewCoinsAddress (a); - final KeyListAccountManager am = new KeyListAccountManager (); - am.addKey (miner); - api.registerTransactionListener (am); + final KeyListAccountManager minerAccount = new KeyListAccountManager (); + minerAccount.addKey (miner); + api.registerTransactionListener (minerAccount); - final Semaphore nb = new Semaphore (0); + final Semaphore blockMined = new Semaphore (0); api.registerTrunkListener (new TrunkListener () { @Override public void trunkUpdate (List removed, List added) { - try - { - api.sendTransaction (am.pay (target, 10 * 100000000L, 0)); - } - catch ( ValidationException | BCSAPIException e ) - { - } - nb.release (); + blockMined.release (); } }); - final Semaphore nb2 = new Semaphore (0); - am.addAccountListener (new AccountListener () + final Semaphore minerAccountChanged = new Semaphore (0); + minerAccount.addAccountListener (new AccountListener () { @Override public void accountChanged (AccountManager account, Transaction t) { - nb2.release (); + minerAccountChanged.release (); } }); - final Semaphore nb3 = new Semaphore (0); - addm.addAccountListener (new AccountListener () + final Semaphore targetAccountChanged = new Semaphore (0); + targetAccount.addAccountListener (new AccountListener () { @Override public void accountChanged (AccountManager account, Transaction t) { - nb3.release (); + targetAccountChanged.release (); } }); - box.mine (1000, 10); - nb.acquireUninterruptibly (10); - nb2.acquireUninterruptibly (20); - nb3.acquireUninterruptibly (10); - long b = am.getBalance (); - assertTrue (am.getBalance () == 10 * 40 * 100000000L); - assertTrue (addm.getBalance () == 10 * 10 * 100000000L); + box.mine (10, 2, 1000); + for ( int i = 0; i < 9; ++i ) + { + try + { + blockMined.acquireUninterruptibly (); + minerAccountChanged.acquireUninterruptibly (); + if ( i > 0 ) + { + minerAccountChanged.acquireUninterruptibly (); + targetAccountChanged.acquireUninterruptibly (); + } + + Transaction t = minerAccount.pay (target, 10 * 100000000L, 0); + api.sendTransaction (t); + targetAccountChanged.acquireUninterruptibly (); + minerAccountChanged.acquireUninterruptibly (); + } + catch ( ValidationException | BCSAPIException e ) + { + } + } + Transaction t; + try + { + t = minerAccount.pay (target, 10 * 100000000L, 0); + api.sendTransaction (t); + targetAccountChanged.acquireUninterruptibly (); + minerAccountChanged.acquireUninterruptibly (); + } + catch ( ValidationException e ) + { + } + + assertTrue (minerAccount.getBalance () == 10 * 40 * 100000000L); + assertTrue (targetAccount.getBalance () == 10 * 10 * 100000000L); } } From 0e499617e44bf70187d31db1d3000e0833423246 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 22 Nov 2013 23:05:52 +0100 Subject: [PATCH 240/299] connection was static this linked test cases in maven arghhhh...... --- .../common/InMemoryBusConnectionFactory.java | 48 +++++++------------ 1 file changed, 17 insertions(+), 31 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java b/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java index b21b6680..76e26ce8 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java @@ -664,14 +664,8 @@ private static class MockConsumer implements MessageConsumer { private LinkedBlockingQueue queue; private MessageListener listener; - private final List peers; private final Semaphore initialized = new Semaphore (0); - public MockConsumer (List peers) - { - this.peers = peers; - } - public synchronized LinkedBlockingQueue getQueue () { initialized.acquireUninterruptibly (); @@ -777,10 +771,6 @@ public Message receiveNoWait () throws JMSException @Override public synchronized void close () throws JMSException { - synchronized ( peers ) - { - peers.remove (this); - } listener = null; queue = null; } @@ -812,28 +802,27 @@ public void run () List cl; synchronized ( consumer ) { - cl = new ArrayList (); - cl.addAll (consumer.get (md.getDestination ())); - } - if ( cl != null ) - { - for ( MockConsumer c : cl ) + cl = consumer.get (md.getDestination ()); + if ( cl != null ) { - if ( c.getListener () != null ) + for ( MockConsumer c : cl ) { - try + if ( c.getListener () != null ) { - c.getListener ().onMessage (md.getMessage ()); + try + { + c.getListener ().onMessage (md.getMessage ()); + } + catch ( Exception e ) + { + log.error ("Uncaught exception in message listener", e); + } } - catch ( Exception e ) + else { - log.error ("Uncaught exception in message listener", e); + c.getQueue ().put (md.getMessage ()); } } - else - { - c.getQueue ().put (md.getMessage ()); - } } } } @@ -982,13 +971,10 @@ public MessageConsumer createConsumer (Destination destination) throws JMSExcept cl = new ArrayList (); consumer.put (name, cl); } - } - MockConsumer c = new MockConsumer (cl); - synchronized ( cl ) - { + MockConsumer c = new MockConsumer (); cl.add (c); + return c; } - return c; } @Override @@ -1136,7 +1122,7 @@ public ConnectionConsumer createDurableConnectionConsumer (Topic topic, String s } - private static Connection connection; + private Connection connection; @Override public Connection createConnection () throws JMSException From 7f017287f0ea0aa8070b9f38720ccf381de7df7e Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 22 Nov 2013 23:06:34 +0100 Subject: [PATCH 241/299] cosmetics --- .../supernode/core/ImplementBCSAPI.java | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java index 63a73902..0c70a95f 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ImplementBCSAPI.java @@ -163,7 +163,7 @@ public void init () } } - private void closeSession (Session session) + private static void closeSession (Session session) { if ( session != null ) { @@ -778,13 +778,7 @@ public void process (Tx tx, boolean doubleSpend) } finally { - try - { - session.close (); - } - catch ( JMSException e ) - { - } + closeSession (session); } } @@ -844,13 +838,7 @@ public void trunkUpdate (final List removed, final List extended) } finally { - try - { - session.close (); - } - catch ( JMSException e ) - { - } + closeSession (session); } } From a5db3a1e5177d13f938707df354cae9cc0a96023 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 22 Nov 2013 23:07:36 +0100 Subject: [PATCH 242/299] use a queue for unconfirmed --- .../supernode/testbox/APIServerInABox.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java b/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java index 5f44b1c4..8c85454f 100644 --- a/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java +++ b/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java @@ -4,8 +4,9 @@ import java.math.BigInteger; import java.util.ArrayList; import java.util.HashSet; -import java.util.List; import java.util.Set; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -77,7 +78,7 @@ public void setNewCoinsAddress (Address a) public void mine (final int nblocks, final int numberOfTxInBlock, final int timeout) { - final List mempool = new ArrayList (); + final BlockingQueue mempool = new LinkedBlockingQueue (); final Set seen = new HashSet (); Thread miner = new Thread (new Runnable () @@ -91,12 +92,12 @@ public void run () @Override public void process (Tx transaction, boolean doubleSpend) { - if ( !transaction.getInputs ().get (0).getSourceHash ().equals (Hash.ZERO_HASH_STRING) && !seen.contains (transaction.getHash ()) ) + synchronized ( seen ) { - Transaction t = Transaction.fromWireDump (transaction.toWireDump ()); - synchronized ( mempool ) + if ( !transaction.getInputs ().get (0).getSourceHash ().equals (Hash.ZERO_HASH_STRING) && !seen.contains (transaction.getHash ()) ) { - mempool.add (t); + Transaction t = Transaction.fromWireDump (transaction.toWireDump ()); + mempool.offer (t); seen.add (transaction.getHash ()); } } @@ -111,10 +112,9 @@ public void process (Tx transaction, boolean doubleSpend) coinbase = Transaction.createCoinbase (newCoinsAddress, 50 * 100000000L, blockHeight); Block block = createBlock (previousHash, coinbase); - synchronized ( mempool ) + if ( blockHeight > 1 ) { - block.getTransactions ().addAll (mempool); - mempool.clear (); + mempool.drainTo (block.getTransactions ()); } mineBlock (block); From 275cab3bd1b559ad36799e69fd1f2d6381abc514 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 22 Nov 2013 23:07:55 +0100 Subject: [PATCH 243/299] better logging --- .../java/com/bitsofproof/supernode/test/ServerInABoxTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java b/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java index 1704412f..30e9416a 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java @@ -102,6 +102,7 @@ public void accountChanged (AccountManager account, Transaction t) } catch ( ValidationException | BCSAPIException e ) { + log.error ("REJECT ", e); } } Transaction t; @@ -114,6 +115,7 @@ public void accountChanged (AccountManager account, Transaction t) } catch ( ValidationException e ) { + log.error ("REJECT ", e); } assertTrue (minerAccount.getBalance () == 10 * 40 * 100000000L); From 61aaa331081204c29517daaf8e0a9c4102885e91 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 22 Nov 2013 23:12:44 +0100 Subject: [PATCH 244/299] [maven-release-plugin] prepare release 1.3.3 --- api/pom.xml | 2 +- pom.xml | 4 ++-- server/pom.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 2ea8235e..ed3c7c7f 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 1.3.3-SNAPSHOT + 1.3.3 ../ diff --git a/pom.xml b/pom.xml index 7578711b..83b840bb 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.bitsofproof supernode - 1.3.3-SNAPSHOT + 1.3.3 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom @@ -78,7 +78,7 @@ scm:git:git@github.com:bitsofproof/supernode.git scm:git:git@github.com:bitsofproof/supernode.git https://github.com/bitsofproof/supernode - HEAD + 1.3.3 diff --git a/server/pom.xml b/server/pom.xml index dd360ab0..c67ac67e 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 1.3.3-SNAPSHOT + 1.3.3 ../ From e27070b9265f76503279ff6dcd9655ffb1106bc4 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 22 Nov 2013 23:12:49 +0100 Subject: [PATCH 245/299] [maven-release-plugin] prepare for next development iteration --- api/pom.xml | 2 +- pom.xml | 4 ++-- server/pom.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index ed3c7c7f..e23796af 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 1.3.3 + 1.3.4-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index 83b840bb..a252844e 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.bitsofproof supernode - 1.3.3 + 1.3.4-SNAPSHOT supernode The bitsofproof supernode implementation of the Bitcoin protocol pom @@ -78,7 +78,7 @@ scm:git:git@github.com:bitsofproof/supernode.git scm:git:git@github.com:bitsofproof/supernode.git https://github.com/bitsofproof/supernode - 1.3.3 + HEAD diff --git a/server/pom.xml b/server/pom.xml index c67ac67e..5ffb0fe0 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 1.3.3 + 1.3.4-SNAPSHOT ../ From d518696a24c0e97bd7704799a786dcd5090e38a7 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 22 Nov 2013 23:37:54 +0100 Subject: [PATCH 246/299] process messages outstanding before closing session --- .../supernode/common/InMemoryBusConnectionFactory.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java b/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java index 76e26ce8..c803b3e3 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java @@ -771,8 +771,6 @@ public Message receiveNoWait () throws JMSException @Override public synchronized void close () throws JMSException { - listener = null; - queue = null; } } From 655a1c4587c196554d05d2db3b5a54e1e3f42c59 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 23 Nov 2013 12:48:35 +0100 Subject: [PATCH 247/299] [maven-release-plugin] prepare release 1.3.4 --- api/pom.xml | 2 +- pom.xml | 4 ++-- server/pom.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index e23796af..b40f129a 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 1.3.4-SNAPSHOT + 1.3.4 ../ diff --git a/pom.xml b/pom.xml index a252844e..28944770 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.bitsofproof supernode - 1.3.4-SNAPSHOT + 1.3.4 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom @@ -78,7 +78,7 @@ scm:git:git@github.com:bitsofproof/supernode.git scm:git:git@github.com:bitsofproof/supernode.git https://github.com/bitsofproof/supernode - HEAD + 1.3.4 diff --git a/server/pom.xml b/server/pom.xml index 5ffb0fe0..bb35da80 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 1.3.4-SNAPSHOT + 1.3.4 ../ From 965f8ab20e5d6d93c0a722218aac5711a91cf79f Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 23 Nov 2013 12:48:40 +0100 Subject: [PATCH 248/299] [maven-release-plugin] prepare for next development iteration --- api/pom.xml | 2 +- pom.xml | 4 ++-- server/pom.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index b40f129a..845f36f4 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 1.3.4 + 1.3.5-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index 28944770..4628f108 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.bitsofproof supernode - 1.3.4 + 1.3.5-SNAPSHOT supernode The bitsofproof supernode implementation of the Bitcoin protocol pom @@ -78,7 +78,7 @@ scm:git:git@github.com:bitsofproof/supernode.git scm:git:git@github.com:bitsofproof/supernode.git https://github.com/bitsofproof/supernode - 1.3.4 + HEAD diff --git a/server/pom.xml b/server/pom.xml index bb35da80..a31a2edb 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 1.3.4 + 1.3.5-SNAPSHOT ../ From e2b8c8571af13c86c97040aba2f33884dd1948b1 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sun, 24 Nov 2013 12:19:41 +0100 Subject: [PATCH 249/299] mine only after timout exceeded --- .../supernode/testbox/APIServerInABox.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java b/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java index 8c85454f..5ea9dbcc 100644 --- a/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java +++ b/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java @@ -7,6 +7,7 @@ import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -115,6 +116,21 @@ public void process (Tx transaction, boolean doubleSpend) if ( blockHeight > 1 ) { mempool.drainTo (block.getTransactions ()); + if ( block.getTransactions ().size () < numberOfTxInBlock ) + { + try + { + Transaction t = mempool.poll (timeout, TimeUnit.MILLISECONDS); + if ( t != null ) + { + block.getTransactions ().add (t); + mempool.drainTo (block.getTransactions ()); + } + } + catch ( InterruptedException e ) + { + } + } } mineBlock (block); From e6be1895c10afe852761abfc6f7f17c0f3c87605 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 30 Nov 2013 20:10:02 +0100 Subject: [PATCH 250/299] more concurrency in memory bus --- .../common/InMemoryBusConnectionFactory.java | 100 ++++++------------ 1 file changed, 32 insertions(+), 68 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java b/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java index c803b3e3..76c8559c 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java @@ -19,13 +19,12 @@ import java.security.SecureRandom; import java.util.ArrayList; import java.util.Enumeration; -import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import javax.jms.BytesMessage; @@ -662,15 +661,8 @@ private void sendToConsumer (Message message) private static class MockConsumer implements MessageConsumer { - private LinkedBlockingQueue queue; + private final LinkedBlockingQueue queue = new LinkedBlockingQueue (); private MessageListener listener; - private final Semaphore initialized = new Semaphore (0); - - public synchronized LinkedBlockingQueue getQueue () - { - initialized.acquireUninterruptibly (); - return queue; - } @Override public String getMessageSelector () throws JMSException @@ -679,41 +671,32 @@ public String getMessageSelector () throws JMSException } @Override - public synchronized MessageListener getMessageListener () throws JMSException + public MessageListener getMessageListener () throws JMSException { return listener; } @Override - public synchronized void setMessageListener (final MessageListener listener) throws JMSException + public void setMessageListener (final MessageListener listener) throws JMSException { - if ( queue != null ) - { - throw new JMSException ("Use either setListener or receive"); - } this.listener = listener; - initialized.release (); } - private synchronized MessageListener getListener () + public void putMessage (Message m) throws JMSException { - return listener; + if ( listener != null ) + { + throw new JMSException ("Use either setListener or receive"); + } + queue.offer (m); } @Override public Message receive () throws JMSException { - synchronized ( this ) + if ( listener != null ) { - if ( listener != null ) - { - throw new JMSException ("Use either setListener or receive"); - } - if ( queue == null ) - { - queue = new LinkedBlockingQueue (); - initialized.release (); - } + throw new JMSException ("Use either setListener or receive"); } try { @@ -728,17 +711,9 @@ public Message receive () throws JMSException @Override public Message receive (long timeout) throws JMSException { - synchronized ( this ) + if ( listener != null ) { - if ( listener != null ) - { - throw new JMSException ("Use either setListener or receive"); - } - if ( queue == null ) - { - queue = new LinkedBlockingQueue (); - initialized.release (); - } + throw new JMSException ("Use either setListener or receive"); } try { @@ -753,17 +728,9 @@ public Message receive (long timeout) throws JMSException @Override public Message receiveNoWait () throws JMSException { - synchronized ( this ) + if ( listener != null ) { - if ( listener != null ) - { - throw new JMSException ("Use either setListener or receive"); - } - if ( queue == null ) - { - queue = new LinkedBlockingQueue (); - initialized.release (); - } + throw new JMSException ("Use either setListener or receive"); } return queue.poll (); } @@ -798,34 +765,31 @@ public void run () if ( md != null ) { List cl; - synchronized ( consumer ) + cl = consumer.get (md.getDestination ()); + if ( cl != null ) { - cl = consumer.get (md.getDestination ()); - if ( cl != null ) + for ( MockConsumer c : cl ) { - for ( MockConsumer c : cl ) + if ( c.getMessageListener () != null ) { - if ( c.getListener () != null ) + try { - try - { - c.getListener ().onMessage (md.getMessage ()); - } - catch ( Exception e ) - { - log.error ("Uncaught exception in message listener", e); - } + c.getMessageListener ().onMessage (md.getMessage ()); } - else + catch ( Exception e ) { - c.getQueue ().put (md.getMessage ()); + log.error ("Uncaught exception in message listener", e); } } + else + { + c.putMessage (md.getMessage ()); + } } } } } - catch ( InterruptedException e ) + catch ( InterruptedException | JMSException e ) { } } while ( md != null || run ); @@ -960,6 +924,7 @@ public MessageConsumer createConsumer (Destination destination) throws JMSExcept name = ((MockTemporaryQueue) destination).getQueueName (); } + MockConsumer c = new MockConsumer (); List cl; synchronized ( consumer ) { @@ -969,10 +934,9 @@ public MessageConsumer createConsumer (Destination destination) throws JMSExcept cl = new ArrayList (); consumer.put (name, cl); } - MockConsumer c = new MockConsumer (); cl.add (c); - return c; } + return c; } @Override @@ -1045,7 +1009,7 @@ private class MockConnection implements Connection { private final List sessions = new ArrayList (); private final ExecutorService consumerExecutor = Executors.newCachedThreadPool (); - private final Map> consumer = new HashMap<> (); + private final Map> consumer = new ConcurrentHashMap<> (); private final LinkedBlockingQueue queue = new LinkedBlockingQueue<> (); @Override From 1ce59dd087c00e83cc633d4b400fc7f273882078 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 30 Nov 2013 20:23:59 +0100 Subject: [PATCH 251/299] log exception in account listener --- .../com/bitsofproof/supernode/wallet/BaseAccountManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java index c28be3a1..a64dfb10 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java @@ -698,6 +698,7 @@ protected void notifyListener (Transaction t) } catch ( Exception e ) { + log.error ("Uncaught exception in account listener", e); } } } From 7a235204b44d84fdc6867c87ffc55fa99fe78d16 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sun, 1 Dec 2013 18:18:18 +0100 Subject: [PATCH 252/299] if receiver pays fee then the last output pays the fee. --- .../supernode/wallet/AccountManager.java | 10 +-- .../bitsofproof/supernode/wallet/Address.java | 4 +- .../supernode/wallet/AddressConverter.java | 4 +- .../supernode/wallet/BaseAccountManager.java | 85 ++++--------------- .../bitsofproof/supernode/test/APITest.java | 10 +-- .../supernode/test/ServerInABoxTest.java | 4 +- 6 files changed, 25 insertions(+), 92 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java index 93e77e06..6edd5934 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java @@ -57,7 +57,7 @@ public interface UTXO public Collection getAddresses (); - public Transaction pay (Address receiver, long amount, long fee) throws ValidationException; + public Transaction pay (Address receiver, long amount, long fee, boolean senderPaysFee) throws ValidationException; public Transaction pay (Address receiver, long amount, boolean senderPaysFee) throws ValidationException; @@ -65,14 +65,6 @@ public interface UTXO public Transaction pay (List
receiver, List amounts, boolean senderPaysFee) throws ValidationException; - @Deprecated - public Transaction pay (byte[] receiver, long amount, long fee) throws ValidationException; - - @Deprecated - public Transaction pay (byte[] receiver, long amount) throws ValidationException; - - public Transaction split (long[] amounts, long fee) throws ValidationException; - public long getBalance (); public long getConfirmed (); diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/Address.java b/api/src/main/java/com/bitsofproof/supernode/wallet/Address.java index c6c7cd48..2383d646 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/Address.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/Address.java @@ -12,13 +12,13 @@ public class Address { public enum Type { - UNKNOWN, COMMON, P2SH + COMMON, P2SH }; private final Type type; private final byte[] bytes; - private Network network = Network.UNKNOWN;; + private Network network = Network.PRODUCTION; public Network getNetwork () { diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/AddressConverter.java b/api/src/main/java/com/bitsofproof/supernode/wallet/AddressConverter.java index b968fb8f..25a32db6 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/AddressConverter.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/AddressConverter.java @@ -27,8 +27,8 @@ public static Address fromSatoshiStyle (String s) throws ValidationException { try { - Network network = Network.UNKNOWN; - Address.Type type = Type.UNKNOWN; + Network network = Network.PRODUCTION; + Address.Type type = Type.COMMON; byte[] raw = ByteUtils.fromBase58 (s); if ( (raw[0] & 0xff) == 0x0 ) { diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java index a64dfb10..81f9b5dd 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java @@ -356,17 +356,17 @@ public Address getNextAddress () throws ValidationException } @Override - public Transaction pay (Address receiver, long amount, long fee) throws ValidationException + public Transaction pay (Address receiver, long amount, long fee, boolean senderPaysFee) throws ValidationException { List
a = new ArrayList
(); a.add (receiver); List v = new ArrayList (); v.add (amount); - return pay (a, v, fee, true); + return pay (a, v, fee, senderPaysFee); } @Override - public Transaction pay (List
receiver, List amounts, long fee, boolean senderPaysfee) throws ValidationException + public Transaction pay (List
receiver, List amounts, long fee, boolean senderPaysFee) throws ValidationException { synchronized ( confirmed ) { @@ -375,19 +375,11 @@ public Transaction pay (List
receiver, List amounts, long fee, bo { amount += a; } - log.trace ("pay " + amount + (senderPaysfee ? " " + fee : "")); - List sources; - if ( senderPaysfee ) - { - sources = getSufficientSources (amount, fee, null); - } - else - { - sources = getSufficientSources (amount, 0, null); - } + log.trace ("pay " + amount + (senderPaysFee ? " + " + fee : "")); + List sources = getSufficientSources (amount, senderPaysFee ? fee : 0, null); if ( sources == null ) { - throw new ValidationException ("Insufficient funds to pay " + (amount + (senderPaysfee ? fee : 0))); + throw new ValidationException ("Insufficient funds to pay " + amount + (senderPaysFee ? " + " + fee : "")); } long in = 0; for ( TransactionOutput o : sources ) @@ -399,11 +391,17 @@ public Transaction pay (List
receiver, List amounts, long fee, bo Iterator ai = amounts.iterator (); for ( Address r : receiver ) { - sinks.add (new TransactionSink (r, ai.next () - (!senderPaysfee ? (fee + receiver.size () - 1) / receiver.size () : 0))); + sinks.add (new TransactionSink (r, ai.next ())); } - if ( (in - amount - fee) > 0 ) + if ( !senderPaysFee ) { - TransactionSink change = new TransactionSink (getNextAddress (), in - amount - (senderPaysfee ? fee : 0)); + TransactionSink last = sinks.get (sinks.size () - 1); + sinks.set (sinks.size () - 1, new TransactionSink (last.getAddress (), + Math.max (last.getValue () - fee, 0))); + } + if ( (in - amount) > (senderPaysFee ? fee : 0) ) + { + TransactionSink change = new TransactionSink (getNextAddress (), in - amount - (senderPaysFee ? fee : 0)); log.trace ("change to " + change.getAddress () + " " + change.getValue ()); sinks.add (change); } @@ -412,12 +410,6 @@ public Transaction pay (List
receiver, List amounts, long fee, bo } } - @Override - public Transaction pay (byte[] receiver, long amount, long fee) throws ValidationException - { - return pay (new Address (Address.Type.COMMON, receiver), amount, fee); - } - @Override public Transaction pay (Address receiver, long amount, boolean senderPaysFee) throws ValidationException { @@ -429,7 +421,7 @@ public Transaction pay (Address receiver, long amount, boolean senderPaysFee) th } @Override - public Transaction pay (List
receiver, List amounts, boolean senderPaysfee) throws ValidationException + public Transaction pay (List
receiver, List amounts, boolean senderPaysFee) throws ValidationException { long fee = MINIMUM_FEE; long estimate = 0; @@ -438,7 +430,7 @@ public Transaction pay (List
receiver, List amounts, boolean send do { fee = Math.max (fee, estimate); - t = pay (receiver, amounts, fee, senderPaysfee); + t = pay (receiver, amounts, fee, senderPaysFee); estimate = estimateFee (t); if ( fee < estimate ) { @@ -449,49 +441,6 @@ public Transaction pay (List
receiver, List amounts, boolean send return t; } - @Override - public Transaction pay (byte[] receiver, long amount) throws ValidationException - { - return pay (new Address (Address.Type.COMMON, receiver), amount, true); - } - - @Override - public Transaction split (long[] amounts, long fee) throws ValidationException - { - synchronized ( confirmed ) - { - List sinks = new ArrayList (); - - long amount = 0; - for ( long a : amounts ) - { - amount += a; - } - List sources = getSufficientSources (amount, fee, null); - if ( sources == null ) - { - throw new ValidationException ("Insufficient funds to pay " + (amount + fee)); - } - long in = 0; - for ( TransactionOutput o : sources ) - { - in += o.getValue (); - } - for ( long a : amounts ) - { - TransactionSink target = new TransactionSink (getNextAddress (), a); - sinks.add (target); - } - if ( (in - amount - fee) > 0 ) - { - TransactionSink change = new TransactionSink (getNextAddress (), in - amount - fee); - sinks.add (change); - } - Collections.shuffle (sinks); - return createSpend (sources, sinks, fee); - } - } - public boolean updateWithTransaction (Transaction t) { synchronized ( confirmed ) diff --git a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java index 8fa0995a..da203d53 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/APITest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/APITest.java @@ -219,21 +219,13 @@ public void test () throws BCSAPIException, ValidationException, IOException // spend some long aliceStartingBalance = alice.getBalance (); - Transaction spend = alice.pay (bob.getNextKey ().getAddress (), 50 * COIN, FEE); + Transaction spend = alice.pay (bob.getNextKey ().getAddress (), 50 * COIN, FEE, true); api.sendTransaction (spend); aliceMonitor.expectUpdates (1); bobMonitor.expectUpdates (1); validationMonitor.expectTransactionValidations (1); assertTrue (bob.getBalance () == 50 * COIN); assertTrue (alice.getBalance () == aliceStartingBalance - bob.getBalance () - FEE); - - // split - aliceStartingBalance = alice.getBalance (); - spend = alice.split (new long[] { 1 * COIN, 2 * COIN }, FEE); - api.sendTransaction (spend); - aliceMonitor.expectUpdates (1); - validationMonitor.expectTransactionValidations (1); - assertTrue (alice.getBalance () == aliceStartingBalance - FEE); } private Block createBlock (String previous, Transaction coinbase) diff --git a/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java b/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java index 30e9416a..bcb8654d 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java @@ -95,7 +95,7 @@ public void accountChanged (AccountManager account, Transaction t) targetAccountChanged.acquireUninterruptibly (); } - Transaction t = minerAccount.pay (target, 10 * 100000000L, 0); + Transaction t = minerAccount.pay (target, 10 * 100000000L, 0, true); api.sendTransaction (t); targetAccountChanged.acquireUninterruptibly (); minerAccountChanged.acquireUninterruptibly (); @@ -108,7 +108,7 @@ public void accountChanged (AccountManager account, Transaction t) Transaction t; try { - t = minerAccount.pay (target, 10 * 100000000L, 0); + t = minerAccount.pay (target, 10 * 100000000L, 0, true); api.sendTransaction (t); targetAccountChanged.acquireUninterruptibly (); minerAccountChanged.acquireUninterruptibly (); From 3f666a08be04222ce433e9907be4fae9ee330f7a Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Mon, 2 Dec 2013 11:18:49 +0100 Subject: [PATCH 253/299] use Address instead of byte[] --- .../Address.java} | 142 +++++++++++++++--- .../com/bitsofproof/supernode/api/BCSAPI.java | 22 ++- .../supernode/api/JMSServerConnector.java | 23 +++ .../supernode/api/Transaction.java | 1 - .../supernode/api/TransactionOutput.java | 7 +- .../supernode/common/BloomFilter.java | 6 +- .../supernode/common/ECKeyPair.java | 2 +- .../supernode/common/ECPublicKey.java | 2 +- .../com/bitsofproof/supernode/common/Key.java | 2 +- .../supernode/wallet/AccountManager.java | 6 +- .../bitsofproof/supernode/wallet/Address.java | 127 ---------------- .../wallet/AddressListAccountManager.java | 31 ++-- .../supernode/wallet/BaseAccountManager.java | 32 ++-- .../wallet/ExtendedKeyAccountManager.java | 30 ++-- .../supernode/wallet/KeyFormatter.java | 7 +- .../wallet/KeyListAccountManager.java | 26 ++-- .../supernode/api/KeySerializerTest.java | 3 +- .../supernode/testbox/APIServerInABox.java | 2 +- .../supernode/test/AddressTest.java | 10 +- .../supernode/test/ServerInABoxTest.java | 4 +- 20 files changed, 242 insertions(+), 243 deletions(-) rename api/src/main/java/com/bitsofproof/supernode/{wallet/AddressConverter.java => api/Address.java} (57%) delete mode 100644 api/src/main/java/com/bitsofproof/supernode/wallet/Address.java diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/AddressConverter.java b/api/src/main/java/com/bitsofproof/supernode/api/Address.java similarity index 57% rename from api/src/main/java/com/bitsofproof/supernode/wallet/AddressConverter.java rename to api/src/main/java/com/bitsofproof/supernode/api/Address.java index 25a32db6..bc6bfae3 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/AddressConverter.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/Address.java @@ -1,28 +1,130 @@ -/* - * Copyright 2013 bits of proof zrt. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.bitsofproof.supernode.wallet; - -import com.bitsofproof.supernode.api.Network; +package com.bitsofproof.supernode.api; + +import org.bouncycastle.util.Arrays; + import com.bitsofproof.supernode.common.ByteUtils; import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ScriptFormat; +import com.bitsofproof.supernode.common.ScriptFormat.Opcode; import com.bitsofproof.supernode.common.ValidationException; -import com.bitsofproof.supernode.wallet.Address.Type; -public class AddressConverter +public class Address { + public enum Type + { + COMMON, P2SH + }; + + private final Type type; + private final byte[] bytes; + + private Network network = Network.PRODUCTION; + + public Network getNetwork () + { + return network; + } + + public void setNetwork (Network network) + { + this.network = network; + } + + public Address (Network network, Type type, byte[] address) throws ValidationException + { + this.network = network; + this.type = type; + if ( address.length != 20 ) + { + throw new ValidationException ("invalid digest length for an address"); + } + this.bytes = Arrays.clone (address); + } + + public Address (Type type, byte[] address) throws ValidationException + { + this.type = type; + if ( address.length != 20 ) + { + throw new ValidationException ("invalid digest length for an address"); + } + this.bytes = Arrays.clone (address); + } + + public Address (Network network, Address address) throws ValidationException + { + this.network = network; + this.type = address.type; + this.bytes = Arrays.clone (address.bytes); + } + + public Type getType () + { + return type; + } + + public byte[] toByteArray () + { + return Arrays.clone (bytes); + } + + public byte[] getAddressScript () throws ValidationException + { + ScriptFormat.Writer writer = new ScriptFormat.Writer (); + if ( type == Address.Type.COMMON ) + { + writer.writeToken (new ScriptFormat.Token (Opcode.OP_DUP)); + writer.writeToken (new ScriptFormat.Token (Opcode.OP_HASH160)); + writer.writeData (bytes); + writer.writeToken (new ScriptFormat.Token (Opcode.OP_EQUALVERIFY)); + writer.writeToken (new ScriptFormat.Token (Opcode.OP_CHECKSIG)); + } + else if ( type == Address.Type.P2SH ) + { + writer.writeToken (new ScriptFormat.Token (Opcode.OP_HASH160)); + writer.writeData (bytes); + writer.writeToken (new ScriptFormat.Token (Opcode.OP_EQUAL)); + } + else + { + throw new ValidationException ("unknown sink address type"); + } + return writer.toByteArray (); + } + + @Override + public int hashCode () + { + return Arrays.hashCode (bytes) + type.ordinal (); + } + + @Override + public boolean equals (Object obj) + { + if ( this == obj ) + { + return true; + } + if ( obj == null || getClass () != obj.getClass () ) + { + return false; + } + return Arrays.areEqual (bytes, ((Address) obj).bytes) && type == ((Address) obj).type; + } + + @Override + public String toString () + { + try + { + return toSatoshiStyle (this); + } + catch ( ValidationException e ) + { + return network.name () + ":" + type.name () + ":" + ByteUtils.toHex (bytes); + } + } + public static Address fromSatoshiStyle (String s) throws ValidationException { try diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java index aa169874..800c4aad 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java @@ -16,9 +16,10 @@ package com.bitsofproof.supernode.api; import java.util.Collection; +import java.util.Set; -import com.bitsofproof.supernode.common.ExtendedKey; import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; +import com.bitsofproof.supernode.common.ExtendedKey; /** * This is the API extensions to the bitsofproof supernode should build on @@ -134,6 +135,16 @@ public interface BCSAPI * @param listener * @throws BCSAPIException */ + public void scanTransactionsForAddresses (Set
addresses, UpdateMode mode, long after, TransactionListener listener) + throws BCSAPIException; + + /** + * Scan transactions using generic binary match. + * + * @param match + * @param listener + * @throws BCSAPIException + */ public void scanTransactions (Collection match, UpdateMode mode, long after, TransactionListener listener) throws BCSAPIException; /** @@ -146,6 +157,15 @@ public interface BCSAPI */ public void scanTransactions (ExtendedKey master, int firstIndex, int lookAhead, long after, TransactionListener listener) throws BCSAPIException; + /** + * Scan unspent transactions using generic binary match. + * + * @param match + * @param listener + * @throws BCSAPIException + */ + public void scanUTXOForAddresses (Set
addresses, UpdateMode mode, long after, TransactionListener listener) throws BCSAPIException; + /** * Scan unspent transactions using and address or outpoint in match. * diff --git a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java index 3bb58c03..8c0c4848 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java @@ -20,6 +20,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.Semaphore; import javax.jms.BytesMessage; @@ -312,6 +313,28 @@ public boolean isProduction () throws BCSAPIException return production = getBlockHeader ("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f") != null; } + @Override + public void scanTransactionsForAddresses (Set
addresses, UpdateMode mode, long after, TransactionListener listener) throws BCSAPIException + { + List al = new ArrayList<> (addresses.size ()); + for ( Address a : addresses ) + { + al.add (a.toByteArray ()); + } + scanTransactions (al, mode, after, listener); + } + + @Override + public void scanUTXOForAddresses (Set
addresses, UpdateMode mode, long after, TransactionListener listener) throws BCSAPIException + { + List al = new ArrayList<> (addresses.size ()); + for ( Address a : addresses ) + { + al.add (a.toByteArray ()); + } + scanUTXO (al, mode, after, listener); + } + @Override public void scanUTXO (Collection match, UpdateMode mode, long after, TransactionListener listener) throws BCSAPIException { diff --git a/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java b/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java index 2b0fba0a..0cb701b2 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/Transaction.java @@ -24,7 +24,6 @@ import com.bitsofproof.supernode.common.ScriptFormat; import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.common.WireFormat; -import com.bitsofproof.supernode.wallet.Address; import com.google.protobuf.ByteString; public class Transaction implements Serializable, Cloneable diff --git a/api/src/main/java/com/bitsofproof/supernode/api/TransactionOutput.java b/api/src/main/java/com/bitsofproof/supernode/api/TransactionOutput.java index 9443c5bf..b4948f56 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/TransactionOutput.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/TransactionOutput.java @@ -18,6 +18,7 @@ import java.io.Serializable; import java.util.List; +import com.bitsofproof.supernode.api.Address.Type; import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.ScriptFormat; import com.bitsofproof.supernode.common.ScriptFormat.Token; @@ -103,7 +104,7 @@ public static TransactionOutput fromWire (WireFormat.Reader reader) return o; } - public byte[] getOutputAddress () + public Address getOutputAddress () { if ( ScriptFormat.isPayToAddress (script) ) { @@ -111,7 +112,7 @@ public byte[] getOutputAddress () try { tokens = ScriptFormat.parse (script); - return tokens.get (2).data; + return new Address (Type.COMMON, tokens.get (2).data); } catch ( ValidationException e ) { @@ -123,7 +124,7 @@ else if ( ScriptFormat.isPayToScriptHash (script) ) try { tokens = ScriptFormat.parse (script); - return tokens.get (1).data; + return new Address (Type.P2SH, tokens.get (1).data); } catch ( ValidationException e ) { diff --git a/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java b/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java index ba9332fc..f56b19f0 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java @@ -22,7 +22,7 @@ import org.bouncycastle.util.Arrays; -import com.bitsofproof.supernode.wallet.AddressConverter; +import com.bitsofproof.supernode.api.Address; public class BloomFilter { @@ -68,7 +68,7 @@ public BloomFilter (byte[] data, long hashFunctions, long tweak, UpdateMode upda public void addAddress (String address, int addressFlag) throws ValidationException { - add (AddressConverter.fromSatoshiStyle (address, addressFlag)); + add (Address.fromSatoshiStyle (address, addressFlag)); } public static byte[] serializedOutpoint (String hash, long ix) @@ -91,7 +91,7 @@ public boolean containsOutpoint (String hash, long ix) public boolean containsAddress (String address, int addressFlag) throws ValidationException { - return contains (AddressConverter.fromSatoshiStyle (address, addressFlag)); + return contains (Address.fromSatoshiStyle (address, addressFlag)); } private void setBit (int n) diff --git a/api/src/main/java/com/bitsofproof/supernode/common/ECKeyPair.java b/api/src/main/java/com/bitsofproof/supernode/common/ECKeyPair.java index 653c196c..58885bc4 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/ECKeyPair.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/ECKeyPair.java @@ -36,7 +36,7 @@ import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; -import com.bitsofproof.supernode.wallet.Address; +import com.bitsofproof.supernode.api.Address; public class ECKeyPair implements Key { diff --git a/api/src/main/java/com/bitsofproof/supernode/common/ECPublicKey.java b/api/src/main/java/com/bitsofproof/supernode/common/ECPublicKey.java index ce58f0a4..69d7427f 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/ECPublicKey.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/ECPublicKey.java @@ -17,7 +17,7 @@ import org.bouncycastle.util.Arrays; -import com.bitsofproof.supernode.wallet.Address; +import com.bitsofproof.supernode.api.Address; public class ECPublicKey implements Key { diff --git a/api/src/main/java/com/bitsofproof/supernode/common/Key.java b/api/src/main/java/com/bitsofproof/supernode/common/Key.java index 8ab0b1e6..862bb8f7 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/Key.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/Key.java @@ -15,7 +15,7 @@ */ package com.bitsofproof.supernode.common; -import com.bitsofproof.supernode.wallet.Address; +import com.bitsofproof.supernode.api.Address; public interface Key extends Cloneable { diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java index 6edd5934..ce404b70 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java @@ -17,7 +17,9 @@ import java.util.Collection; import java.util.List; +import java.util.Set; +import com.bitsofproof.supernode.api.Address; import com.bitsofproof.supernode.api.BCSAPI; import com.bitsofproof.supernode.api.BCSAPIException; import com.bitsofproof.supernode.api.Transaction; @@ -53,9 +55,9 @@ public interface UTXO public Address getNextAddress () throws ValidationException; - public Key getKeyForAddress (byte[] address); + public Key getKeyForAddress (Address address); - public Collection getAddresses (); + public Set
getAddresses (); public Transaction pay (Address receiver, long amount, long fee, boolean senderPaysFee) throws ValidationException; diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/Address.java b/api/src/main/java/com/bitsofproof/supernode/wallet/Address.java deleted file mode 100644 index 2383d646..00000000 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/Address.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.bitsofproof.supernode.wallet; - -import org.bouncycastle.util.Arrays; - -import com.bitsofproof.supernode.api.Network; -import com.bitsofproof.supernode.common.ByteUtils; -import com.bitsofproof.supernode.common.ScriptFormat; -import com.bitsofproof.supernode.common.ScriptFormat.Opcode; -import com.bitsofproof.supernode.common.ValidationException; - -public class Address -{ - public enum Type - { - COMMON, P2SH - }; - - private final Type type; - private final byte[] bytes; - - private Network network = Network.PRODUCTION; - - public Network getNetwork () - { - return network; - } - - public void setNetwork (Network network) - { - this.network = network; - } - - public Address (Network network, Type type, byte[] address) throws ValidationException - { - this.network = network; - this.type = type; - if ( address.length != 20 ) - { - throw new ValidationException ("invalid digest length for an address"); - } - this.bytes = Arrays.clone (address); - } - - public Address (Type type, byte[] address) throws ValidationException - { - this.type = type; - if ( address.length != 20 ) - { - throw new ValidationException ("invalid digest length for an address"); - } - this.bytes = Arrays.clone (address); - } - - public Address (Network network, Address address) throws ValidationException - { - this.network = network; - this.type = address.type; - this.bytes = Arrays.clone (address.bytes); - } - - public Type getType () - { - return type; - } - - public byte[] toByteArray () - { - return Arrays.clone (bytes); - } - - public byte[] getAddressScript () throws ValidationException - { - ScriptFormat.Writer writer = new ScriptFormat.Writer (); - if ( type == Address.Type.COMMON ) - { - writer.writeToken (new ScriptFormat.Token (Opcode.OP_DUP)); - writer.writeToken (new ScriptFormat.Token (Opcode.OP_HASH160)); - writer.writeData (bytes); - writer.writeToken (new ScriptFormat.Token (Opcode.OP_EQUALVERIFY)); - writer.writeToken (new ScriptFormat.Token (Opcode.OP_CHECKSIG)); - } - else if ( type == Address.Type.P2SH ) - { - writer.writeToken (new ScriptFormat.Token (Opcode.OP_HASH160)); - writer.writeData (bytes); - writer.writeToken (new ScriptFormat.Token (Opcode.OP_EQUAL)); - } - else - { - throw new ValidationException ("unknown sink address type"); - } - return writer.toByteArray (); - } - - @Override - public int hashCode () - { - return Arrays.hashCode (bytes) + type.ordinal (); - } - - @Override - public boolean equals (Object obj) - { - if ( this == obj ) - { - return true; - } - if ( obj == null || getClass () != obj.getClass () ) - { - return false; - } - return Arrays.areEqual (bytes, ((Address) obj).bytes) && type == ((Address) obj).type; - } - - @Override - public String toString () - { - try - { - return AddressConverter.toSatoshiStyle (this); - } - catch ( ValidationException e ) - { - return network.name () + ":" + type.name () + ":" + ByteUtils.toHex (bytes); - } - } -} diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/AddressListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/AddressListAccountManager.java index 8d5c1f53..e8203876 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/AddressListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/AddressListAccountManager.java @@ -1,20 +1,18 @@ package com.bitsofproof.supernode.wallet; -import java.util.ArrayList; -import java.util.Collection; +import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.bitsofproof.supernode.api.Address; import com.bitsofproof.supernode.api.BCSAPI; import com.bitsofproof.supernode.api.BCSAPIException; import com.bitsofproof.supernode.api.Transaction; import com.bitsofproof.supernode.api.TransactionListener; import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; -import com.bitsofproof.supernode.common.ByteVector; import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ValidationException; @@ -22,29 +20,24 @@ public class AddressListAccountManager extends BaseAccountManager { private static final Logger log = LoggerFactory.getLogger (AddressListAccountManager.class); - private final Set addresses = new HashSet (); + private final Set
addresses = new HashSet<> (); @Override - public Collection getAddresses () + public Set
getAddresses () { - List al = new ArrayList (); - for ( ByteVector b : addresses ) - { - al.add (b.toByteArray ()); - } - return al; + return Collections.unmodifiableSet (addresses); } @Override - public Key getKeyForAddress (byte[] address) + public Key getKeyForAddress (Address address) { return null; } @Override - public boolean isOwnAddress (byte[] address) + public boolean isOwnAddress (Address address) { - return addresses.contains (new ByteVector (address)); + return addresses.contains (address); } @Override @@ -53,9 +46,9 @@ public Key getNextKey () throws ValidationException return null; } - public void addAddress (byte[] address) + public void addAddress (Address address) { - addresses.add (new ByteVector (address)); + addresses.add (address); } @Override @@ -63,7 +56,7 @@ public void syncHistory (BCSAPI api) throws BCSAPIException { reset (); log.trace ("Sync naddr: " + addresses.size ()); - api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () + api.scanTransactionsForAddresses (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () { @Override public void process (Transaction t) @@ -79,7 +72,7 @@ public void sync (BCSAPI api) throws BCSAPIException { reset (); log.trace ("Sync naddr: " + addresses.size ()); - api.scanUTXO (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () + api.scanTransactionsForAddresses (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () { @Override public void process (Transaction t) diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java index 81f9b5dd..1bc44c0f 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java @@ -17,11 +17,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.bitsofproof.supernode.api.Address; import com.bitsofproof.supernode.api.Transaction; import com.bitsofproof.supernode.api.TransactionInput; import com.bitsofproof.supernode.api.TransactionOutput; import com.bitsofproof.supernode.common.ByteUtils; -import com.bitsofproof.supernode.common.ByteVector; import com.bitsofproof.supernode.common.ECKeyPair; import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ScriptFormat; @@ -84,7 +84,7 @@ protected void reset () sending = createSendingUTXO (); } - public boolean isOwnAddress (byte[] address) + public boolean isOwnAddress (Address address) { return getKeyForAddress (address) != null; } @@ -162,7 +162,7 @@ protected Transaction createSpend (List sources, List addresses = new HashSet (); + Set
addresses = new HashSet<> (); for ( TransactionOutput o : confirmed.getUTXO () ) { - addresses.add (new ByteVector (o.getOutputAddress ())); + addresses.add (o.getOutputAddress ()); } for ( TransactionOutput o : change.getUTXO () ) { - addresses.add (new ByteVector (o.getOutputAddress ())); + addresses.add (o.getOutputAddress ()); } for ( TransactionOutput o : receiving.getUTXO () ) { - addresses.add (new ByteVector (o.getOutputAddress ())); + addresses.add (o.getOutputAddress ()); } - for ( ByteVector b : addresses ) + for ( Address a : addresses ) { - Key k = getKeyForAddress (b.toByteArray ()); + Key k = getKeyForAddress (a); if ( k != null ) { - print.println (ECKeyPair.serializeWIF (k) + " (" + ByteUtils.toHex (b.toByteArray ()) + ")"); + print.println (ECKeyPair.serializeWIF (k) + " (" + a + ")"); } } } @@ -734,7 +734,7 @@ private void dumpUTXO (PrintStream print, UTXO set) { for ( TransactionOutput o : set.getUTXO () ) { - print.println (o.getTxHash () + "[" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + o.getValue ()); + print.println (o.getTxHash () + "[" + o.getIx () + "] (" + o.getOutputAddress () + ") " + o.getValue ()); } print.println (); } diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeyAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeyAccountManager.java index 91754091..60257287 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeyAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeyAccountManager.java @@ -15,23 +15,21 @@ */ package com.bitsofproof.supernode.wallet; -import java.util.ArrayList; -import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.bitsofproof.supernode.api.Address; import com.bitsofproof.supernode.api.BCSAPI; import com.bitsofproof.supernode.api.BCSAPIException; import com.bitsofproof.supernode.api.Transaction; import com.bitsofproof.supernode.api.TransactionListener; import com.bitsofproof.supernode.api.TransactionOutput; -import com.bitsofproof.supernode.common.ByteVector; import com.bitsofproof.supernode.common.ExtendedKey; import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ValidationException; @@ -41,7 +39,7 @@ public class ExtendedKeyAccountManager extends BaseAccountManager implements Tra private static final Logger log = LoggerFactory.getLogger (ExtendedKeyAccountManager.class); private final Set usedKeys = new HashSet (); - private final Map keyIDForAddress = new HashMap (); + private final Map keyIDForAddress = new HashMap (); private ExtendedKey master; private int nextSequence; private int lookAhead = 100; @@ -84,7 +82,7 @@ protected void notifyListener (Transaction t) { if ( isOwnAddress (o.getOutputAddress ()) ) { - int keyId = keyIDForAddress.get (new ByteVector (o.getOutputAddress ())); + int keyId = keyIDForAddress.get (o.getOutputAddress ()); ensureLookAhead (keyId); } } @@ -103,7 +101,7 @@ private void ensureLookAhead (int from) catch ( ValidationException e ) { } - keyIDForAddress.put (new ByteVector (key.getAddress ().toByteArray ()), keyIDForAddress.size () + firstIndex); + keyIDForAddress.put (key.getAddress (), keyIDForAddress.size () + firstIndex); } } @@ -130,9 +128,9 @@ public Set getUsedKeys () return usedKeys; } - public Integer getKeyIDForAddress (byte[] address) + public Integer getKeyIDForAddress (Address address) { - Integer id = keyIDForAddress.get (new ByteVector (address)); + Integer id = keyIDForAddress.get (address); if ( id != null ) { usedKeys.add (id); @@ -141,7 +139,7 @@ public Integer getKeyIDForAddress (byte[] address) } @Override - public Key getKeyForAddress (byte[] address) + public Key getKeyForAddress (Address address) { Integer keyId = getKeyIDForAddress (address); if ( keyId == null ) @@ -159,17 +157,9 @@ public Key getKeyForAddress (byte[] address) } @Override - public Collection getAddresses () + public Set
getAddresses () { - List addresses = new ArrayList (); - for ( ByteVector v : keyIDForAddress.keySet () ) - { - if ( keyIDForAddress.get (v).intValue () < nextSequence ) - { - addresses.add (v.toByteArray ()); - } - } - return addresses; + return Collections.unmodifiableSet (keyIDForAddress.keySet ()); } private final TransactionListener processor = new TransactionListener () diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/KeyFormatter.java b/api/src/main/java/com/bitsofproof/supernode/wallet/KeyFormatter.java index dc10ba5d..9a171009 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/KeyFormatter.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/KeyFormatter.java @@ -32,6 +32,7 @@ import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.crypto.generators.SCrypt; +import com.bitsofproof.supernode.api.Address; import com.bitsofproof.supernode.api.Network; import com.bitsofproof.supernode.common.ByteUtils; import com.bitsofproof.supernode.common.ECKeyPair; @@ -244,7 +245,7 @@ private ECKeyPair parseBIP38NoEC (byte[] store, boolean compressed) throws Valid } ECKeyPair kp = new ECKeyPair (decrypted, compressed); - byte[] acs = Hash.hash (AddressConverter.toSatoshiStyle (new Address (network, kp.getAddress ())).getBytes ("US-ASCII")); + byte[] acs = Hash.hash (new Address (network, kp.getAddress ()).toString ().getBytes ("US-ASCII")); byte[] check = new byte[4]; System.arraycopy (acs, 0, check, 0, 4); if ( !Arrays.equals (check, addressHash) ) @@ -342,7 +343,7 @@ private ECKeyPair parseBIP38EC (byte[] store, boolean compressed, boolean hasLot new BigInteger (1, passfactor).multiply (new BigInteger (1, Hash.hash (seed))).remainder (SECNamedCurves.getByName ("secp256k1").getN ()); kp = new ECKeyPair (priv, compressed); - byte[] acs = Hash.hash (AddressConverter.toSatoshiStyle (new Address (network, kp.getAddress ())).getBytes ("US-ASCII")); + byte[] acs = Hash.hash (new Address (network, kp.getAddress ()).toString ().getBytes ("US-ASCII")); byte[] check = new byte[4]; System.arraycopy (acs, 0, check, 0, 4); if ( !Arrays.equals (check, addressHash) ) @@ -396,7 +397,7 @@ private byte[] bytesBIP38 (Key key) throws ValidationException byte[] xor = new byte[32]; try { - byte[] ac = Hash.hash (AddressConverter.toSatoshiStyle (new Address (network, key.getAddress ())).getBytes ("US-ASCII")); + byte[] ac = Hash.hash (new Address (network, key.getAddress ()).toString ().getBytes ("US-ASCII")); System.arraycopy (ac, 0, addressHash, 0, 4); System.arraycopy (ac, 0, store, 3, 4); byte[] derived = SCrypt.generate (passphrase.getBytes ("UTF-8"), addressHash, 16384, 8, 8, 64); diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/KeyListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/KeyListAccountManager.java index 44dc479f..485e04af 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/KeyListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/KeyListAccountManager.java @@ -2,20 +2,21 @@ import java.security.SecureRandom; import java.util.ArrayList; -import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.bitsofproof.supernode.api.Address; import com.bitsofproof.supernode.api.BCSAPI; import com.bitsofproof.supernode.api.BCSAPIException; import com.bitsofproof.supernode.api.Transaction; import com.bitsofproof.supernode.api.TransactionListener; import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; -import com.bitsofproof.supernode.common.ByteVector; import com.bitsofproof.supernode.common.ECKeyPair; import com.bitsofproof.supernode.common.Key; import com.bitsofproof.supernode.common.ValidationException; @@ -24,25 +25,20 @@ public class KeyListAccountManager extends BaseAccountManager { private static final Logger log = LoggerFactory.getLogger (KeyListAccountManager.class); - private final Map keyByAddress = new HashMap (); + private final Map keyByAddress = new HashMap<> (); private final List keys = new ArrayList (); private static final SecureRandom rnd = new SecureRandom (); @Override - public Collection getAddresses () + public Set
getAddresses () { - List a = new ArrayList (); - for ( ByteVector b : keyByAddress.keySet () ) - { - a.add (b.toByteArray ()); - } - return a; + return Collections.unmodifiableSet (keyByAddress.keySet ()); } @Override - public Key getKeyForAddress (byte[] address) + public Key getKeyForAddress (Address address) { - return keyByAddress.get (new ByteVector (address)); + return keyByAddress.get (address); } @Override @@ -54,7 +50,7 @@ public Key getNextKey () throws ValidationException public void addKey (ECKeyPair key) { keys.add (key); - keyByAddress.put (new ByteVector (key.getAddress ().toByteArray ()), key); + keyByAddress.put (key.getAddress (), key); } @Override @@ -62,7 +58,7 @@ public void syncHistory (BCSAPI api) throws BCSAPIException { reset (); log.trace ("Sync naddr: " + keys.size ()); - api.scanTransactions (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () + api.scanTransactionsForAddresses (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () { @Override public void process (Transaction t) @@ -78,7 +74,7 @@ public void sync (BCSAPI api) throws BCSAPIException { reset (); log.trace ("Sync naddr: " + keys.size ()); - api.scanUTXO (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () + api.scanUTXOForAddresses (getAddresses (), UpdateMode.all, getCreated (), new TransactionListener () { @Override public void process (Transaction t) diff --git a/api/src/test/java/com/bitsofproof/supernode/api/KeySerializerTest.java b/api/src/test/java/com/bitsofproof/supernode/api/KeySerializerTest.java index ac0a0e89..4afc47b6 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/KeySerializerTest.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/KeySerializerTest.java @@ -32,7 +32,6 @@ import com.bitsofproof.supernode.common.ECKeyPair; import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.ValidationException; -import com.bitsofproof.supernode.wallet.AddressConverter; import com.bitsofproof.supernode.wallet.KeyFormatter; public class KeySerializerTest @@ -71,7 +70,7 @@ public void wifTest () throws IOException, JSONException, ValidationException { JSONArray test = testData.getJSONArray (i); ECKeyPair kp = formatter.parseSerializedKey (test.getString (1)); - String address = AddressConverter.toSatoshiStyle (Hash.keyHash (kp.getPublic ()), 0x0); + String address = Address.toSatoshiStyle (Hash.keyHash (kp.getPublic ()), 0x0); assertTrue (test.getString (0).equals (address)); String serialized = formatter.serializeKey (kp); assertTrue (test.getString (1).equals (serialized)); diff --git a/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java b/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java index 5ea9dbcc..94f8f650 100644 --- a/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java +++ b/server/src/main/java/com/bitsofproof/supernode/testbox/APIServerInABox.java @@ -12,6 +12,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.bitsofproof.supernode.api.Address; import com.bitsofproof.supernode.api.BCSAPI; import com.bitsofproof.supernode.api.BCSAPIException; import com.bitsofproof.supernode.api.Block; @@ -30,7 +31,6 @@ import com.bitsofproof.supernode.model.LvlMemoryStore; import com.bitsofproof.supernode.model.LvlStore; import com.bitsofproof.supernode.model.Tx; -import com.bitsofproof.supernode.wallet.Address; public class APIServerInABox { diff --git a/server/src/test/java/com/bitsofproof/supernode/test/AddressTest.java b/server/src/test/java/com/bitsofproof/supernode/test/AddressTest.java index a09e650f..a00ee66f 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/AddressTest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/AddressTest.java @@ -24,9 +24,9 @@ import org.junit.Test; +import com.bitsofproof.supernode.api.Address; import com.bitsofproof.supernode.common.ByteUtils; import com.bitsofproof.supernode.common.ValidationException; -import com.bitsofproof.supernode.wallet.AddressConverter; public class AddressTest { @@ -45,9 +45,9 @@ public void base58Test () throws ValidationException public void satoshiAddressTest () throws ValidationException, UnsupportedEncodingException { // some real addresses - assertTrue (AddressConverter.toSatoshiStyle (ByteUtils.fromHex ("9e969049aefe972e41aaefac385296ce18f30751"), 0x0).equals ( + assertTrue (Address.toSatoshiStyle (ByteUtils.fromHex ("9e969049aefe972e41aaefac385296ce18f30751"), 0x0).equals ( "1FTY8etSpSW3xv6s2XRrYE77rrRfza8aJJ")); - assertTrue (AddressConverter.toSatoshiStyle (ByteUtils.fromHex ("623dbe779a29c6bc2615cd7bf5a35453f495e229"), 0x0).equals ( + assertTrue (Address.toSatoshiStyle (ByteUtils.fromHex ("623dbe779a29c6bc2615cd7bf5a35453f495e229"), 0x0).equals ( "19xTBrDcnZiJSMuzirE7SfcsjkG1ghp1RL")); // some random @@ -56,8 +56,8 @@ public void satoshiAddressTest () throws ValidationException, UnsupportedEncodin { BigInteger n = new BigInteger (160, rnd); byte[] keyDigest = n.toByteArray (); - String a = AddressConverter.toSatoshiStyle (keyDigest, 0x0); - byte[] check = AddressConverter.fromSatoshiStyle (a, 0x0); + String a = Address.toSatoshiStyle (keyDigest, 0x0); + byte[] check = Address.fromSatoshiStyle (a, 0x0); assertTrue (Arrays.equals (check, keyDigest)); } } diff --git a/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java b/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java index bcb8654d..08b85012 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/ServerInABoxTest.java @@ -11,6 +11,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.bitsofproof.supernode.api.Address; import com.bitsofproof.supernode.api.BCSAPI; import com.bitsofproof.supernode.api.BCSAPIException; import com.bitsofproof.supernode.api.Block; @@ -22,7 +23,6 @@ import com.bitsofproof.supernode.testbox.APIServerInABox; import com.bitsofproof.supernode.wallet.AccountListener; import com.bitsofproof.supernode.wallet.AccountManager; -import com.bitsofproof.supernode.wallet.Address; import com.bitsofproof.supernode.wallet.AddressListAccountManager; import com.bitsofproof.supernode.wallet.KeyListAccountManager; @@ -44,7 +44,7 @@ public void run () throws BCSAPIException, InterruptedException { final Address target = ECKeyPair.createNew (true).getAddress (); AddressListAccountManager targetAccount = new AddressListAccountManager (); - targetAccount.addAddress (target.toByteArray ()); + targetAccount.addAddress (target); api.registerTransactionListener (targetAccount); ECKeyPair miner = ECKeyPair.createNew (true); From f69d1bbce3ea85c72dd6d32667417b0dca195af0 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Mon, 2 Dec 2013 11:22:24 +0100 Subject: [PATCH 254/299] [maven-release-plugin] prepare release 1.3.5 --- api/pom.xml | 2 +- pom.xml | 4 ++-- server/pom.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 845f36f4..ab137947 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 1.3.5-SNAPSHOT + 1.3.5 ../ diff --git a/pom.xml b/pom.xml index 4628f108..3d98a88f 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.bitsofproof supernode - 1.3.5-SNAPSHOT + 1.3.5 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom @@ -78,7 +78,7 @@ scm:git:git@github.com:bitsofproof/supernode.git scm:git:git@github.com:bitsofproof/supernode.git https://github.com/bitsofproof/supernode - HEAD + 1.3.5 diff --git a/server/pom.xml b/server/pom.xml index a31a2edb..20c4081c 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 1.3.5-SNAPSHOT + 1.3.5 ../ From 6cad17fff260c2a6329131dd32eaf8229ae6eac6 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Mon, 2 Dec 2013 11:22:30 +0100 Subject: [PATCH 255/299] [maven-release-plugin] prepare for next development iteration --- api/pom.xml | 2 +- pom.xml | 4 ++-- server/pom.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index ab137947..d836859d 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 1.3.5 + 1.3.6-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index 3d98a88f..c5d4c443 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.bitsofproof supernode - 1.3.5 + 1.3.6-SNAPSHOT supernode The bitsofproof supernode implementation of the Bitcoin protocol pom @@ -78,7 +78,7 @@ scm:git:git@github.com:bitsofproof/supernode.git scm:git:git@github.com:bitsofproof/supernode.git https://github.com/bitsofproof/supernode - 1.3.5 + HEAD diff --git a/server/pom.xml b/server/pom.xml index 20c4081c..ee79113e 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 1.3.5 + 1.3.6-SNAPSHOT ../ From 6123c5d87193d1b34a08363dcdc4eee94abc3a11 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sun, 8 Dec 2013 09:17:14 +0100 Subject: [PATCH 256/299] Missing copyright --- .../wallet/AddressListAccountManager.java | 15 +++++++++++++++ .../supernode/wallet/BaseAccountManager.java | 15 +++++++++++++++ .../supernode/wallet/ExtendedKeySetWallet.java | 15 +++++++++++++++ .../supernode/wallet/KeyListAccountManager.java | 15 +++++++++++++++ .../supernode/wallet/SimpleFileWallet.java | 15 +++++++++++++++ .../com/bitsofproof/supernode/wallet/Wallet.java | 15 +++++++++++++++ 6 files changed, 90 insertions(+) diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/AddressListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/AddressListAccountManager.java index e8203876..0b025711 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/AddressListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/AddressListAccountManager.java @@ -1,3 +1,18 @@ +/* + * Copyright 2013 bits of proof zrt. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.bitsofproof.supernode.wallet; import java.util.Collections; diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java index 1bc44c0f..d0894651 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java @@ -1,3 +1,18 @@ +/* + * Copyright 2013 bits of proof zrt. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.bitsofproof.supernode.wallet; import java.io.PrintStream; diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeySetWallet.java b/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeySetWallet.java index 4ce488b5..15733c04 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeySetWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/ExtendedKeySetWallet.java @@ -1,3 +1,18 @@ +/* + * Copyright 2013 bits of proof zrt. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.bitsofproof.supernode.wallet; import java.util.HashMap; diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/KeyListAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/KeyListAccountManager.java index 485e04af..1bd5d15a 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/KeyListAccountManager.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/KeyListAccountManager.java @@ -1,3 +1,18 @@ +/* + * Copyright 2013 bits of proof zrt. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.bitsofproof.supernode.wallet; import java.security.SecureRandom; diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/SimpleFileWallet.java b/api/src/main/java/com/bitsofproof/supernode/wallet/SimpleFileWallet.java index 8060591d..a30f7e8f 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/SimpleFileWallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/SimpleFileWallet.java @@ -1,3 +1,18 @@ +/* + * Copyright 2013 bits of proof zrt. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.bitsofproof.supernode.wallet; import java.io.File; diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/Wallet.java b/api/src/main/java/com/bitsofproof/supernode/wallet/Wallet.java index f4f2ab36..328e0b56 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/Wallet.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/Wallet.java @@ -1,3 +1,18 @@ +/* + * Copyright 2013 bits of proof zrt. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.bitsofproof.supernode.wallet; import com.bitsofproof.supernode.common.ValidationException; From 1a6d5f27b4aeee1cb4dbd47fb9c44a96568ffa90 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sun, 8 Dec 2013 09:17:49 +0100 Subject: [PATCH 257/299] add BIP39 support --- .../bitsofproof/supernode/wallet/BIP39.java | 2144 +++++++++++++++++ .../bitsofproof/supernode/api/BIP39Test.java | 76 + api/src/test/resources/BIP39.json | 124 + 3 files changed, 2344 insertions(+) create mode 100644 api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java create mode 100644 api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java create mode 100644 api/src/test/resources/BIP39.json diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java b/api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java new file mode 100644 index 00000000..f11f5644 --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java @@ -0,0 +1,2144 @@ +/* + * Copyright 2013 bits of proof zrt. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bitsofproof.supernode.wallet; + +import java.io.UnsupportedEncodingException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ValidationException; + +public class BIP39 +{ + public static byte[] getSeed (String mnenonic, String passphrase) throws NoSuchAlgorithmException, NoSuchProviderException, + InvalidKeyException, UnsupportedEncodingException + { + Mac mac = Mac.getInstance ("HmacSHA512", "BC"); + SecretKey seedkey = new SecretKeySpec (("mnemonic" + passphrase).getBytes ("UTF-8"), "HmacSHA512"); + mac.init (seedkey); + byte[] seed = mnenonic.getBytes (); + for ( int i = 0; i < 10000; ++i ) + { + seed = mac.doFinal (seed); + } + return seed; + } + + public static String getMnemonic (byte[] data) throws ValidationException + { + return getMnemonic (data, english); + } + + public static String getMnemonic (byte[] data, List dictionary) throws ValidationException + { + if ( data.length % 4 != 0 || data.length < 16 || data.length > 32 ) + { + throw new ValidationException ("Invalid data length for mnemonic"); + } + byte check = Hash.sha256 (data)[0]; + + boolean[] bits = new boolean[(data.length + 1) * 8]; + + for ( int i = 0; i < data.length; i++ ) + { + for ( int j = 0; j < 8; j++ ) + { + bits[8 * i + j] = (data[i] & (1 << (7 - j))) > 0; + } + } + for ( int i = 0; i < data.length / 4; i++ ) + { + bits[8 * data.length + i] = (check & (1 << (7 - i))) > 0; + } + + int mlen = data.length * 3 / 4; + StringBuffer mnemo = new StringBuffer (); + for ( int i = 0; i < mlen; i++ ) + { + int idx = 0; + for ( int j = 0; j < 11; j++ ) + { + idx += (bits[i * 11 + j] ? 1 : 0) << (10 - j); + } + mnemo.append (dictionary.get (idx)); + if ( i < mlen - 1 ) + { + mnemo.append (" "); + } + } + return mnemo.toString (); + } + + public final static List english = Collections.unmodifiableList (Arrays.asList ( + "abandon", + "ability", + "able", + "about", + "above", + "absent", + "absorb", + "abstract", + "absurd", + "abuse", + "access", + "accident", + "account", + "accuse", + "achieve", + "acid", + "acoustic", + "acquire", + "across", + "act", + "action", + "actor", + "actress", + "actual", + "adapt", + "add", + "addict", + "address", + "adjust", + "admit", + "adult", + "advance", + "advice", + "aerobic", + "affair", + "afford", + "afraid", + "again", + "age", + "agent", + "agree", + "ahead", + "aim", + "air", + "airport", + "aisle", + "alarm", + "album", + "alcohol", + "alert", + "alien", + "all", + "alley", + "allow", + "almost", + "alone", + "alpha", + "already", + "also", + "alter", + "always", + "amateur", + "amazing", + "among", + "amount", + "amused", + "analyst", + "anchor", + "ancient", + "anger", + "angle", + "angry", + "animal", + "ankle", + "announce", + "annual", + "another", + "answer", + "antenna", + "antique", + "anxiety", + "any", + "apart", + "apology", + "appear", + "apple", + "approve", + "april", + "arch", + "arctic", + "area", + "arena", + "argue", + "arm", + "armed", + "armor", + "army", + "around", + "arrange", + "arrest", + "arrive", + "arrow", + "art", + "artefact", + "artist", + "artwork", + "ask", + "aspect", + "assault", + "asset", + "assist", + "assume", + "asthma", + "athlete", + "atom", + "attack", + "attend", + "attitude", + "attract", + "auction", + "audit", + "august", + "aunt", + "author", + "auto", + "autumn", + "average", + "avocado", + "avoid", + "awake", + "aware", + "away", + "awesome", + "awful", + "awkward", + "axis", + "baby", + "bachelor", + "bacon", + "badge", + "bag", + "balance", + "balcony", + "ball", + "bamboo", + "banana", + "banner", + "bar", + "barely", + "bargain", + "barrel", + "base", + "basic", + "basket", + "battle", + "beach", + "bean", + "beauty", + "because", + "become", + "beef", + "before", + "begin", + "behave", + "behind", + "believe", + "below", + "belt", + "bench", + "benefit", + "best", + "betray", + "better", + "between", + "beyond", + "bicycle", + "bid", + "bike", + "bind", + "biology", + "bird", + "birth", + "bitter", + "black", + "blade", + "blame", + "blanket", + "blast", + "bleak", + "bless", + "blind", + "blood", + "blossom", + "blouse", + "blue", + "blur", + "blush", + "board", + "boat", + "body", + "boil", + "bomb", + "bone", + "bonus", + "book", + "boost", + "border", + "boring", + "borrow", + "boss", + "bottom", + "bounce", + "box", + "boy", + "bracket", + "brain", + "brand", + "brass", + "brave", + "bread", + "breeze", + "brick", + "bridge", + "brief", + "bright", + "bring", + "brisk", + "broccoli", + "broken", + "bronze", + "broom", + "brother", + "brown", + "brush", + "bubble", + "buddy", + "budget", + "buffalo", + "build", + "bulb", + "bulk", + "bullet", + "bundle", + "bunker", + "burden", + "burger", + "burst", + "bus", + "business", + "busy", + "butter", + "buyer", + "buzz", + "cabbage", + "cabin", + "cable", + "cactus", + "cage", + "cake", + "call", + "calm", + "camera", + "camp", + "can", + "canal", + "cancel", + "candy", + "cannon", + "canoe", + "canvas", + "canyon", + "capable", + "capital", + "captain", + "car", + "carbon", + "card", + "cargo", + "carpet", + "carry", + "cart", + "case", + "cash", + "casino", + "castle", + "casual", + "cat", + "catalog", + "catch", + "category", + "cattle", + "caught", + "cause", + "caution", + "cave", + "ceiling", + "celery", + "cement", + "census", + "century", + "cereal", + "certain", + "chair", + "chalk", + "champion", + "change", + "chaos", + "chapter", + "charge", + "chase", + "chat", + "cheap", + "check", + "cheese", + "chef", + "cherry", + "chest", + "chicken", + "chief", + "child", + "chimney", + "choice", + "choose", + "chronic", + "chuckle", + "chunk", + "churn", + "cigar", + "cinnamon", + "circle", + "citizen", + "city", + "civil", + "claim", + "clap", + "clarify", + "claw", + "clay", + "clean", + "clerk", + "clever", + "click", + "client", + "cliff", + "climb", + "clinic", + "clip", + "clock", + "clog", + "close", + "cloth", + "cloud", + "clown", + "club", + "clump", + "cluster", + "clutch", + "coach", + "coast", + "coconut", + "code", + "coffee", + "coil", + "coin", + "collect", + "color", + "column", + "combine", + "come", + "comfort", + "comic", + "common", + "company", + "concert", + "conduct", + "confirm", + "congress", + "connect", + "consider", + "control", + "convince", + "cook", + "cool", + "copper", + "copy", + "coral", + "core", + "corn", + "correct", + "cost", + "cotton", + "couch", + "country", + "couple", + "course", + "cousin", + "cover", + "coyote", + "crack", + "cradle", + "craft", + "cram", + "crane", + "crash", + "crater", + "crawl", + "crazy", + "cream", + "credit", + "creek", + "crew", + "cricket", + "crime", + "crisp", + "critic", + "crop", + "cross", + "crouch", + "crowd", + "crucial", + "cruel", + "cruise", + "crumble", + "crunch", + "crush", + "cry", + "crystal", + "cube", + "culture", + "cup", + "cupboard", + "curious", + "current", + "curtain", + "curve", + "cushion", + "custom", + "cute", + "cycle", + "dad", + "damage", + "damp", + "dance", + "danger", + "daring", + "dash", + "daughter", + "dawn", + "day", + "deal", + "debate", + "debris", + "decade", + "december", + "decide", + "decline", + "decorate", + "decrease", + "deer", + "defense", + "define", + "defy", + "degree", + "delay", + "deliver", + "demand", + "demise", + "denial", + "dentist", + "deny", + "depart", + "depend", + "deposit", + "depth", + "deputy", + "derive", + "describe", + "desert", + "design", + "desk", + "despair", + "destroy", + "detail", + "detect", + "develop", + "device", + "devote", + "diagram", + "dial", + "diamond", + "diary", + "dice", + "diesel", + "diet", + "differ", + "digital", + "dignity", + "dilemma", + "dinner", + "dinosaur", + "direct", + "dirt", + "disagree", + "discover", + "disease", + "dish", + "dismiss", + "disorder", + "display", + "distance", + "divert", + "divide", + "divorce", + "dizzy", + "doctor", + "document", + "dog", + "doll", + "dolphin", + "domain", + "donate", + "donkey", + "donor", + "door", + "dose", + "double", + "dove", + "draft", + "dragon", + "drama", + "drastic", + "draw", + "dream", + "dress", + "drift", + "drill", + "drink", + "drip", + "drive", + "drop", + "drum", + "dry", + "duck", + "dumb", + "dune", + "during", + "dust", + "dutch", + "duty", + "dwarf", + "dynamic", + "eager", + "eagle", + "early", + "earn", + "earth", + "easily", + "east", + "easy", + "echo", + "ecology", + "economy", + "edge", + "edit", + "educate", + "effort", + "egg", + "eight", + "either", + "elbow", + "elder", + "electric", + "elegant", + "element", + "elephant", + "elevator", + "elite", + "else", + "embark", + "embody", + "embrace", + "emerge", + "emotion", + "employ", + "empower", + "empty", + "enable", + "enact", + "end", + "endless", + "endorse", + "enemy", + "energy", + "enforce", + "engage", + "engine", + "enhance", + "enjoy", + "enlist", + "enough", + "enrich", + "enroll", + "ensure", + "enter", + "entire", + "entry", + "envelope", + "episode", + "equal", + "equip", + "era", + "erase", + "erode", + "erosion", + "error", + "erupt", + "escape", + "essay", + "essence", + "estate", + "eternal", + "ethics", + "evidence", + "evil", + "evoke", + "evolve", + "exact", + "example", + "excess", + "exchange", + "excite", + "exclude", + "excuse", + "execute", + "exercise", + "exhaust", + "exhibit", + "exile", + "exist", + "exit", + "exotic", + "expand", + "expect", + "expire", + "explain", + "expose", + "express", + "extend", + "extra", + "eye", + "eyebrow", + "fabric", + "face", + "faculty", + "fade", + "faint", + "faith", + "fall", + "false", + "fame", + "family", + "famous", + "fan", + "fancy", + "fantasy", + "farm", + "fashion", + "fat", + "fatal", + "father", + "fatigue", + "fault", + "favorite", + "feature", + "february", + "federal", + "fee", + "feed", + "feel", + "female", + "fence", + "festival", + "fetch", + "fever", + "few", + "fiber", + "fiction", + "field", + "figure", + "file", + "film", + "filter", + "final", + "find", + "fine", + "finger", + "finish", + "fire", + "firm", + "first", + "fiscal", + "fish", + "fit", + "fitness", + "fix", + "flag", + "flame", + "flash", + "flat", + "flavor", + "flee", + "flight", + "flip", + "float", + "flock", + "floor", + "flower", + "fluid", + "flush", + "fly", + "foam", + "focus", + "fog", + "foil", + "fold", + "follow", + "food", + "foot", + "force", + "forest", + "forget", + "fork", + "fortune", + "forum", + "forward", + "fossil", + "foster", + "found", + "fox", + "fragile", + "frame", + "frequent", + "fresh", + "friend", + "fringe", + "frog", + "front", + "frost", + "frown", + "frozen", + "fruit", + "fuel", + "fun", + "funny", + "furnace", + "fury", + "future", + "gadget", + "gain", + "galaxy", + "gallery", + "game", + "gap", + "garage", + "garbage", + "garden", + "garlic", + "garment", + "gas", + "gasp", + "gate", + "gather", + "gauge", + "gaze", + "general", + "genius", + "genre", + "gentle", + "genuine", + "gesture", + "ghost", + "giant", + "gift", + "giggle", + "ginger", + "giraffe", + "girl", + "give", + "glad", + "glance", + "glare", + "glass", + "glide", + "glimpse", + "globe", + "gloom", + "glory", + "glove", + "glow", + "glue", + "goat", + "goddess", + "gold", + "good", + "goose", + "gorilla", + "gospel", + "gossip", + "govern", + "gown", + "grab", + "grace", + "grain", + "grant", + "grape", + "grass", + "gravity", + "great", + "green", + "grid", + "grief", + "grit", + "grocery", + "group", + "grow", + "grunt", + "guard", + "guess", + "guide", + "guilt", + "guitar", + "gun", + "gym", + "habit", + "hair", + "half", + "hammer", + "hamster", + "hand", + "happy", + "harbor", + "hard", + "harsh", + "harvest", + "hat", + "have", + "hawk", + "hazard", + "head", + "health", + "heart", + "heavy", + "hedgehog", + "height", + "hello", + "helmet", + "help", + "hen", + "hero", + "hidden", + "high", + "hill", + "hint", + "hip", + "hire", + "history", + "hobby", + "hockey", + "hold", + "hole", + "holiday", + "hollow", + "home", + "honey", + "hood", + "hope", + "horn", + "horror", + "horse", + "hospital", + "host", + "hotel", + "hour", + "hover", + "hub", + "huge", + "human", + "humble", + "humor", + "hundred", + "hungry", + "hunt", + "hurdle", + "hurry", + "hurt", + "husband", + "hybrid", + "ice", + "icon", + "idea", + "identify", + "idle", + "ignore", + "ill", + "illegal", + "illness", + "image", + "imitate", + "immense", + "immune", + "impact", + "impose", + "improve", + "impulse", + "inch", + "include", + "income", + "increase", + "index", + "indicate", + "indoor", + "industry", + "infant", + "inflict", + "inform", + "inhale", + "inherit", + "initial", + "inject", + "injury", + "inmate", + "inner", + "innocent", + "input", + "inquiry", + "insane", + "insect", + "inside", + "inspire", + "install", + "intact", + "interest", + "into", + "invest", + "invite", + "involve", + "iron", + "island", + "isolate", + "issue", + "item", + "ivory", + "jacket", + "jaguar", + "jar", + "jazz", + "jealous", + "jeans", + "jelly", + "jewel", + "job", + "join", + "joke", + "journey", + "joy", + "judge", + "juice", + "jump", + "jungle", + "junior", + "junk", + "just", + "kangaroo", + "keen", + "keep", + "ketchup", + "key", + "kick", + "kid", + "kidney", + "kind", + "kingdom", + "kiss", + "kit", + "kitchen", + "kite", + "kitten", + "kiwi", + "knee", + "knife", + "knock", + "know", + "lab", + "label", + "labor", + "ladder", + "lady", + "lake", + "lamp", + "language", + "laptop", + "large", + "later", + "latin", + "laugh", + "laundry", + "lava", + "law", + "lawn", + "lawsuit", + "layer", + "lazy", + "leader", + "leaf", + "learn", + "leave", + "lecture", + "left", + "leg", + "legal", + "legend", + "leisure", + "lemon", + "lend", + "length", + "lens", + "leopard", + "lesson", + "letter", + "level", + "liar", + "liberty", + "library", + "license", + "life", + "lift", + "light", + "like", + "limb", + "limit", + "link", + "lion", + "liquid", + "list", + "little", + "live", + "lizard", + "load", + "loan", + "lobster", + "local", + "lock", + "logic", + "lonely", + "long", + "loop", + "lottery", + "loud", + "lounge", + "love", + "loyal", + "lucky", + "luggage", + "lumber", + "lunar", + "lunch", + "luxury", + "lyrics", + "machine", + "mad", + "magic", + "magnet", + "maid", + "mail", + "main", + "major", + "make", + "mammal", + "man", + "manage", + "mandate", + "mango", + "mansion", + "manual", + "maple", + "marble", + "march", + "margin", + "marine", + "market", + "marriage", + "mask", + "mass", + "master", + "match", + "material", + "math", + "matrix", + "matter", + "maximum", + "maze", + "meadow", + "mean", + "measure", + "meat", + "mechanic", + "medal", + "media", + "melody", + "melt", + "member", + "memory", + "mention", + "menu", + "mercy", + "merge", + "merit", + "merry", + "mesh", + "message", + "metal", + "method", + "middle", + "midnight", + "milk", + "million", + "mimic", + "mind", + "minimum", + "minor", + "minute", + "miracle", + "mirror", + "misery", + "miss", + "mistake", + "mix", + "mixed", + "mixture", + "mobile", + "model", + "modify", + "mom", + "moment", + "monitor", + "monkey", + "monster", + "month", + "moon", + "moral", + "more", + "morning", + "mosquito", + "mother", + "motion", + "motor", + "mountain", + "mouse", + "move", + "movie", + "much", + "muffin", + "mule", + "multiply", + "muscle", + "museum", + "mushroom", + "music", + "must", + "mutual", + "myself", + "mystery", + "myth", + "naive", + "name", + "napkin", + "narrow", + "nasty", + "nation", + "nature", + "near", + "neck", + "need", + "negative", + "neglect", + "neither", + "nephew", + "nerve", + "nest", + "net", + "network", + "neutral", + "never", + "news", + "next", + "nice", + "night", + "noble", + "noise", + "nominee", + "noodle", + "normal", + "north", + "nose", + "notable", + "note", + "nothing", + "notice", + "novel", + "now", + "nuclear", + "number", + "nurse", + "nut", + "oak", + "obey", + "object", + "oblige", + "obscure", + "observe", + "obtain", + "obvious", + "occur", + "ocean", + "october", + "odor", + "off", + "offer", + "office", + "often", + "oil", + "okay", + "old", + "olive", + "olympic", + "omit", + "once", + "one", + "onion", + "online", + "only", + "open", + "opera", + "opinion", + "oppose", + "option", + "orange", + "orbit", + "orchard", + "order", + "ordinary", + "organ", + "orient", + "original", + "orphan", + "ostrich", + "other", + "outdoor", + "outer", + "output", + "outside", + "oval", + "oven", + "over", + "own", + "owner", + "oxygen", + "oyster", + "ozone", + "pact", + "paddle", + "page", + "pair", + "palace", + "palm", + "panda", + "panel", + "panic", + "panther", + "paper", + "parade", + "parent", + "park", + "parrot", + "party", + "pass", + "patch", + "path", + "patient", + "patrol", + "pattern", + "pause", + "pave", + "payment", + "peace", + "peanut", + "pear", + "peasant", + "pelican", + "pen", + "penalty", + "pencil", + "people", + "pepper", + "perfect", + "permit", + "person", + "pet", + "phone", + "photo", + "phrase", + "physical", + "piano", + "picnic", + "picture", + "piece", + "pig", + "pigeon", + "pill", + "pilot", + "pink", + "pioneer", + "pipe", + "pistol", + "pitch", + "pizza", + "place", + "planet", + "plastic", + "plate", + "play", + "please", + "pledge", + "pluck", + "plug", + "plunge", + "poem", + "poet", + "point", + "polar", + "pole", + "police", + "pond", + "pony", + "pool", + "popular", + "portion", + "position", + "possible", + "post", + "potato", + "pottery", + "poverty", + "powder", + "power", + "practice", + "praise", + "predict", + "prefer", + "prepare", + "present", + "pretty", + "prevent", + "price", + "pride", + "primary", + "print", + "priority", + "prison", + "private", + "prize", + "problem", + "process", + "produce", + "profit", + "program", + "project", + "promote", + "proof", + "property", + "prosper", + "protect", + "proud", + "provide", + "public", + "pudding", + "pull", + "pulp", + "pulse", + "pumpkin", + "punch", + "pupil", + "puppy", + "purchase", + "purity", + "purpose", + "purse", + "push", + "put", + "puzzle", + "pyramid", + "quality", + "quantum", + "quarter", + "question", + "quick", + "quit", + "quiz", + "quote", + "rabbit", + "raccoon", + "race", + "rack", + "radar", + "radio", + "rail", + "rain", + "raise", + "rally", + "ramp", + "ranch", + "random", + "range", + "rapid", + "rare", + "rate", + "rather", + "raven", + "raw", + "razor", + "ready", + "real", + "reason", + "rebel", + "rebuild", + "recall", + "receive", + "recipe", + "record", + "recycle", + "reduce", + "reflect", + "reform", + "refuse", + "region", + "regret", + "regular", + "reject", + "relax", + "release", + "relief", + "rely", + "remain", + "remember", + "remind", + "remove", + "render", + "renew", + "rent", + "reopen", + "repair", + "repeat", + "replace", + "report", + "require", + "rescue", + "resemble", + "resist", + "resource", + "response", + "result", + "retire", + "retreat", + "return", + "reunion", + "reveal", + "review", + "reward", + "rhythm", + "rib", + "ribbon", + "rice", + "rich", + "ride", + "ridge", + "rifle", + "right", + "rigid", + "ring", + "riot", + "ripple", + "risk", + "ritual", + "rival", + "river", + "road", + "roast", + "robot", + "robust", + "rocket", + "romance", + "roof", + "rookie", + "room", + "rose", + "rotate", + "rough", + "round", + "route", + "royal", + "rubber", + "rude", + "rug", + "rule", + "run", + "runway", + "rural", + "sad", + "saddle", + "sadness", + "safe", + "sail", + "salad", + "salmon", + "salon", + "salt", + "salute", + "same", + "sample", + "sand", + "satisfy", + "satoshi", + "sauce", + "sausage", + "save", + "say", + "scale", + "scan", + "scare", + "scatter", + "scene", + "scheme", + "school", + "science", + "scissors", + "scorpion", + "scout", + "scrap", + "screen", + "script", + "scrub", + "sea", + "search", + "season", + "seat", + "second", + "secret", + "section", + "security", + "seed", + "seek", + "segment", + "select", + "sell", + "seminar", + "senior", + "sense", + "sentence", + "series", + "service", + "session", + "settle", + "setup", + "seven", + "shadow", + "shaft", + "shallow", + "share", + "shed", + "shell", + "sheriff", + "shield", + "shift", + "shine", + "ship", + "shiver", + "shock", + "shoe", + "shoot", + "shop", + "short", + "shoulder", + "shove", + "shrimp", + "shrug", + "shuffle", + "shy", + "sibling", + "sick", + "side", + "siege", + "sight", + "sign", + "silent", + "silk", + "silly", + "silver", + "similar", + "simple", + "since", + "sing", + "siren", + "sister", + "situate", + "six", + "size", + "skate", + "sketch", + "ski", + "skill", + "skin", + "skirt", + "skull", + "slab", + "slam", + "sleep", + "slender", + "slice", + "slide", + "slight", + "slim", + "slogan", + "slot", + "slow", + "slush", + "small", + "smart", + "smile", + "smoke", + "smooth", + "snack", + "snake", + "snap", + "sniff", + "snow", + "soap", + "soccer", + "social", + "sock", + "soda", + "soft", + "solar", + "soldier", + "solid", + "solution", + "solve", + "someone", + "song", + "soon", + "sorry", + "sort", + "soul", + "sound", + "soup", + "source", + "south", + "space", + "spare", + "spatial", + "spawn", + "speak", + "special", + "speed", + "spell", + "spend", + "sphere", + "spice", + "spider", + "spike", + "spin", + "spirit", + "split", + "spoil", + "sponsor", + "spoon", + "sport", + "spot", + "spray", + "spread", + "spring", + "spy", + "square", + "squeeze", + "squirrel", + "stable", + "stadium", + "staff", + "stage", + "stairs", + "stamp", + "stand", + "start", + "state", + "stay", + "steak", + "steel", + "stem", + "step", + "stereo", + "stick", + "still", + "sting", + "stock", + "stomach", + "stone", + "stool", + "story", + "stove", + "strategy", + "street", + "strike", + "strong", + "struggle", + "student", + "stuff", + "stumble", + "style", + "subject", + "submit", + "subway", + "success", + "such", + "sudden", + "suffer", + "sugar", + "suggest", + "suit", + "summer", + "sun", + "sunny", + "sunset", + "super", + "supply", + "supreme", + "sure", + "surface", + "surge", + "surprise", + "surround", + "survey", + "suspect", + "sustain", + "swallow", + "swamp", + "swap", + "swarm", + "swear", + "sweet", + "swift", + "swim", + "swing", + "switch", + "sword", + "symbol", + "symptom", + "syrup", + "system", + "table", + "tackle", + "tag", + "tail", + "talent", + "talk", + "tank", + "tape", + "target", + "task", + "taste", + "tattoo", + "taxi", + "teach", + "team", + "tell", + "ten", + "tenant", + "tennis", + "tent", + "term", + "test", + "text", + "thank", + "that", + "theme", + "then", + "theory", + "there", + "they", + "thing", + "this", + "thought", + "three", + "thrive", + "throw", + "thumb", + "thunder", + "ticket", + "tide", + "tiger", + "tilt", + "timber", + "time", + "tiny", + "tip", + "tired", + "tissue", + "title", + "toast", + "tobacco", + "today", + "toddler", + "toe", + "together", + "toilet", + "token", + "tomato", + "tomorrow", + "tone", + "tongue", + "tonight", + "tool", + "tooth", + "top", + "topic", + "topple", + "torch", + "tornado", + "tortoise", + "toss", + "total", + "tourist", + "toward", + "tower", + "town", + "toy", + "track", + "trade", + "traffic", + "tragic", + "train", + "transfer", + "trap", + "trash", + "travel", + "tray", + "treat", + "tree", + "trend", + "trial", + "tribe", + "trick", + "trigger", + "trim", + "trip", + "trophy", + "trouble", + "truck", + "true", + "truly", + "trumpet", + "trust", + "truth", + "try", + "tube", + "tuition", + "tumble", + "tuna", + "tunnel", + "turkey", + "turn", + "turtle", + "twelve", + "twenty", + "twice", + "twin", + "twist", + "two", + "type", + "typical", + "ugly", + "umbrella", + "unable", + "unaware", + "uncle", + "uncover", + "under", + "undo", + "unfair", + "unfold", + "unhappy", + "uniform", + "unique", + "unit", + "universe", + "unknown", + "unlock", + "until", + "unusual", + "unveil", + "update", + "upgrade", + "uphold", + "upon", + "upper", + "upset", + "urban", + "urge", + "usage", + "use", + "used", + "useful", + "useless", + "usual", + "utility", + "vacant", + "vacuum", + "vague", + "valid", + "valley", + "valve", + "van", + "vanish", + "vapor", + "various", + "vast", + "vault", + "vehicle", + "velvet", + "vendor", + "venture", + "venue", + "verb", + "verify", + "version", + "very", + "vessel", + "veteran", + "viable", + "vibrant", + "vicious", + "victory", + "video", + "view", + "village", + "vintage", + "violin", + "virtual", + "virus", + "visa", + "visit", + "visual", + "vital", + "vivid", + "vocal", + "voice", + "void", + "volcano", + "volume", + "vote", + "voyage", + "wage", + "wagon", + "wait", + "walk", + "wall", + "walnut", + "want", + "warfare", + "warm", + "warrior", + "wash", + "wasp", + "waste", + "water", + "wave", + "way", + "wealth", + "weapon", + "wear", + "weasel", + "weather", + "web", + "wedding", + "weekend", + "weird", + "welcome", + "west", + "wet", + "whale", + "what", + "wheat", + "wheel", + "when", + "where", + "whip", + "whisper", + "wide", + "width", + "wife", + "wild", + "will", + "win", + "window", + "wine", + "wing", + "wink", + "winner", + "winter", + "wire", + "wisdom", + "wise", + "wish", + "witness", + "wolf", + "woman", + "wonder", + "wood", + "wool", + "word", + "work", + "world", + "worry", + "worth", + "wrap", + "wreck", + "wrestle", + "wrist", + "write", + "wrong", + "yard", + "year", + "yellow", + "you", + "young", + "youth", + "zebra", + "zero", + "zone", + "zoo" + )); +} diff --git a/api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java b/api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java new file mode 100644 index 00000000..f7834063 --- /dev/null +++ b/api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java @@ -0,0 +1,76 @@ +/* + * Copyright 2013 bits of proof zrt. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bitsofproof.supernode.api; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.io.InputStream; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Security; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.wallet.BIP39; + +public class BIP39Test +{ + @BeforeClass + public static void init () + { + Security.addProvider (new BouncyCastleProvider ()); + } + + private static final String TESTS = "BIP39.json"; + + private JSONObject readObject (String resource) throws IOException, JSONException + { + InputStream input = this.getClass ().getResource ("/" + resource).openStream (); + StringBuffer content = new StringBuffer (); + byte[] buffer = new byte[1024]; + int len; + while ( (len = input.read (buffer)) > 0 ) + { + byte[] s = new byte[len]; + System.arraycopy (buffer, 0, s, 0, len); + content.append (new String (buffer, "UTF-8")); + } + return new JSONObject (content.toString ()); + } + + @Test + public void bip39Test () throws IOException, JSONException, ValidationException, InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException + { + JSONObject testData = readObject (TESTS); + JSONArray english = testData.getJSONArray ("english"); + for ( int i = 0; i < testData.length (); ++i ) + { + JSONArray test = english.getJSONArray (i); + String m = BIP39.getMnemonic (ByteUtils.fromHex (test.getString (i))); + assertTrue (m.equals (test.getString (i + 1))); + assertTrue (ByteUtils.toHex (BIP39.getSeed (m, "TREZOR")).equals (test.getString (i + 2))); + } + } +} diff --git a/api/src/test/resources/BIP39.json b/api/src/test/resources/BIP39.json new file mode 100644 index 00000000..0cba395b --- /dev/null +++ b/api/src/test/resources/BIP39.json @@ -0,0 +1,124 @@ +{ + "english": [ + [ + "00000000000000000000000000000000", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", + "cb5e7230ce8229de990674f6aa4288325fd4d8181f761734bd8b5cc944fedc2a4300e64422864b565352de7ffbc5ad0fafdf5344489f3a83e4a4bb5271cafaae" + ], + [ + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "legal winner thank year wave sausage worth useful legal winner thank yellow", + "de1277934939d6969519f44b7b3757a905d7f635be41e1e88022c346bc52ad26c0a3e9578e73e9b89066873266f285a5891d27d28cb27fccfe26d92bbd7ee364" + ], + [ + "80808080808080808080808080808080", + "letter advice cage absurd amount doctor acoustic avoid letter advice cage above", + "8863bccef9cfffeacef1e4c6fc97bba8227ab0fc7e8e162be7467282689a13521ea364d7c4bc8cd241b59f53c5147a89c18a47248a96592ab9a2c1f1870b026c" + ], + [ + "ffffffffffffffffffffffffffffffff", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong", + "7a29e57c7a1532af1bddb7e02b892cfccc6a57b74fe9784324ea89fab8a66dc64fde79c31166b159685116f4e93c1795496f20ffdc2d3a69d3439931dabde86e" + ], + [ + "000000000000000000000000000000000000000000000000", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent", + "c3e382025b6a22a901505cf393faea450eb6c4a5f2a8c8f0596285b2bd84688877a6cc7231420e2bbdd2428e62ed549a78fa215b3adafd8dea075dabfc704d5e" + ], + [ + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will", + "c82666e40eb097bf6eb05fecd7dc2ddfb6bbdc6071900f4b3fd3c3e635db69aa2094f1f450c98e8dc6103aa72df635abdfcc3b6d6ec5261a9208a07a35a3f1c8" + ], + [ + "808080808080808080808080808080808080808080808080", + "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always", + "e90681c67c55504afadca009ce4042819341fa0e90300b6d32b4f2e8e8a6678ff7e7fc1da663ae194dc7a2ef7ec7b50112d1a5efce47bfd00c66eec82f2265b5" + ], + [ + "ffffffffffffffffffffffffffffffffffffffffffffffff", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when", + "2a372547df962742942674170a7cef495ea0b97f4864de16d0f3ee82eb577ca1eca345e601cc2df7c626c5bc51c52c28a3b4294224b685c958c7450bee6769e6" + ], + [ + "0000000000000000000000000000000000000000000000000000000000000000", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art", + "58cb9c5555d67ecc7b32305a78d1a2fcf0c9b22f1af761cfafc65eb1d3909f63ee2cab84996a7478cfd3e864cda5efb0caf580d56cf49739c6b3638d94e758c1" + ], + [ + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title", + "0093cb3ed6d1302d3cf498017f8cb1c7dc2fdbd62ec57fc49e4b2a4dd47a23e44e0b309517d5a3e7b0f4f0ef0ed132818cf120a098a92e572ad086f1a90ccb7f" + ], + [ + "8080808080808080808080808080808080808080808080808080808080808080", + "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless", + "8a21e46b9d264328c63e707e3d38ed4eb21508deda309fa2ef57cc8eca8b351ca3018758844ba9fb5851bab15d026a61cabace53a9a39bc91dc2c51407542cf5" + ], + [ + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote", + "5c8a1a284ab2844daf02cab322df3996574c9d53cbd36159c493441990f0d2a6bc9bc1502e3a067943d8ec67324663cbfb9667b57fed220e3f28335e26a90f93" + ], + [ + "1083fa24dbb0afa4e7b327d23f666567", + "awesome cabin matrix resist april sponsor paddle gossip split will off soon", + "467406b36a0176e40e013393e5ecef1f5b4019980b502eda9db1db06f7786e088b206f045f2bfcf93bd3b17a598335b078fcc5890115857ff741bd154b54f049" + ], + [ + "d8cbcd1ac2153ecd74048480c2732f637d642b21f0dd40df", + "sugar fury effort loud fault grit source mountain liar bean slim shoulder stone better march brick dolphin zero", + "f60180ea5047659cbb17ed6ef79c974de86c0170c7a1962b205329eb8fe9dcdd148615d35c515c4ec8da25f4cf54d5b7cd8cd5bf8dc4059df3a7900ca25f8306" + ], + [ + "2952f95cefe041616f6f379ab649cf8b702ecf8e4acceaebdda4cc50e2bf1d7b", + "citizen oak fire thank advice radar sad tragic one rather initial black actual guitar decrease flower turtle galaxy hard obvious athlete garbage invest have", + "eff4b6a15bb55fcf4bbfa2b3b9e24e7dc4bed8319ef7703f1786d472c73666922925778eaa5a06f8a26d2c7e7240be746fd69edfaf197e0dae12d7e0b550cfc8" + ], + [ + "f5e82717078a6ddc538a03e825f91bed", + "vote donkey shift audit plug until evolve document trial cool eight swarm", + "83dad22293225780a914083fc1a69bfe1d910f5b5962b0364820132a42ae1bd567a1fb4d5a19ad3d64539e38a7ee3d6429fac2b74e72b020913131c5eadb7db4" + ], + [ + "16b59b6a426f2f302f73049a32ab8572394278982212357a", + "birth proud surround luggage very object saddle gauge olive next throw tongue neither detail gauge drastic cube strategy", + "38ceb07e0dad221f612631843be6ae44a650aaf789c8ebea9313e07498d7864385227d25c7a8268a5b850367eef31639632e9218acadead20980b864b1cd477e" + ], + [ + "95b6cb48c7bc9c2a54496ae3eea790824b57e52b9637058f084555bc1b809b2f", + "noble rent split month six benefit eye coil token inside tomorrow afraid rely verb purity shoulder airport joke bacon problem script scare hole trumpet", + "e33e3d32e467877596a18ac60050488a0ec1557fda6bf95bad3d33d964c5e99dcd97d378403cc2723ed1c85c12b42bc59f15458d970d7a9d015f556109c146b0" + ], + [ + "7f93397f750f70a26513de2732ed95ee", + "legend oil garlic tube warfare eye nephew knock cheese number grace tackle", + "7f92ad63e4cdf4f15c23740556ad81e7f8cbd67cc672c93894c9c0d4fb171539eed5ab29f366570ed9940b816f45a539c3816f7ac19511794b752c5c1ec0e732" + ], + [ + "14c29fe840dd1c9f05d392ba13e4e1466b32ed0726a15f89", + "below belt wheel like spike exhibit blanket inch ring palace debate mimic rebel isolate broken stage garbage enhance", + "7bae6e54f8bad645f18f574b310bd3e6fde126dabcaf63a889940380e4798810e48c8151fc56bb2389c07498deacef025f03cbf8fc57ea3ec68f6421b0fcb649" + ], + [ + "cb30610d175ffeab8357d5190d31923997752a7f9815087bfcad5eb0b43f6468", + "sleep loan drive concert zoo fiction ask wide boil hat goose industry jar news wrist actor anchor that clip runway area cabbage museum abuse", + "b922030609e7626696b9cf5ca4c06cd99290be30b1052770f6a60c5f26532d178f287a4285d7a2add2845dc89a816b26fdba1c830067d130740f64c0ab5cfbe1" + ], + [ + "a30b50a5439dcd1774f412ea5ec33403", + "perfect fold citizen mango system merry stable liquid tumble voyage snack alter", + "aae175f26848370c4d5d3d0640597e2bf1b28e95908dd877259b3eac5d71ffe3140739a3ed80180f88159571df84441985620e6b2fb0696e5cba1aa7b8d10b98" + ], + [ + "70044da2175ad681d0ebbf2da83cf407eb9c8fd91fc0a8c9", + "hybrid carbon hammer concert pulp domain dry jewel color draft dial average right elevator good way potato energy", + "a3dffe3a31a2e949d1b04af7495a5b59db17e41d93b985feeaaae89260a9c86c6dcdf7cb32eaba61c2f4f0340f0f17d1ebb67af11657286b2ffd66ec4e05a8b7" + ], + [ + "0e0bab4df9669b97ba3f75a50b2e92423bbe6e91a1b01dbbf3ba200a917c9106", + "asthma front square version have slim trophy upgrade pink floor pig love room dance educate current buffalo test update divorce poverty salad dune scheme", + "2eb4d85fbd8deaf9b06bf9cdb3e5f36e8da040d110312075eb32e776fc8e505b94be3e63c1525ad41f5e5968a263853001dc7c40ea3af8e8b0cfb7effd5f408c" + ] + ] +} \ No newline at end of file From 9eb402763419b56f5e6b7e98f6c706877602dc72 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sun, 8 Dec 2013 14:59:15 +0100 Subject: [PATCH 258/299] add encode/decode to BIP39 --- .../bitsofproof/supernode/wallet/BIP39.java | 93 ++++++++++++++++--- .../bitsofproof/supernode/api/BIP39Test.java | 27 +++++- 2 files changed, 108 insertions(+), 12 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java b/api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java index f11f5644..d47be3c1 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java @@ -20,10 +20,13 @@ import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import java.util.StringTokenizer; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; @@ -32,6 +35,79 @@ public class BIP39 { + public static byte[] decode (String mnemonic, String passphrase) throws ValidationException + { + StringTokenizer tokenizer = new StringTokenizer (mnemonic); + int nt = tokenizer.countTokens (); + if ( nt % 6 != 0 ) + { + throw new ValidationException ("invalid mnemonic - word cound not divisible by 6"); + } + boolean[] bits = new boolean[11 * nt]; + int i = 0; + while ( tokenizer.hasMoreElements () ) + { + int c = Arrays.binarySearch (english, tokenizer.nextToken ()); + for ( int j = 0; j < 11; ++j ) + { + bits[i++] = (c & (1 << (10 - j))) > 0; + } + } + byte[] data = new byte[bits.length / 33 * 4]; + for ( i = 0; i < bits.length / 33 * 32; ++i ) + { + data[i / 8] |= (bits[i] ? 1 : 0) << (7 - (i % 8)); + } + byte check = Hash.sha256 (data)[0]; + for ( i = bits.length / 33 * 32; i < bits.length; ++i ) + { + if ( (check & (1 << (7 - (i % 8))) ^ (bits[i] ? 1 : 0) << (7 - (i % 8))) != 0 ) + { + throw new ValidationException ("invalid mnemonic - checksum failed"); + } + } + try + { + SecretKey seedkey = new SecretKeySpec (("mnemonic" + passphrase).getBytes ("UTF-8"), "Blowfish"); + Cipher cipher = Cipher.getInstance ("BlowFish/ECB/NoPadding", "BC"); + cipher.init (Cipher.DECRYPT_MODE, seedkey); + for ( i = 0; i < 1000; ++i ) + { + data = cipher.doFinal (data); + } + } + catch ( UnsupportedEncodingException | NoSuchAlgorithmException | + NoSuchProviderException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e ) + { + throw new ValidationException ("can not decrypt mnemonic", e); + } + return data; + } + + public static String encode (byte[] data, String passphrase) throws ValidationException + { + if ( data.length % 8 != 0 ) + { + throw new ValidationException ("can nor encode - data length not divisible with 8"); + } + try + { + SecretKey seedkey = new SecretKeySpec (("mnemonic" + passphrase).getBytes ("UTF-8"), "Blowfish"); + Cipher cipher = Cipher.getInstance ("BlowFish/ECB/NoPadding", "BC"); + cipher.init (Cipher.ENCRYPT_MODE, seedkey); + for ( int i = 0; i < 1000; ++i ) + { + data = cipher.doFinal (data); + } + } + catch ( UnsupportedEncodingException | NoSuchAlgorithmException | + NoSuchProviderException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e ) + { + throw new ValidationException ("can not decrypt mnemonic", e); + } + return getMnemonic (data); + } + public static byte[] getSeed (String mnenonic, String passphrase) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, UnsupportedEncodingException { @@ -47,11 +123,6 @@ public static byte[] getSeed (String mnenonic, String passphrase) throws NoSuchA } public static String getMnemonic (byte[] data) throws ValidationException - { - return getMnemonic (data, english); - } - - public static String getMnemonic (byte[] data, List dictionary) throws ValidationException { if ( data.length % 4 != 0 || data.length < 16 || data.length > 32 ) { @@ -59,7 +130,7 @@ public static String getMnemonic (byte[] data, List dictionary) throws V } byte check = Hash.sha256 (data)[0]; - boolean[] bits = new boolean[(data.length + 1) * 8]; + boolean[] bits = new boolean[data.length * 8 + data.length / 4]; for ( int i = 0; i < data.length; i++ ) { @@ -82,7 +153,7 @@ public static String getMnemonic (byte[] data, List dictionary) throws V { idx += (bits[i * 11 + j] ? 1 : 0) << (10 - j); } - mnemo.append (dictionary.get (idx)); + mnemo.append (english[idx]); if ( i < mlen - 1 ) { mnemo.append (" "); @@ -91,7 +162,7 @@ public static String getMnemonic (byte[] data, List dictionary) throws V return mnemo.toString (); } - public final static List english = Collections.unmodifiableList (Arrays.asList ( + private final static String[] english = { "abandon", "ability", "able", @@ -2140,5 +2211,5 @@ public static String getMnemonic (byte[] data, List dictionary) throws V "zero", "zone", "zoo" - )); + }; } diff --git a/api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java b/api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java index f7834063..37798404 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java @@ -22,7 +22,9 @@ import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; +import java.security.SecureRandom; import java.security.Security; +import java.util.Arrays; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.json.JSONArray; @@ -61,7 +63,8 @@ private JSONObject readObject (String resource) throws IOException, JSONExceptio } @Test - public void bip39Test () throws IOException, JSONException, ValidationException, InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException + public void bip39TrezorTest () throws IOException, JSONException, ValidationException, InvalidKeyException, NoSuchAlgorithmException, + NoSuchProviderException { JSONObject testData = readObject (TESTS); JSONArray english = testData.getJSONArray ("english"); @@ -73,4 +76,26 @@ public void bip39Test () throws IOException, JSONException, ValidationException, assertTrue (ByteUtils.toHex (BIP39.getSeed (m, "TREZOR")).equals (test.getString (i + 2))); } } + + @Test + public void bip39EncodeDecodeTest () throws ValidationException, IOException, JSONException, InvalidKeyException, NoSuchAlgorithmException, + NoSuchProviderException + { + JSONObject testData = readObject (TESTS); + JSONArray english = testData.getJSONArray ("english"); + for ( int i = 0; i < testData.length (); ++i ) + { + JSONArray test = english.getJSONArray (i); + byte[] m = BIP39.decode (test.getString (1), "BOP"); + assertTrue (test.getString (1).equals (BIP39.encode (m, "BOP"))); + } + SecureRandom random = new SecureRandom (); + for ( int i = 0; i < 100; ++i ) + { + byte[] secret = new byte[16]; + random.nextBytes (secret); + String e = BIP39.encode (secret, "BOP"); + assertTrue (Arrays.equals (BIP39.decode (e, "BOP"), secret)); + } + } } From 1346a3d965bf9863ecf1483ff4d230b91c165fc0 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sun, 8 Dec 2013 14:59:48 +0100 Subject: [PATCH 259/299] Add Shamir's secret sharing --- .../wallet/ShamirsSecretSharing.java | 106 ++++++++++++++++++ .../api/SahmirsSecretSharingTest.java | 46 ++++++++ 2 files changed, 152 insertions(+) create mode 100644 api/src/main/java/com/bitsofproof/supernode/wallet/ShamirsSecretSharing.java create mode 100644 api/src/test/java/com/bitsofproof/supernode/api/SahmirsSecretSharingTest.java diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/ShamirsSecretSharing.java b/api/src/main/java/com/bitsofproof/supernode/wallet/ShamirsSecretSharing.java new file mode 100644 index 00000000..ea28e149 --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/ShamirsSecretSharing.java @@ -0,0 +1,106 @@ +/* + * Copyright 2013 bits of proof zrt. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bitsofproof.supernode.wallet; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import com.bitsofproof.supernode.common.ValidationException; + +public class ShamirsSecretSharing +{ + public static class SecretShare + { + public BigInteger m; + public BigInteger x; + public BigInteger y; + public int n; + } + + private final static SecureRandom random = new SecureRandom (); + + public static SecretShare[] cut (BigInteger secret, int pieces, int needed) + { + BigInteger mod = BigInteger.probablePrime (secret.bitLength () + 1, random); + BigInteger[] coeff = new BigInteger[needed - 1]; + for ( int i = 0; i < coeff.length; ++i ) + { + byte[] r = new byte[secret.bitLength () / 8]; + random.nextBytes (r); + coeff[i] = new BigInteger (1, r); + } + + SecretShare[] shares = new SecretShare[pieces]; + for ( int x = 1; x <= pieces; ++x ) + { + int pow = x; + BigInteger poly = secret; + for ( int i = 0; i < needed - 1; ++i ) + { + poly = poly.add (BigInteger.valueOf (pow).multiply (coeff[i]).mod (mod)).mod (mod); + pow *= x; + } + shares[x - 1] = new SecretShare (); + shares[x - 1].m = mod; + shares[x - 1].x = BigInteger.valueOf (x); + shares[x - 1].y = poly; + shares[x - 1].n = needed; + } + + return shares; + } + + public static BigInteger reconstruct (SecretShare[] shares) throws ValidationException + { + for ( int i = 0; i < shares.length - 1; ++i ) + { + if ( !shares[i].m.equals (shares[i + 1].m) || + shares[i].n != shares[i + 1].n ) + { + throw new ValidationException ("Not shares of the same secret"); + } + for ( int j = 0; j < shares.length; ++j ) + { + if ( i != j && shares[i].x.equals (shares[j].x) ) + { + throw new ValidationException ("Shares are not unique"); + } + } + } + if ( shares.length < shares[0].n ) + { + throw new ValidationException ("Not enough shares"); + } + BigInteger m = shares[0].m; + BigInteger[] y = new BigInteger[shares.length]; + for ( int i = 0; i < shares.length; ++i ) + { + y[i] = shares[i].y; + } + int d, i; + for ( d = 1; d < shares.length; d++ ) + { + for ( i = 0; i < shares.length - d; i++ ) + { + int j = i + d; + BigInteger num = shares[j].x.multiply (y[i]).mod (m).subtract (shares[i].x.multiply (y[i + 1]).mod (m)).mod (m); + BigInteger den = shares[j].x.subtract (shares[i].x).mod (m); + y[i] = num.multiply (den.modInverse (m)).mod (m); + } + } + return y[0]; + } +} diff --git a/api/src/test/java/com/bitsofproof/supernode/api/SahmirsSecretSharingTest.java b/api/src/test/java/com/bitsofproof/supernode/api/SahmirsSecretSharingTest.java new file mode 100644 index 00000000..6e86d4c2 --- /dev/null +++ b/api/src/test/java/com/bitsofproof/supernode/api/SahmirsSecretSharingTest.java @@ -0,0 +1,46 @@ +package com.bitsofproof.supernode.api; + +import static org.junit.Assert.assertTrue; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.HashSet; +import java.util.Set; + +import org.junit.Test; + +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.wallet.ShamirsSecretSharing; + +public class SahmirsSecretSharingTest +{ + @Test + public void test () throws ValidationException + { + SecureRandom random = new SecureRandom (); + for ( int s = 3; s < 10; ++s ) + { + for ( int g = 1; g < s - 1; ++g ) + { + for ( int n = 2; n < s - g; ++n ) + { + BigInteger secret = new BigInteger (256, random); + ShamirsSecretSharing.SecretShare[] all = ShamirsSecretSharing.cut (secret, s, n); + + ShamirsSecretSharing.SecretShare[] some = new ShamirsSecretSharing.SecretShare[n]; + Set pick = new HashSet (); + while ( pick.size () < n ) + { + int p = random.nextInt (n); + if ( !pick.contains (p) ) + { + some[pick.size ()] = all[p]; + pick.add (p); + } + } + assertTrue (ShamirsSecretSharing.reconstruct (some).equals (secret)); + } + } + } + } +} From a5034ae48f8dcc12ab9ef7298e774b89bbaa1757 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sun, 8 Dec 2013 15:51:47 +0100 Subject: [PATCH 260/299] throw only one exception --- .../bitsofproof/supernode/wallet/BIP39.java | 25 ++++++++++++------- .../bitsofproof/supernode/api/BIP39Test.java | 9 ++----- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java b/api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java index d47be3c1..8ca41b27 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java @@ -108,18 +108,25 @@ public static String encode (byte[] data, String passphrase) throws ValidationEx return getMnemonic (data); } - public static byte[] getSeed (String mnenonic, String passphrase) throws NoSuchAlgorithmException, NoSuchProviderException, - InvalidKeyException, UnsupportedEncodingException + public static byte[] getSeed (String mnenonic, String passphrase) throws ValidationException { - Mac mac = Mac.getInstance ("HmacSHA512", "BC"); - SecretKey seedkey = new SecretKeySpec (("mnemonic" + passphrase).getBytes ("UTF-8"), "HmacSHA512"); - mac.init (seedkey); - byte[] seed = mnenonic.getBytes (); - for ( int i = 0; i < 10000; ++i ) + Mac mac; + try + { + mac = Mac.getInstance ("HmacSHA512", "BC"); + SecretKey seedkey = new SecretKeySpec (("mnemonic" + passphrase).getBytes ("UTF-8"), "HmacSHA512"); + mac.init (seedkey); + byte[] seed = mnenonic.getBytes (); + for ( int i = 0; i < 10000; ++i ) + { + seed = mac.doFinal (seed); + } + return seed; + } + catch ( NoSuchAlgorithmException | NoSuchProviderException | UnsupportedEncodingException | InvalidKeyException e ) { - seed = mac.doFinal (seed); + throw new ValidationException (e); } - return seed; } public static String getMnemonic (byte[] data) throws ValidationException diff --git a/api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java b/api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java index 37798404..6df620b0 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java @@ -19,9 +19,6 @@ import java.io.IOException; import java.io.InputStream; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; import java.security.SecureRandom; import java.security.Security; import java.util.Arrays; @@ -63,8 +60,7 @@ private JSONObject readObject (String resource) throws IOException, JSONExceptio } @Test - public void bip39TrezorTest () throws IOException, JSONException, ValidationException, InvalidKeyException, NoSuchAlgorithmException, - NoSuchProviderException + public void bip39TrezorTest () throws IOException, JSONException, ValidationException { JSONObject testData = readObject (TESTS); JSONArray english = testData.getJSONArray ("english"); @@ -78,8 +74,7 @@ public void bip39TrezorTest () throws IOException, JSONException, ValidationExce } @Test - public void bip39EncodeDecodeTest () throws ValidationException, IOException, JSONException, InvalidKeyException, NoSuchAlgorithmException, - NoSuchProviderException + public void bip39EncodeDecodeTest () throws IOException, JSONException, ValidationException { JSONObject testData = readObject (TESTS); JSONArray english = testData.getJSONArray ("english"); From bd972e51ec332e62f325db50bf14d100995e2324 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sun, 8 Dec 2013 21:13:16 +0100 Subject: [PATCH 261/299] fixes in BIP39 COmbine Shamir's secret sharing scheme with BIP39 --- .../supernode/common/ExtendedKey.java | 7 ++ .../bitsofproof/supernode/wallet/BIP39.java | 10 +- .../wallet/ShamirsSecretSharing.java | 108 +++++++++++++----- .../bitsofproof/supernode/api/BIP39Test.java | 4 +- .../api/SahmirsSecretSharingTest.java | 80 ++++++++++--- 5 files changed, 156 insertions(+), 53 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/common/ExtendedKey.java b/api/src/main/java/com/bitsofproof/supernode/common/ExtendedKey.java index 2fa545d1..28f6542d 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/ExtendedKey.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/ExtendedKey.java @@ -40,6 +40,8 @@ import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; +import com.bitsofproof.supernode.wallet.BIP39; + /** * Key Generator following BIP32 https://en.bitcoin.it/wiki/BIP_0032 */ @@ -56,6 +58,11 @@ public class ExtendedKey private static final byte[] BITCOIN_SEED = "Bitcoin seed".getBytes (); + public static ExtendedKey createFromMnemonic (String mnemonic, String passphrase) throws ValidationException + { + return ExtendedKey.create (BIP39.getSeed (mnemonic, passphrase)); + } + public static ExtendedKey createFromPassphrase (String passphrase, byte[] encrypted) throws ValidationException { try diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java b/api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java index 8ca41b27..d3ee225a 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java @@ -58,10 +58,10 @@ public static byte[] decode (String mnemonic, String passphrase) throws Validati { data[i / 8] |= (bits[i] ? 1 : 0) << (7 - (i % 8)); } - byte check = Hash.sha256 (data)[0]; + byte[] check = Hash.sha256 (data); for ( i = bits.length / 33 * 32; i < bits.length; ++i ) { - if ( (check & (1 << (7 - (i % 8))) ^ (bits[i] ? 1 : 0) << (7 - (i % 8))) != 0 ) + if ( (check[(i - bits.length / 33 * 32) / 8] & (1 << (7 - (i % 8))) ^ (bits[i] ? 1 : 0) << (7 - (i % 8))) != 0 ) { throw new ValidationException ("invalid mnemonic - checksum failed"); } @@ -131,11 +131,11 @@ public static byte[] getSeed (String mnenonic, String passphrase) throws Validat public static String getMnemonic (byte[] data) throws ValidationException { - if ( data.length % 4 != 0 || data.length < 16 || data.length > 32 ) + if ( data.length % 4 != 0 ) { throw new ValidationException ("Invalid data length for mnemonic"); } - byte check = Hash.sha256 (data)[0]; + byte[] check = Hash.sha256 (data); boolean[] bits = new boolean[data.length * 8 + data.length / 4]; @@ -148,7 +148,7 @@ public static String getMnemonic (byte[] data) throws ValidationException } for ( int i = 0; i < data.length / 4; i++ ) { - bits[8 * data.length + i] = (check & (1 << (7 - i))) > 0; + bits[8 * data.length + i] = (check[i / 8] & (1 << (7 - (i % 8)))) > 0; } int mlen = data.length * 3 / 4; diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/ShamirsSecretSharing.java b/api/src/main/java/com/bitsofproof/supernode/wallet/ShamirsSecretSharing.java index ea28e149..7552325b 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/ShamirsSecretSharing.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/ShamirsSecretSharing.java @@ -17,6 +17,10 @@ import java.math.BigInteger; import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.List; + +import org.bouncycastle.util.Arrays; import com.bitsofproof.supernode.common.ValidationException; @@ -24,71 +28,101 @@ public class ShamirsSecretSharing { public static class SecretShare { - public BigInteger m; - public BigInteger x; - public BigInteger y; - public int n; + public byte x; + public byte dl; + public byte yl; + public byte[] y; } private final static SecureRandom random = new SecureRandom (); - public static SecretShare[] cut (BigInteger secret, int pieces, int needed) + public static List issueMnemonicShares (byte[] data, int pieces, int needed, String passphrase) throws ValidationException + { + List m = new ArrayList<> (); + for ( SecretShare share : cut (data, pieces, needed) ) + { + int l = share.y.length + 3; + if ( l % 8 != 0 ) + { + l += 8 - l % 8; + } + byte[] e = new byte[l]; + e[0] = share.x; + e[1] = share.dl; + e[2] = (byte) share.y.length; + System.arraycopy (share.y, 0, e, 3, share.y.length); + m.add (BIP39.encode (e, passphrase)); + } + return m; + } + + public static byte[] reconstructFromMnemonicShares (List shareList, String passphrase) throws ValidationException { - BigInteger mod = BigInteger.probablePrime (secret.bitLength () + 1, random); + SecretShare[] shares = new SecretShare[shareList.size ()]; + int i = 0; + for ( String m : shareList ) + { + byte[] e = BIP39.decode (m, passphrase); + shares[i] = new SecretShare (); + shares[i].x = e[0]; + shares[i].dl = e[1]; + shares[i].yl = e[2]; + shares[i].y = Arrays.copyOfRange (e, 3, shares[i].yl); + ++i; + } + return reconstruct (shares); + } + + public static SecretShare[] cut (byte[] secret, int pieces, int needed) throws ValidationException + { + if ( secret.length > 127 ) + { + throw new ValidationException ("secret too long"); + } BigInteger[] coeff = new BigInteger[needed - 1]; for ( int i = 0; i < coeff.length; ++i ) { - byte[] r = new byte[secret.bitLength () / 8]; + byte[] r = new byte[secret.length]; random.nextBytes (r); - coeff[i] = new BigInteger (1, r); + coeff[i] = new BigInteger (r); } SecretShare[] shares = new SecretShare[pieces]; for ( int x = 1; x <= pieces; ++x ) { int pow = x; - BigInteger poly = secret; + BigInteger poly = new BigInteger (1, secret); for ( int i = 0; i < needed - 1; ++i ) { - poly = poly.add (BigInteger.valueOf (pow).multiply (coeff[i]).mod (mod)).mod (mod); + poly = poly.add (BigInteger.valueOf (pow).multiply (coeff[i])); pow *= x; } shares[x - 1] = new SecretShare (); - shares[x - 1].m = mod; - shares[x - 1].x = BigInteger.valueOf (x); - shares[x - 1].y = poly; - shares[x - 1].n = needed; + shares[x - 1].x = (byte) x; + shares[x - 1].dl = (byte) secret.length; + shares[x - 1].y = poly.toByteArray (); + shares[x - 1].yl = (byte) poly.toByteArray ().length; } return shares; } - public static BigInteger reconstruct (SecretShare[] shares) throws ValidationException + public static byte[] reconstruct (SecretShare[] shares) throws ValidationException { for ( int i = 0; i < shares.length - 1; ++i ) { - if ( !shares[i].m.equals (shares[i + 1].m) || - shares[i].n != shares[i + 1].n ) - { - throw new ValidationException ("Not shares of the same secret"); - } for ( int j = 0; j < shares.length; ++j ) { - if ( i != j && shares[i].x.equals (shares[j].x) ) + if ( i != j && shares[i].x == shares[j].x ) { throw new ValidationException ("Shares are not unique"); } } } - if ( shares.length < shares[0].n ) - { - throw new ValidationException ("Not enough shares"); - } - BigInteger m = shares[0].m; BigInteger[] y = new BigInteger[shares.length]; for ( int i = 0; i < shares.length; ++i ) { - y[i] = shares[i].y; + y[i] = new BigInteger (shares[i].y); } int d, i; for ( d = 1; d < shares.length; d++ ) @@ -96,11 +130,23 @@ public static BigInteger reconstruct (SecretShare[] shares) throws ValidationExc for ( i = 0; i < shares.length - d; i++ ) { int j = i + d; - BigInteger num = shares[j].x.multiply (y[i]).mod (m).subtract (shares[i].x.multiply (y[i + 1]).mod (m)).mod (m); - BigInteger den = shares[j].x.subtract (shares[i].x).mod (m); - y[i] = num.multiply (den.modInverse (m)).mod (m); + BigInteger num = + BigInteger.valueOf (shares[j].x).multiply (y[i]).subtract (BigInteger.valueOf (shares[i].x).multiply (y[i + 1])); + BigInteger den = BigInteger.valueOf (shares[j].x).subtract (BigInteger.valueOf (shares[i].x)); + y[i] = num.divide (den); } } - return y[0]; + byte[] result = y[0].toByteArray (); + if ( result.length > shares[0].dl ) + { + result = Arrays.copyOfRange (result, result.length - shares[0].dl, result.length); + } + if ( result.length < shares[0].dl ) + { + byte[] tmp = new byte[shares[0].dl]; + System.arraycopy (result, 0, tmp, shares[0].dl - result.length, result.length); + result = tmp; + } + return result; } } diff --git a/api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java b/api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java index 6df620b0..729e3d90 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java @@ -85,9 +85,9 @@ public void bip39EncodeDecodeTest () throws IOException, JSONException, Validati assertTrue (test.getString (1).equals (BIP39.encode (m, "BOP"))); } SecureRandom random = new SecureRandom (); - for ( int i = 0; i < 100; ++i ) + for ( int i = 0; i < 1000; ++i ) { - byte[] secret = new byte[16]; + byte[] secret = new byte[32]; random.nextBytes (secret); String e = BIP39.encode (secret, "BOP"); assertTrue (Arrays.equals (BIP39.decode (e, "BOP"), secret)); diff --git a/api/src/test/java/com/bitsofproof/supernode/api/SahmirsSecretSharingTest.java b/api/src/test/java/com/bitsofproof/supernode/api/SahmirsSecretSharingTest.java index 6e86d4c2..29ee8a9e 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/SahmirsSecretSharingTest.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/SahmirsSecretSharingTest.java @@ -2,11 +2,16 @@ import static org.junit.Assert.assertTrue; -import java.math.BigInteger; import java.security.SecureRandom; +import java.security.Security; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Set; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.BeforeClass; import org.junit.Test; import com.bitsofproof.supernode.common.ValidationException; @@ -14,31 +19,76 @@ public class SahmirsSecretSharingTest { + @BeforeClass + public static void init () + { + Security.addProvider (new BouncyCastleProvider ()); + } + @Test - public void test () throws ValidationException + public void testBinary () throws ValidationException { SecureRandom random = new SecureRandom (); - for ( int s = 3; s < 10; ++s ) + for ( int i = 0; i < 100; ++i ) { - for ( int g = 1; g < s - 1; ++g ) + for ( int s = 3; s < 10; ++s ) { - for ( int n = 2; n < s - g; ++n ) + for ( int g = 0; g < s; ++g ) { - BigInteger secret = new BigInteger (256, random); - ShamirsSecretSharing.SecretShare[] all = ShamirsSecretSharing.cut (secret, s, n); + for ( int n = 2; n < (s - g); ++n ) + { + byte[] secret = new byte[32]; + random.nextBytes (secret); + ShamirsSecretSharing.SecretShare[] all = ShamirsSecretSharing.cut (secret, s, n); + + ShamirsSecretSharing.SecretShare[] some = new ShamirsSecretSharing.SecretShare[n]; + Set pick = new HashSet (); + while ( pick.size () < n ) + { + int p = random.nextInt (s); + if ( !pick.contains (p) ) + { + some[pick.size ()] = all[p]; + pick.add (p); + } + } + byte[] recreated = ShamirsSecretSharing.reconstruct (some); + assertTrue (Arrays.equals (recreated, secret)); + } + } + } + } + } - ShamirsSecretSharing.SecretShare[] some = new ShamirsSecretSharing.SecretShare[n]; - Set pick = new HashSet (); - while ( pick.size () < n ) + public void testMnemonic () throws ValidationException + { + SecureRandom random = new SecureRandom (); + for ( int i = 0; i < 1000; ++i ) + { + for ( int s = 3; s < 10; ++s ) + { + for ( int g = 0; g < s; ++g ) + { + for ( int n = 2; n < (s - g); ++n ) { - int p = random.nextInt (n); - if ( !pick.contains (p) ) + byte[] secret = new byte[32]; + random.nextBytes (secret); + List all = ShamirsSecretSharing.issueMnemonicShares (secret, s, n, "BOP"); + + List some = new ArrayList<> (); + Set pick = new HashSet (); + while ( pick.size () < n ) { - some[pick.size ()] = all[p]; - pick.add (p); + int p = random.nextInt (s); + if ( !pick.contains (p) ) + { + some.add (all.get (p)); + pick.add (p); + } } + byte[] recreated = ShamirsSecretSharing.reconstructFromMnemonicShares (some, "BOP"); + assertTrue (Arrays.equals (recreated, secret)); } - assertTrue (ShamirsSecretSharing.reconstruct (some).equals (secret)); } } } From 9e3e9a7fad58521f64763ba249882630f5af651d Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sun, 8 Dec 2013 22:45:47 +0100 Subject: [PATCH 262/299] fixes and tests for Shamir's --- .../java/com/bitsofproof/supernode/common/ExtendedKey.java | 7 ------- .../bitsofproof/supernode/wallet/ShamirsSecretSharing.java | 2 +- .../supernode/api/SahmirsSecretSharingTest.java | 3 ++- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/common/ExtendedKey.java b/api/src/main/java/com/bitsofproof/supernode/common/ExtendedKey.java index 28f6542d..2fa545d1 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/ExtendedKey.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/ExtendedKey.java @@ -40,8 +40,6 @@ import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; -import com.bitsofproof.supernode.wallet.BIP39; - /** * Key Generator following BIP32 https://en.bitcoin.it/wiki/BIP_0032 */ @@ -58,11 +56,6 @@ public class ExtendedKey private static final byte[] BITCOIN_SEED = "Bitcoin seed".getBytes (); - public static ExtendedKey createFromMnemonic (String mnemonic, String passphrase) throws ValidationException - { - return ExtendedKey.create (BIP39.getSeed (mnemonic, passphrase)); - } - public static ExtendedKey createFromPassphrase (String passphrase, byte[] encrypted) throws ValidationException { try diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/ShamirsSecretSharing.java b/api/src/main/java/com/bitsofproof/supernode/wallet/ShamirsSecretSharing.java index 7552325b..cccaa8fa 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/ShamirsSecretSharing.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/ShamirsSecretSharing.java @@ -67,7 +67,7 @@ public static byte[] reconstructFromMnemonicShares (List shareList, Stri shares[i].x = e[0]; shares[i].dl = e[1]; shares[i].yl = e[2]; - shares[i].y = Arrays.copyOfRange (e, 3, shares[i].yl); + shares[i].y = Arrays.copyOfRange (e, 3, shares[i].yl + 3); ++i; } return reconstruct (shares); diff --git a/api/src/test/java/com/bitsofproof/supernode/api/SahmirsSecretSharingTest.java b/api/src/test/java/com/bitsofproof/supernode/api/SahmirsSecretSharingTest.java index 29ee8a9e..de936374 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/SahmirsSecretSharingTest.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/SahmirsSecretSharingTest.java @@ -60,10 +60,11 @@ public void testBinary () throws ValidationException } } + @Test public void testMnemonic () throws ValidationException { SecureRandom random = new SecureRandom (); - for ( int i = 0; i < 1000; ++i ) + for ( int i = 0; i < 10; ++i ) { for ( int s = 3; s < 10; ++s ) { From 0097d654f33a6fef959e77372e340cad8578f831 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Tue, 10 Dec 2013 15:12:28 +0100 Subject: [PATCH 263/299] multisig signature order does matter --- .../supernode/common/ScriptFormat.java | 101 ++++++++++++++++++ .../supernode/core/ScriptEvaluation.java | 58 +++++++--- .../supernode/test/TransactionTest.java | 16 +++ 3 files changed, 159 insertions(+), 16 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/common/ScriptFormat.java b/api/src/main/java/com/bitsofproof/supernode/common/ScriptFormat.java index 2c03974e..67e06c0d 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/ScriptFormat.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/ScriptFormat.java @@ -29,6 +29,11 @@ public class ScriptFormat public static final int SIGHASH_NONE = 2; public static final int SIGHASH_SINGLE = 3; public static final int SIGHASH_ANYONECANPAY = 0x80; + public static final int SCRIPT_VERIFY_NONE = 0; + public static final int SCRIPT_VERIFY_P2SH = 1; + public static final int SCRIPT_VERIFY_STRICTENC = 2; + public static final int SCRIPT_VERIFY_EVEN_S = 4; + public static final int SCRIPT_VERIFY_NOCACHE = 8; public static enum Opcode { @@ -784,4 +789,100 @@ public static byte[] deleteSignatureFromScript (byte[] script, byte[] sig) throw } return writer.toByteArray (); } + + public static boolean isCanonicalSignature (byte[] sig, int flags) throws ValidationException + { + if ( (flags & SCRIPT_VERIFY_STRICTENC) == 0 ) + { + return true; + } + + // See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 + // A canonical signature exists of: <30> <02> <02> + // Where R and S are not negative (their first byte has its highest bit not set), and not + // excessively padded (do not start with a 0 byte, unless an otherwise negative number follows, + // in which case a single 0 byte is necessary and even required). + if ( sig.length < 9 ) + { + throw new ValidationException ("Non-canonical signature: too short"); + } + if ( sig.length > 73 ) + { + throw new ValidationException ("Non-canonical signature: too long"); + } + byte nHashType = (byte) (sig[sig.length - 1] & (~(ScriptFormat.SIGHASH_ANYONECANPAY))); + if ( nHashType < ScriptFormat.SIGHASH_ALL || nHashType > ScriptFormat.SIGHASH_SINGLE ) + { + throw new ValidationException ("Non-canonical signature: unknown hashtype byte"); + } + if ( sig[0] != 0x30 ) + { + throw new ValidationException ("Non-canonical signature: wrong type"); + } + if ( sig[1] != sig.length - 3 ) + { + throw new ValidationException ("Non-canonical signature: wrong length marker"); + } + int nLenR = sig[3]; + if ( 5 + nLenR - sig.length >= 0 ) + { + throw new ValidationException ("Non-canonical signature: S length misplaced"); + } + int nLenS = sig[5 + nLenR]; + if ( (nLenR + nLenS + 7) - sig.length != 0 ) + { + throw new ValidationException ("Non-canonical signature: R+S length mismatch"); + } + // const unsigned char *R = &vchSig[4]; + // if (R[-2] != 0x02) + if ( sig[2] != 0x02 ) + { + throw new ValidationException ("Non-canonical signature: R value type mismatch"); + } + if ( nLenR == 0 ) + { + throw new ValidationException ("Non-canonical signature: R length is zero"); + } + // if (R[0] & 0x80) + if ( (sig[4] & 0x80) != 0 ) + { + throw new ValidationException ("Non-canonical signature: R value negative"); + } + // if (nLenR > 1 && (R[0] == 0x00) && !(R[1] & 0x80)) + if ( nLenR > 1 && (sig[4] == 0x00) && (sig[5] & 0x80) == 0 ) + { + throw new ValidationException ("Non-canonical signature: R value excessively padded"); + } + // const unsigned char *S = &vchSig[6+nLenR]; + // if (S[-2] != 0x02) + if ( sig[nLenR + 6 - 2] != 0x02 ) + { + throw new ValidationException ("Non-canonical signature: S value type mismatch"); + } + if ( nLenS == 0 ) + { + throw new ValidationException ("Non-canonical signature: S length is zero"); + } + // if (S[0] & 0x80) + if ( (sig[6 + nLenR] & 0x80) != 0 ) + { + throw new ValidationException ("Non-canonical signature: S value negative"); + } + // if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80)) + if ( nLenS > 1 && (sig[6 + nLenR] == 0x00) && (sig[6 + nLenR + 1] & 0x80) == 0 ) + { + throw new ValidationException ("Non-canonical signature: S value excessively padded"); + } + + if ( (flags & SCRIPT_VERIFY_EVEN_S) != 0 ) + { + // if (S[nLenS-1] & 1) + if ( (sig[6 + nLenR + nLenS - 1] & 1) != 0 ) + { + throw new ValidationException ("Non-canonical signature: S value odd"); + } + } + + return true; + } } diff --git a/server/src/main/java/com/bitsofproof/supernode/core/ScriptEvaluation.java b/server/src/main/java/com/bitsofproof/supernode/core/ScriptEvaluation.java index 672236a1..ec2c3111 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/ScriptEvaluation.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/ScriptEvaluation.java @@ -31,9 +31,9 @@ import com.bitsofproof.supernode.common.ECKeyPair; import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.ScriptFormat; +import com.bitsofproof.supernode.common.ScriptFormat.Opcode; import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.common.WireFormat; -import com.bitsofproof.supernode.common.ScriptFormat.Opcode; import com.bitsofproof.supernode.model.Tx; import com.bitsofproof.supernode.model.TxIn; import com.bitsofproof.supernode.model.TxOut; @@ -48,12 +48,23 @@ public class ScriptEvaluation private final Tx tx; private TxOut source; private int inr; + private int canonicalChecks = ScriptFormat.SCRIPT_VERIFY_NOCACHE | ScriptFormat.SCRIPT_VERIFY_NONE; public int getInr () { return inr; } + public int getCanonicalChecks () + { + return canonicalChecks; + } + + public void setCanonicalChecks (int canonicalChecks) + { + this.canonicalChecks = canonicalChecks; + } + private void push (byte[] data) throws ValidationException { if ( data.length > 520 ) @@ -804,13 +815,19 @@ public boolean evaluateSingleScript (byte[] script) throws ValidationException { return false; } + opcodeCount += nkeys; + if ( opcodeCount > 201 ) + { + return false; + } + byte[][] keys = new byte[nkeys][]; for ( int i = 0; i < nkeys; ++i ) { keys[i] = stack.pop (); } int required = (int) popInt (); - if ( required <= 0 ) + if ( required < 0 || required > nkeys ) { return false; } @@ -826,26 +843,35 @@ public boolean evaluateSingleScript (byte[] script) throws ValidationException sts = ScriptFormat.deleteSignatureFromScript (sts, sigs[i]); } stack.pop (); // reproduce Satoshi client bug - int successCounter = 0; - for ( int i = 0; (required - successCounter) <= (nkeys - i) && i < nkeys; ++i ) + + boolean fSuccess = true; + int isig = 0; + int ikey = 0; + while ( fSuccess && havesig > 0 ) { - for ( int j = 0; successCounter < required && j < havesig; ++j ) + try { - try + if ( ScriptFormat.isCanonicalSignature (sigs[isig], canonicalChecks) && validateSignature (keys[ikey], sigs[isig], sts) ) { - if ( validateSignature (keys[i], sigs[j], sts) ) - { - ++successCounter; - } - } - catch ( Exception e ) - { - // attempt to validate other no matter if there are - // format error in this + isig++; + havesig--; } } + catch ( Exception e ) + { + // attempt to validate other no matter if there are + // format error in this + } + ikey++; + nkeys--; + + if ( havesig > nkeys ) + { + fSuccess = false; + } } - pushInt (successCounter == required ? 1 : 0); + + pushInt (fSuccess ? 1 : 0); } break; } diff --git a/server/src/test/java/com/bitsofproof/supernode/test/TransactionTest.java b/server/src/test/java/com/bitsofproof/supernode/test/TransactionTest.java index 03191ce0..11da8744 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/TransactionTest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/TransactionTest.java @@ -558,5 +558,21 @@ public ValidationException call () throws Exception Tx.fromWireDump ("0100000001106f0a9fdee8618002cdda338341cedcef81023bfde09d2832d8895c443c9bc4000000004b004930460221008bcf597d5d6a7b2ccf3913983a2d978953e745fa6431331539ded8dc5cbd9f1402210093a4375f9d880fe506e3abcec15a919718e5946b21f823c6d35b539a2386b82701ffffffff0100350c00000000001976a914c676ea066cc2a6b41b0ace8e287fd38030f0b97c88ac00000000"); assertTrue (new ScriptEvaluation (t1, 0, t2.getOutputs ().get (0)).evaluate (true)); + + // correct signature order + t1 = + Tx.fromWireDump ("01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfd000047304402203517cfb4e66d34e9214390b781568c4c53658b2dd251d3f51b75048e05434639022049b6ecdc9c6d0c442ec130dec698c171136f72f423236c5cb61328e889af33c8014830450220142e12366006f68251ffb97e0c973b01da1d8c90501ab5f73158341eeb7e3aef022100945d967539921616af8fa8bdd9ae9be0e98fe5df6240b76948b4a00af9910f74014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000"); + t2 = + Tx.fromWireDump ("0100000001f7301459ac875fe8a28ccf45ceaab6370a7c5791a4248a1234d54b5ccde2951d000000006c493046022100ec0f98b5d40e80d1ed766e098ea7d19d2238f4d0c3b282d588b8226738b16758022100c8c59c2d04d1234fbd1ba07792bba9477d8934f43b35c02f200c0590b9b059ef0121021803e40e13a344074cb437b8666712400c9ea17dbda076368c7c5a337d567039ffffffff02905f01000000000017a914b1ce99298d5f07364b57b1e5c9cc00be0b04a95487a0860100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000"); + assertTrue (new ScriptEvaluation (t1, 0, t2.getOutputs ().get (0)).evaluate (true)); + + // wrong signature order + t1 = + Tx.fromWireDump ("01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe000048304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f401483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000"); + t2 = + Tx.fromWireDump ("0100000001f7301459ac875fe8a28ccf45ceaab6370a7c5791a4248a1234d54b5ccde2951d000000006c493046022100ec0f98b5d40e80d1ed766e098ea7d19d2238f4d0c3b282d588b8226738b16758022100c8c59c2d04d1234fbd1ba07792bba9477d8934f43b35c02f200c0590b9b059ef0121021803e40e13a344074cb437b8666712400c9ea17dbda076368c7c5a337d567039ffffffff02905f01000000000017a914b1ce99298d5f07364b57b1e5c9cc00be0b04a95487a0860100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000"); + + assertFalse (new ScriptEvaluation (t1, 0, t2.getOutputs ().get (0)).evaluate (true)); + } } From 2fc941f94f08d1ab174efa6aeaf729953f74fe22 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Tue, 10 Dec 2013 15:32:41 +0100 Subject: [PATCH 264/299] [maven-release-plugin] prepare release 1.3.6 --- api/pom.xml | 2 +- pom.xml | 4 ++-- server/pom.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index d836859d..2bd47e79 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 1.3.6-SNAPSHOT + 1.3.6 ../ diff --git a/pom.xml b/pom.xml index c5d4c443..32814477 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.bitsofproof supernode - 1.3.6-SNAPSHOT + 1.3.6 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom @@ -78,7 +78,7 @@ scm:git:git@github.com:bitsofproof/supernode.git scm:git:git@github.com:bitsofproof/supernode.git https://github.com/bitsofproof/supernode - HEAD + 1.3.6 diff --git a/server/pom.xml b/server/pom.xml index ee79113e..ea7ea49c 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 1.3.6-SNAPSHOT + 1.3.6 ../ From 12a95d7e972ad2e225ff76c30d56c557ad9b9b27 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Tue, 10 Dec 2013 15:32:46 +0100 Subject: [PATCH 265/299] [maven-release-plugin] prepare for next development iteration --- api/pom.xml | 2 +- pom.xml | 4 ++-- server/pom.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 2bd47e79..2d1d35fc 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 1.3.6 + 1.3.7-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index 32814477..21f2d995 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.bitsofproof supernode - 1.3.6 + 1.3.7-SNAPSHOT supernode The bitsofproof supernode implementation of the Bitcoin protocol pom @@ -78,7 +78,7 @@ scm:git:git@github.com:bitsofproof/supernode.git scm:git:git@github.com:bitsofproof/supernode.git https://github.com/bitsofproof/supernode - 1.3.6 + HEAD diff --git a/server/pom.xml b/server/pom.xml index ea7ea49c..bc356080 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 1.3.6 + 1.3.7-SNAPSHOT ../ From b84406776223a9254e5409d944902e45c33b6698 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Tue, 10 Dec 2013 19:18:04 +0100 Subject: [PATCH 266/299] superseeded --- .../bitsofproof/supernode/wallet/BIP39.java | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java b/api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java index d3ee225a..8f46c34c 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java @@ -25,7 +25,6 @@ import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; -import javax.crypto.Mac; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; @@ -108,27 +107,6 @@ public static String encode (byte[] data, String passphrase) throws ValidationEx return getMnemonic (data); } - public static byte[] getSeed (String mnenonic, String passphrase) throws ValidationException - { - Mac mac; - try - { - mac = Mac.getInstance ("HmacSHA512", "BC"); - SecretKey seedkey = new SecretKeySpec (("mnemonic" + passphrase).getBytes ("UTF-8"), "HmacSHA512"); - mac.init (seedkey); - byte[] seed = mnenonic.getBytes (); - for ( int i = 0; i < 10000; ++i ) - { - seed = mac.doFinal (seed); - } - return seed; - } - catch ( NoSuchAlgorithmException | NoSuchProviderException | UnsupportedEncodingException | InvalidKeyException e ) - { - throw new ValidationException (e); - } - } - public static String getMnemonic (byte[] data) throws ValidationException { if ( data.length % 4 != 0 ) From 7518fde7c8e365d995bea14a616b9be260265b52 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Tue, 10 Dec 2013 19:19:40 +0100 Subject: [PATCH 267/299] superseeded by Trezor team. Waiting for next iteration --- api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java | 1 - 1 file changed, 1 deletion(-) diff --git a/api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java b/api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java index 729e3d90..2c5b255a 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java @@ -69,7 +69,6 @@ public void bip39TrezorTest () throws IOException, JSONException, ValidationExce JSONArray test = english.getJSONArray (i); String m = BIP39.getMnemonic (ByteUtils.fromHex (test.getString (i))); assertTrue (m.equals (test.getString (i + 1))); - assertTrue (ByteUtils.toHex (BIP39.getSeed (m, "TREZOR")).equals (test.getString (i + 2))); } } From 336b67777cfc9bff8461cf7b01f80ce386766589 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Tue, 10 Dec 2013 19:23:00 +0100 Subject: [PATCH 268/299] removed superseeded tests --- api/src/test/resources/BIP39.json | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/api/src/test/resources/BIP39.json b/api/src/test/resources/BIP39.json index 0cba395b..9b536a2c 100644 --- a/api/src/test/resources/BIP39.json +++ b/api/src/test/resources/BIP39.json @@ -3,122 +3,98 @@ [ "00000000000000000000000000000000", "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", - "cb5e7230ce8229de990674f6aa4288325fd4d8181f761734bd8b5cc944fedc2a4300e64422864b565352de7ffbc5ad0fafdf5344489f3a83e4a4bb5271cafaae" ], [ "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", "legal winner thank year wave sausage worth useful legal winner thank yellow", - "de1277934939d6969519f44b7b3757a905d7f635be41e1e88022c346bc52ad26c0a3e9578e73e9b89066873266f285a5891d27d28cb27fccfe26d92bbd7ee364" ], [ "80808080808080808080808080808080", "letter advice cage absurd amount doctor acoustic avoid letter advice cage above", - "8863bccef9cfffeacef1e4c6fc97bba8227ab0fc7e8e162be7467282689a13521ea364d7c4bc8cd241b59f53c5147a89c18a47248a96592ab9a2c1f1870b026c" ], [ "ffffffffffffffffffffffffffffffff", "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong", - "7a29e57c7a1532af1bddb7e02b892cfccc6a57b74fe9784324ea89fab8a66dc64fde79c31166b159685116f4e93c1795496f20ffdc2d3a69d3439931dabde86e" ], [ "000000000000000000000000000000000000000000000000", "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent", - "c3e382025b6a22a901505cf393faea450eb6c4a5f2a8c8f0596285b2bd84688877a6cc7231420e2bbdd2428e62ed549a78fa215b3adafd8dea075dabfc704d5e" ], [ "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will", - "c82666e40eb097bf6eb05fecd7dc2ddfb6bbdc6071900f4b3fd3c3e635db69aa2094f1f450c98e8dc6103aa72df635abdfcc3b6d6ec5261a9208a07a35a3f1c8" ], [ "808080808080808080808080808080808080808080808080", "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always", - "e90681c67c55504afadca009ce4042819341fa0e90300b6d32b4f2e8e8a6678ff7e7fc1da663ae194dc7a2ef7ec7b50112d1a5efce47bfd00c66eec82f2265b5" ], [ "ffffffffffffffffffffffffffffffffffffffffffffffff", "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when", - "2a372547df962742942674170a7cef495ea0b97f4864de16d0f3ee82eb577ca1eca345e601cc2df7c626c5bc51c52c28a3b4294224b685c958c7450bee6769e6" ], [ "0000000000000000000000000000000000000000000000000000000000000000", "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art", - "58cb9c5555d67ecc7b32305a78d1a2fcf0c9b22f1af761cfafc65eb1d3909f63ee2cab84996a7478cfd3e864cda5efb0caf580d56cf49739c6b3638d94e758c1" ], [ "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title", - "0093cb3ed6d1302d3cf498017f8cb1c7dc2fdbd62ec57fc49e4b2a4dd47a23e44e0b309517d5a3e7b0f4f0ef0ed132818cf120a098a92e572ad086f1a90ccb7f" ], [ "8080808080808080808080808080808080808080808080808080808080808080", "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless", - "8a21e46b9d264328c63e707e3d38ed4eb21508deda309fa2ef57cc8eca8b351ca3018758844ba9fb5851bab15d026a61cabace53a9a39bc91dc2c51407542cf5" ], [ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote", - "5c8a1a284ab2844daf02cab322df3996574c9d53cbd36159c493441990f0d2a6bc9bc1502e3a067943d8ec67324663cbfb9667b57fed220e3f28335e26a90f93" ], [ "1083fa24dbb0afa4e7b327d23f666567", "awesome cabin matrix resist april sponsor paddle gossip split will off soon", - "467406b36a0176e40e013393e5ecef1f5b4019980b502eda9db1db06f7786e088b206f045f2bfcf93bd3b17a598335b078fcc5890115857ff741bd154b54f049" ], [ "d8cbcd1ac2153ecd74048480c2732f637d642b21f0dd40df", "sugar fury effort loud fault grit source mountain liar bean slim shoulder stone better march brick dolphin zero", - "f60180ea5047659cbb17ed6ef79c974de86c0170c7a1962b205329eb8fe9dcdd148615d35c515c4ec8da25f4cf54d5b7cd8cd5bf8dc4059df3a7900ca25f8306" ], [ "2952f95cefe041616f6f379ab649cf8b702ecf8e4acceaebdda4cc50e2bf1d7b", "citizen oak fire thank advice radar sad tragic one rather initial black actual guitar decrease flower turtle galaxy hard obvious athlete garbage invest have", - "eff4b6a15bb55fcf4bbfa2b3b9e24e7dc4bed8319ef7703f1786d472c73666922925778eaa5a06f8a26d2c7e7240be746fd69edfaf197e0dae12d7e0b550cfc8" ], [ "f5e82717078a6ddc538a03e825f91bed", "vote donkey shift audit plug until evolve document trial cool eight swarm", - "83dad22293225780a914083fc1a69bfe1d910f5b5962b0364820132a42ae1bd567a1fb4d5a19ad3d64539e38a7ee3d6429fac2b74e72b020913131c5eadb7db4" ], [ "16b59b6a426f2f302f73049a32ab8572394278982212357a", "birth proud surround luggage very object saddle gauge olive next throw tongue neither detail gauge drastic cube strategy", - "38ceb07e0dad221f612631843be6ae44a650aaf789c8ebea9313e07498d7864385227d25c7a8268a5b850367eef31639632e9218acadead20980b864b1cd477e" ], [ "95b6cb48c7bc9c2a54496ae3eea790824b57e52b9637058f084555bc1b809b2f", "noble rent split month six benefit eye coil token inside tomorrow afraid rely verb purity shoulder airport joke bacon problem script scare hole trumpet", - "e33e3d32e467877596a18ac60050488a0ec1557fda6bf95bad3d33d964c5e99dcd97d378403cc2723ed1c85c12b42bc59f15458d970d7a9d015f556109c146b0" ], [ "7f93397f750f70a26513de2732ed95ee", "legend oil garlic tube warfare eye nephew knock cheese number grace tackle", - "7f92ad63e4cdf4f15c23740556ad81e7f8cbd67cc672c93894c9c0d4fb171539eed5ab29f366570ed9940b816f45a539c3816f7ac19511794b752c5c1ec0e732" ], [ "14c29fe840dd1c9f05d392ba13e4e1466b32ed0726a15f89", "below belt wheel like spike exhibit blanket inch ring palace debate mimic rebel isolate broken stage garbage enhance", - "7bae6e54f8bad645f18f574b310bd3e6fde126dabcaf63a889940380e4798810e48c8151fc56bb2389c07498deacef025f03cbf8fc57ea3ec68f6421b0fcb649" ], [ "cb30610d175ffeab8357d5190d31923997752a7f9815087bfcad5eb0b43f6468", "sleep loan drive concert zoo fiction ask wide boil hat goose industry jar news wrist actor anchor that clip runway area cabbage museum abuse", - "b922030609e7626696b9cf5ca4c06cd99290be30b1052770f6a60c5f26532d178f287a4285d7a2add2845dc89a816b26fdba1c830067d130740f64c0ab5cfbe1" ], [ "a30b50a5439dcd1774f412ea5ec33403", "perfect fold citizen mango system merry stable liquid tumble voyage snack alter", - "aae175f26848370c4d5d3d0640597e2bf1b28e95908dd877259b3eac5d71ffe3140739a3ed80180f88159571df84441985620e6b2fb0696e5cba1aa7b8d10b98" ], [ "70044da2175ad681d0ebbf2da83cf407eb9c8fd91fc0a8c9", "hybrid carbon hammer concert pulp domain dry jewel color draft dial average right elevator good way potato energy", - "a3dffe3a31a2e949d1b04af7495a5b59db17e41d93b985feeaaae89260a9c86c6dcdf7cb32eaba61c2f4f0340f0f17d1ebb67af11657286b2ffd66ec4e05a8b7" ], [ "0e0bab4df9669b97ba3f75a50b2e92423bbe6e91a1b01dbbf3ba200a917c9106", "asthma front square version have slim trophy upgrade pink floor pig love room dance educate current buffalo test update divorce poverty salad dune scheme", - "2eb4d85fbd8deaf9b06bf9cdb3e5f36e8da040d110312075eb32e776fc8e505b94be3e63c1525ad41f5e5968a263853001dc7c40ea3af8e8b0cfb7effd5f408c" ] ] } \ No newline at end of file From e4083c8f6a122fb12cf0df382deb7f38bc020d0a Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Tue, 10 Dec 2013 23:38:18 +0100 Subject: [PATCH 269/299] use same signatures for correct/incorrect order test --- .../supernode/test/TransactionTest.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/server/src/test/java/com/bitsofproof/supernode/test/TransactionTest.java b/server/src/test/java/com/bitsofproof/supernode/test/TransactionTest.java index 11da8744..7ecf8a4d 100644 --- a/server/src/test/java/com/bitsofproof/supernode/test/TransactionTest.java +++ b/server/src/test/java/com/bitsofproof/supernode/test/TransactionTest.java @@ -559,13 +559,6 @@ public ValidationException call () throws Exception assertTrue (new ScriptEvaluation (t1, 0, t2.getOutputs ().get (0)).evaluate (true)); - // correct signature order - t1 = - Tx.fromWireDump ("01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfd000047304402203517cfb4e66d34e9214390b781568c4c53658b2dd251d3f51b75048e05434639022049b6ecdc9c6d0c442ec130dec698c171136f72f423236c5cb61328e889af33c8014830450220142e12366006f68251ffb97e0c973b01da1d8c90501ab5f73158341eeb7e3aef022100945d967539921616af8fa8bdd9ae9be0e98fe5df6240b76948b4a00af9910f74014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000"); - t2 = - Tx.fromWireDump ("0100000001f7301459ac875fe8a28ccf45ceaab6370a7c5791a4248a1234d54b5ccde2951d000000006c493046022100ec0f98b5d40e80d1ed766e098ea7d19d2238f4d0c3b282d588b8226738b16758022100c8c59c2d04d1234fbd1ba07792bba9477d8934f43b35c02f200c0590b9b059ef0121021803e40e13a344074cb437b8666712400c9ea17dbda076368c7c5a337d567039ffffffff02905f01000000000017a914b1ce99298d5f07364b57b1e5c9cc00be0b04a95487a0860100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000"); - assertTrue (new ScriptEvaluation (t1, 0, t2.getOutputs ().get (0)).evaluate (true)); - // wrong signature order t1 = Tx.fromWireDump ("01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe000048304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f401483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000"); @@ -574,5 +567,12 @@ public ValidationException call () throws Exception assertFalse (new ScriptEvaluation (t1, 0, t2.getOutputs ().get (0)).evaluate (true)); + // correct signature order + t1 = + Tx.fromWireDump ("01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe0000483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa0148304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f4014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000"); + t2 = + Tx.fromWireDump ("0100000001f7301459ac875fe8a28ccf45ceaab6370a7c5791a4248a1234d54b5ccde2951d000000006c493046022100ec0f98b5d40e80d1ed766e098ea7d19d2238f4d0c3b282d588b8226738b16758022100c8c59c2d04d1234fbd1ba07792bba9477d8934f43b35c02f200c0590b9b059ef0121021803e40e13a344074cb437b8666712400c9ea17dbda076368c7c5a337d567039ffffffff02905f01000000000017a914b1ce99298d5f07364b57b1e5c9cc00be0b04a95487a0860100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000"); + + assertTrue (new ScriptEvaluation (t1, 0, t2.getOutputs ().get (0)).evaluate (true)); } } From 1249e70409d87a399a8eda13b8a721f51b2c0f3c Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Wed, 11 Dec 2013 12:23:42 +0100 Subject: [PATCH 270/299] Add tests for signature order --- server/src/test/resources/tx_invalid.json | 8 +++++++- server/src/test/resources/tx_valid.json | 7 ++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/server/src/test/resources/tx_invalid.json b/server/src/test/resources/tx_invalid.json index 8c31d3a6..943d5906 100644 --- a/server/src/test/resources/tx_invalid.json +++ b/server/src/test/resources/tx_invalid.json @@ -61,5 +61,11 @@ ["Null txin"], [[["0000000000000000000000000000000000000000000000000000000000000000", -1, "HASH160 0x14 0x02dae7dbbda56097959cba59b1989dd3e47937bf EQUAL"]], -"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff6e49304602210086f39e028e46dafa8e1e3be63906465f4cf038fbe5ed6403dc3e74ae876e6431022100c4625c675cfc5c7e3a0e0d7eaec92ac24da20c73a88eb40d09253e51ac6def5201232103a183ddc41e84753aca47723c965d1b5c8b0e2b537963518355e6dd6cf8415e50acffffffff010000000000000000015100000000", true] +"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff6e49304602210086f39e028e46dafa8e1e3be63906465f4cf038fbe5ed6403dc3e74ae876e6431022100c4625c675cfc5c7e3a0e0d7eaec92ac24da20c73a88eb40d09253e51ac6def5201232103a183ddc41e84753aca47723c965d1b5c8b0e2b537963518355e6dd6cf8415e50acffffffff010000000000000000015100000000", true], + +["Incorrect signature order"], +["Note the input is just required to make the tester happy"], +[[["b3da01dd4aae683c7aee4d5d8b52a540a508e1115f77cd7fa9a291243f501223", 0, "HASH160 0x14 0xb1ce99298d5f07364b57b1e5c9cc00be0b04a954 EQUAL"]], +"01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe000048304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f401483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000", true] +] ] diff --git a/server/src/test/resources/tx_valid.json b/server/src/test/resources/tx_valid.json index 51263370..fb78b8de 100644 --- a/server/src/test/resources/tx_valid.json +++ b/server/src/test/resources/tx_valid.json @@ -68,5 +68,10 @@ ["Coinbase of size 100"], ["Note the input is just required to make the tester happy"], [[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], -"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff6451515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151ffffffff010000000000000000015100000000", true] +"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff6451515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151ffffffff010000000000000000015100000000", true], + +["Correct signature order"], +["Note the input is just required to make the tester happy"], +[[["b3da01dd4aae683c7aee4d5d8b52a540a508e1115f77cd7fa9a291243f501223", 0, "HASH160 0x14 0xb1ce99298d5f07364b57b1e5c9cc00be0b04a954 EQUAL"]], +"01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe0000483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa0148304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f4014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000", true] ] From 0c6ce4b432cbe37d73d46d1373e2ef936c3acb75 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 13 Dec 2013 15:23:28 +0100 Subject: [PATCH 271/299] re-added missing context for testing --- .../main/resources/context/memdb-profile.xml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 server/src/main/resources/context/memdb-profile.xml diff --git a/server/src/main/resources/context/memdb-profile.xml b/server/src/main/resources/context/memdb-profile.xml new file mode 100644 index 00000000..25f53145 --- /dev/null +++ b/server/src/main/resources/context/memdb-profile.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + From 88dfb359ae9fecab33e3f831253df748c35732d9 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 27 Dec 2013 17:16:51 +0100 Subject: [PATCH 272/299] Added serialized or encrypted HD key root following https://bitcointalk.org/index.php?topic=258678.0 --- .../supernode/wallet/EncryptedHDRoot.java | 297 ++++++++++++++++++ .../supernode/api/EncryptedHDRootTest.java | 85 +++++ api/src/test/resources/EncryptedHDRoot.json | 50 +++ 3 files changed, 432 insertions(+) create mode 100644 api/src/main/java/com/bitsofproof/supernode/wallet/EncryptedHDRoot.java create mode 100644 api/src/test/java/com/bitsofproof/supernode/api/EncryptedHDRootTest.java create mode 100644 api/src/test/resources/EncryptedHDRoot.json diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/EncryptedHDRoot.java b/api/src/main/java/com/bitsofproof/supernode/wallet/EncryptedHDRoot.java new file mode 100644 index 00000000..2143d5ad --- /dev/null +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/EncryptedHDRoot.java @@ -0,0 +1,297 @@ +/* + * Copyright 2013 bits of proof zrt. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.bitsofproof.supernode.wallet; + +import java.io.UnsupportedEncodingException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +import org.bouncycastle.crypto.generators.SCrypt; + +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.ExtendedKey; +import com.bitsofproof.supernode.common.Hash; +import com.bitsofproof.supernode.common.ValidationException; + +/** + * BIP of serialized or encrypted HD key root discussion: https://bitcointalk.org/index.php?topic=258678.0 + */ +public class EncryptedHDRoot +{ + public static enum ScryptDifficulty + { + LOW, MEDIUM, HIGH + } + + private static final byte[] clear16 = { 0x0b, 0x2d, 0x7b }; + private static final byte[] clear32 = { 0x14, (byte) 0x82, 0x17 }; + private static final byte[] clear64 = { 0x01, 0x30, (byte) 0xb7 }; + private static final byte[] encrypted16 = { 0x14, (byte) 0xd6, 0x0d }; + private static final byte[] encrypted16l = encrypted16; + private static final byte[] encrypted16m = { 0x14, (byte) 0xd6, 0x0e }; + private static final byte[] encrypted16h = { 0x14, (byte) 0xd6, 0x0f }; + private static final byte[] encrypted32 = { 0x26, 0x3a, (byte) 0xa2 }; + private static final byte[] encrypted32l = encrypted32; + private static final byte[] encrypted32m = { 0x26, 0x3a, (byte) 0xa3 }; + private static final byte[] encrypted32h = { 0x26, 0x3a, (byte) 0xa4 }; + private static final byte[] encrypted64 = { 0x02, 0x38, 0x04 }; + private static final byte[] encrypted64l = encrypted64; + private static final byte[] encrypted64m = { 0x02, 0x38, 0x05 }; + private static final byte[] encrypted64h = { 0x02, 0x38, 0x06 }; + + public Date decodeBirthDate (String ws) throws ValidationException + { + byte[] raw = ByteUtils.fromBase58WithChecksum (ws); + int weeks = raw[3] + raw[4] << 8; + Calendar c = new GregorianCalendar (2013, Calendar.JANUARY, 1); + c.add (Calendar.DAY_OF_YEAR, weeks * 7); + return c.getTime (); + } + + public static ExtendedKey decode (String ws) throws ValidationException + { + byte[] raw = ByteUtils.fromBase58WithChecksum (ws); + byte[] magic = Arrays.copyOf (raw, 3); + byte[] seed; + if ( Arrays.equals (magic, clear16) ) + { + seed = Arrays.copyOfRange (raw, 9, 16 + 9); + } + else if ( Arrays.equals (magic, clear32) ) + { + seed = Arrays.copyOfRange (raw, 9, 32 + 9); + } + else if ( Arrays.equals (magic, clear64) ) + { + seed = Arrays.copyOfRange (raw, 9, 64 + 9); + } + else + { + throw new ValidationException ("Not an encoded HD root"); + } + ExtendedKey key = ExtendedKey.create (seed); + if ( !Arrays.equals (Arrays.copyOf (Hash.hash (key.getMaster ().getPrivate ()), 4), Arrays.copyOfRange (raw, 5, 9)) ) + { + throw new ValidationException ("HD root checksum error"); + } + return key; + } + + public static ExtendedKey decrypt (String ws, String passphrase) throws ValidationException + { + byte[] raw = ByteUtils.fromBase58WithChecksum (ws); + byte[] magic = Arrays.copyOf (raw, 3); + byte[] encryptedSeed; + int N = 1 << 14; + int r = 16; + int p = 16; + if ( Arrays.equals (magic, encrypted16l) ) + { + encryptedSeed = Arrays.copyOfRange (raw, 9, 16 + 9); + r = p = 8; + } + else if ( Arrays.equals (magic, encrypted16m) ) + { + encryptedSeed = Arrays.copyOfRange (raw, 9, 16 + 9); + N = 1 << 16; + } + else if ( Arrays.equals (magic, encrypted16h) ) + { + encryptedSeed = Arrays.copyOfRange (raw, 9, 16 + 9); + N = 1 << 18; + } + else if ( Arrays.equals (magic, encrypted32l) ) + { + encryptedSeed = Arrays.copyOfRange (raw, 9, 32 + 9); + r = p = 8; + } + else if ( Arrays.equals (magic, encrypted32m) ) + { + encryptedSeed = Arrays.copyOfRange (raw, 9, 32 + 9); + N = 1 << 16; + } + else if ( Arrays.equals (magic, encrypted32h) ) + { + encryptedSeed = Arrays.copyOfRange (raw, 9, 32 + 9); + N = 1 << 18; + } + else if ( Arrays.equals (magic, encrypted64l) ) + { + encryptedSeed = Arrays.copyOfRange (raw, 9, 64 + 9); + r = p = 8; + } + else if ( Arrays.equals (magic, encrypted64m) ) + { + encryptedSeed = Arrays.copyOfRange (raw, 9, 64 + 9); + N = 1 << 16; + } + else if ( Arrays.equals (magic, encrypted64h) ) + { + encryptedSeed = Arrays.copyOfRange (raw, 9, 64 + 9); + N = 1 << 18; + } + else + { + throw new ValidationException ("Not an encoded HD root"); + } + byte salt[] = Arrays.copyOf (raw, 9); + + Mac mac; + try + { + mac = Mac.getInstance ("HmacSHA512", "BC"); + SecretKey seedkey = new SecretKeySpec (salt, "HmacSHA512"); + mac.init (seedkey); + byte[] preH = mac.doFinal (passphrase.getBytes ("UTF-8")); + byte[] strongH = SCrypt.generate (preH, preH, N, r, p, 64); + seedkey = new SecretKeySpec (passphrase.getBytes ("UTF-8"), "HmacSHA512"); + mac.init (seedkey); + byte[] postH = mac.doFinal (salt); + byte[] H = SCrypt.generate (postH, strongH, 1 << 10, 1, 1, encryptedSeed.length + 32); + byte[] X = Arrays.copyOf (H, encryptedSeed.length); + SecretKeySpec keyspec = new SecretKeySpec (Arrays.copyOfRange (H, encryptedSeed.length, encryptedSeed.length + 32), "AES"); + Cipher cipher = Cipher.getInstance ("AES/ECB/NoPadding", "BC"); + cipher.init (Cipher.DECRYPT_MODE, keyspec); + byte[] seed = cipher.doFinal (encryptedSeed); + for ( int i = 0; i < encryptedSeed.length; ++i ) + { + seed[i] ^= X[i]; + } + ExtendedKey key = ExtendedKey.create (seed); + if ( !Arrays.equals (Arrays.copyOf (Hash.hash (key.getMaster ().getPrivate ()), 4), Arrays.copyOfRange (raw, 5, 9)) ) + { + throw new ValidationException ("HD root checksum error"); + } + return key; + } + catch ( NoSuchAlgorithmException | NoSuchProviderException | InvalidKeyException | UnsupportedEncodingException | NoSuchPaddingException + | IllegalBlockSizeException | BadPaddingException e ) + { + throw new ValidationException (e); + } + } + + public static String encode (byte[] seed, Date birth) throws ValidationException + { + if ( seed == null || (seed.length != 16 && seed.length != 32 && seed.length != 64) ) + { + throw new ValidationException ("Seed must be 16, 32 or 64 bytes"); + } + int weeks = + (int) ((birth.getTime () - new GregorianCalendar (2013, Calendar.JANUARY, 1).getTime ().getTime ()) / (7 * 24 * 60 * 60 * 1000L)); + + ExtendedKey key = ExtendedKey.create (seed); + byte raw[]; + if ( seed.length == 16 ) + { + raw = new byte[25]; + System.arraycopy (clear16, 0, raw, 0, 3); + } + else if ( seed.length == 32 ) + { + raw = new byte[41]; + System.arraycopy (clear32, 0, raw, 0, 3); + } + else + { + raw = new byte[73]; + System.arraycopy (clear64, 0, raw, 0, 3); + } + raw[3] = (byte) (weeks & 0xff); + raw[4] = (byte) ((weeks >>> 8) & 0xff); + System.arraycopy (Hash.hash (key.getMaster ().getPrivate ()), 0, raw, 5, 4); + System.arraycopy (seed, 0, raw, 9, seed.length); + return ByteUtils.toBase58WithChecksum (raw); + } + + public static String encrypt (byte[] seed, Date birth, String passphrase, ScryptDifficulty scryptDifficulty) throws ValidationException + { + if ( seed == null || (seed.length != 16 && seed.length != 32 && seed.length != 64) ) + { + throw new ValidationException ("Seed must be 16, 32 or 64 bytes"); + } + int weeks = + (int) ((birth.getTime () - new GregorianCalendar (2013, Calendar.JANUARY, 1).getTime ().getTime ()) / (7 * 24 * 60 * 60 * 1000L)); + + ExtendedKey key = ExtendedKey.create (seed); + byte raw[]; + byte[] salt = new byte[9]; + if ( seed.length == 16 ) + { + raw = new byte[25]; + System.arraycopy (encrypted16, 0, salt, 0, 3); + } + else if ( seed.length == 32 ) + { + raw = new byte[41]; + System.arraycopy (encrypted32, 0, salt, 0, 3); + } + else + { + raw = new byte[73]; + System.arraycopy (encrypted64, 0, salt, 0, 3); + } + salt[2] += scryptDifficulty.ordinal (); + salt[3] = (byte) (weeks & 0xff); + salt[4] = (byte) ((weeks >>> 8) & 0xff); + System.arraycopy (Hash.hash (key.getMaster ().getPrivate ()), 0, salt, 5, 4); + System.arraycopy (salt, 0, raw, 0, 9); + int N = (1 << 14) << (scryptDifficulty.ordinal () * 2); + int r = scryptDifficulty == ScryptDifficulty.LOW ? 8 : 16; + int p = scryptDifficulty == ScryptDifficulty.LOW ? 8 : 16; + Mac mac; + try + { + mac = Mac.getInstance ("HmacSHA512", "BC"); + SecretKey seedkey = new SecretKeySpec (salt, "HmacSHA512"); + mac.init (seedkey); + byte[] preH = mac.doFinal (passphrase.getBytes ("UTF-8")); + byte[] strongH = SCrypt.generate (preH, preH, N, r, p, 64); + seedkey = new SecretKeySpec (passphrase.getBytes ("UTF-8"), "HmacSHA512"); + mac.init (seedkey); + byte[] postH = mac.doFinal (salt); + byte[] H = SCrypt.generate (postH, strongH, 1 << 10, 1, 1, seed.length + 32); + byte[] X = Arrays.copyOf (H, seed.length); + for ( int i = 0; i < seed.length; ++i ) + { + X[i] ^= seed[i]; + } + SecretKeySpec keyspec = new SecretKeySpec (Arrays.copyOfRange (H, seed.length, seed.length + 32), "AES"); + Cipher cipher = Cipher.getInstance ("AES/ECB/NoPadding", "BC"); + cipher.init (Cipher.ENCRYPT_MODE, keyspec); + System.arraycopy (cipher.doFinal (X), 0, raw, 9, seed.length); + } + catch ( NoSuchAlgorithmException | NoSuchProviderException | InvalidKeyException | UnsupportedEncodingException | NoSuchPaddingException + | IllegalBlockSizeException | BadPaddingException e ) + { + throw new ValidationException (e); + } + return ByteUtils.toBase58WithChecksum (raw); + } +} diff --git a/api/src/test/java/com/bitsofproof/supernode/api/EncryptedHDRootTest.java b/api/src/test/java/com/bitsofproof/supernode/api/EncryptedHDRootTest.java new file mode 100644 index 00000000..4846c62a --- /dev/null +++ b/api/src/test/java/com/bitsofproof/supernode/api/EncryptedHDRootTest.java @@ -0,0 +1,85 @@ +package com.bitsofproof.supernode.api; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.io.InputStream; +import java.security.Security; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.ExtendedKey; +import com.bitsofproof.supernode.common.ValidationException; +import com.bitsofproof.supernode.wallet.EncryptedHDRoot; +import com.bitsofproof.supernode.wallet.EncryptedHDRoot.ScryptDifficulty; + +public class EncryptedHDRootTest +{ + @BeforeClass + public static void init () + { + Security.addProvider (new BouncyCastleProvider ()); + } + + private static final String TESTS = "EncryptedHDRoot.json"; + + private JSONArray readArray (String resource) throws IOException, JSONException + { + InputStream input = this.getClass ().getResource ("/" + resource).openStream (); + StringBuffer content = new StringBuffer (); + byte[] buffer = new byte[1024]; + int len; + while ( (len = input.read (buffer)) > 0 ) + { + byte[] s = new byte[len]; + System.arraycopy (buffer, 0, s, 0, len); + content.append (new String (buffer, "UTF-8")); + } + return new JSONArray (content.toString ()); + } + + /** + * @throws IOException + * @throws JSONException + * @throws ValidationException + * @throws ParseException + */ + @Test + public void testHDRoot () throws IOException, JSONException, ValidationException, ParseException + { + DateFormat dateFormat = new SimpleDateFormat ("dd-MM-yyyy"); + JSONArray testData = readArray (TESTS); + for ( int i = 0; i < testData.length (); ++i ) + { + JSONObject test = testData.getJSONObject (i); + byte[] seed = ByteUtils.fromHex (test.getString ("seed")); + Date birth = dateFormat.parse (test.getString ("birth")); + ExtendedKey key = ExtendedKey.create (seed); + assertTrue (key.getMaster ().getAddress ().toString ().equals (test.getString ("address"))); + assertTrue (key.serialize (true).equals (test.getString ("private"))); + assertTrue (key.getReadOnly ().serialize (true).equals (test.getString ("public"))); + assertTrue (EncryptedHDRoot.encode (seed, birth).equals (test.getString ("clear"))); + assertTrue (EncryptedHDRoot.encrypt (seed, birth, test.getString ("password"), ScryptDifficulty.LOW).equals (test.getString ("encryptedLow"))); + assertTrue (EncryptedHDRoot.encrypt (seed, birth, test.getString ("password"), ScryptDifficulty.MEDIUM).equals (test.getString + ("encryptedMedium"))); + assertTrue (EncryptedHDRoot.encrypt (seed, birth, test.getString ("password"), ScryptDifficulty.HIGH).equals (test.getString ("encryptedHigh"))); + assertTrue (EncryptedHDRoot.decode (test.getString ("clear")).serialize (true).equals (key.serialize (true))); + assertTrue (EncryptedHDRoot.decrypt (test.getString ("encryptedLow"), test.getString ("password")).serialize (true).equals (key.serialize + (true))); + assertTrue (EncryptedHDRoot.decrypt (test.getString ("encryptedMedium"), test.getString ("password")).serialize (true) + .equals (key.serialize (true))); + assertTrue (EncryptedHDRoot.decrypt (test.getString ("encryptedHigh"), test.getString ("password")).serialize (true) + .equals (key.serialize (true))); + } + } +} diff --git a/api/src/test/resources/EncryptedHDRoot.json b/api/src/test/resources/EncryptedHDRoot.json new file mode 100644 index 00000000..a6868d9a --- /dev/null +++ b/api/src/test/resources/EncryptedHDRoot.json @@ -0,0 +1,50 @@ +[ + { + "seed": "000102030405060708090a0b0c0d0e0f", + "birth": "24-12-2013", + "clear": "WS1A3xqGKyHq4XMvq2Qj8xBu135p7AhSVSY4fqN", + "password": "Satoshi", + "encryptedLow": "ws19GjxzzP3vEBNp9jCcW3a7L8j3GQpdCkxBguL", + "encryptedMedium": "ws1H7QHFD9SKtqPEYhwG4Ty1TJrmY9PpeswoDLZ", + "encryptedHigh": "ws1Qx4bVRupdZyw9WvAfMAsiGrek42PPTJ3UeVo", + "address":"15mKKb2eos1hWa6tisdPwwDC1a5J1y9nma", + "private": "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", + "public": "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8" + }, + { + "seed": "7f0ad7d595be13e6fe4cf1fa0fbb6ae9c26c5d9b09920709414982b6363d5844", + "birth": "24-12-2013", + "clear": "WS13D1hB7v7XUYz9YH1u4UHab52MbmDUQSczJ7xavcJYkYiKvWnWa8CV87v1n", + "password": "Nakamoto", + "encryptedLow": "ws122WLGmSa7RPS6NSxWbSCbPGRsDigg8KuVNDtwPysozeJPTTQsjc6HV5FDV", + "encryptedMedium": "ws16JK3uHmQvTvJPxzTV7RJgXnsevMiyYUa443zi8jTpDDQAmRYRynf1V332F", + "encryptedHigh": "ws1Aa7mXp6FjxAxWbLbkvXpzj3ogwxo2veR6qs5jiA2CPt646dbdsedR9gZ3m", + "address":"1A54ECavJaJAoLGqqNrPd9Y3cvSvkL2Roz", + "private": "xprv9s21ZrQH143K3f9hMVvcbY4EX4CfxsEtc6C5BMkZtgGpTGpxAscoq7SLSAcL6k5dxaZ9s4SChrtfSFoKpijuwAnhuPn76eva6W8bDr118t3", + "public": "xpub661MyMwAqRbcG9EATXTcxfzy563ANKxjyK7fykABT1ooL5A6iQw4NukpHShDxYgeso4NHscFmqcVEtdUt61c8RCf7FqXK9z6sgfkQvYBQPP" + }, + { + "seed": "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", + "birth": "24-12-2013", + "clear": "WS1k2JRUKEaUCkqtiVcHHx5JBRiwCxU7LazRh2hREFsFkSrMqmLbEHuGb4DZXzr8Ro4WcVaJBZHesA5PyZuMnPxW1UCc1VK4gMwVfS7x", + "password": "Vires In Numeris", + "encryptedLow": "ws2AxUpnu8p8sboukkc1PRJrp1pYNNAnvn4A5ZbySxoJKxWy4L12vPJM8nyF2Jd3f6eanSqEij1mZyLhG9oW5G2DCLseRX5ucAohsz6d", + "encryptedMedium": "ws3Sa4zAotc76fFq3fKmaC8ALVDPPghYnhxiRFn8UuYHjCFND96Tp3ipW6ehA4YWQjTdiUVPpLtm62cpUy9EdVjsjmwZGfhbebPCiQvT", + "encryptedHigh": "ws4iBf9YieQDJVMSpeNkvnhy9MsVac3obtFyBtPhiUsviFhG29S7UhCLmXo52s7rHPx3W4v9HgT9d6rVYNGQJg1zGroHY1NnFjZ85UMH", + "address":"1JEoxevbLLG8cVqeoGKQiAwoWbNYSUyYjg", + "private": "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", + "public": "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB" + }, + { + "seed": "6ca4a27ac660c683340f59353b1375a9", + "birth": "24-12-2013", + "clear": "WS1A3y1yzdgNW2BgegrtNNV57EsaMdEvYqdyWFs", + "password": "聡中本", + "encryptedLow": "ws19Gk9if3SS37cqedG3dTSNgRPRSH9cf6qxarB", + "encryptedMedium": "ws1H7QTxsopohhHTPE3RXAhWpFUWW9kEKvrhUq3", + "encryptedHigh": "ws1Qx4nD6aDA4E29tMF6o8AsZgqmPpSyxskQMFn", + "address":"1JVncPbsdB2s4zHim3VdAWNkZ8JANBZ1U9", + "private": "xprv9s21ZrQH143K3mJ4upPSDfXdA34yNjem6PSsXT63vm8dq8ikUJv4iiTD3PrSKtdGZXFVD689z5T7knXo55BjcHS2WL3Syp2DbGgnbgxw2QA", + "public": "xpub661MyMwAqRbcGFNY1qvSaoUMi4uTnCNcTcNUKqVfV6fchw3u1rEKGWmgtfUMRKLgUHNZ7dfsh8Ys6SLwUojZqScFBQL3dFGF3QywNLJVZ2o" + } +] \ No newline at end of file From 077260e36cfe044a78df76780b306274e1dad59a Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Fri, 27 Dec 2013 18:51:58 +0100 Subject: [PATCH 273/299] Travis can not run scrypt with high parameters --- .../supernode/wallet/EncryptedHDRoot.java | 2 +- .../supernode/api/EncryptedHDRootTest.java | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/wallet/EncryptedHDRoot.java b/api/src/main/java/com/bitsofproof/supernode/wallet/EncryptedHDRoot.java index 2143d5ad..85a8796e 100644 --- a/api/src/main/java/com/bitsofproof/supernode/wallet/EncryptedHDRoot.java +++ b/api/src/main/java/com/bitsofproof/supernode/wallet/EncryptedHDRoot.java @@ -65,7 +65,7 @@ public static enum ScryptDifficulty private static final byte[] encrypted64m = { 0x02, 0x38, 0x05 }; private static final byte[] encrypted64h = { 0x02, 0x38, 0x06 }; - public Date decodeBirthDate (String ws) throws ValidationException + public static Date decodeBirthDate (String ws) throws ValidationException { byte[] raw = ByteUtils.fromBase58WithChecksum (ws); int weeks = raw[3] + raw[4] << 8; diff --git a/api/src/test/java/com/bitsofproof/supernode/api/EncryptedHDRootTest.java b/api/src/test/java/com/bitsofproof/supernode/api/EncryptedHDRootTest.java index 4846c62a..bba6221e 100644 --- a/api/src/test/java/com/bitsofproof/supernode/api/EncryptedHDRootTest.java +++ b/api/src/test/java/com/bitsofproof/supernode/api/EncryptedHDRootTest.java @@ -70,16 +70,18 @@ public void testHDRoot () throws IOException, JSONException, ValidationException assertTrue (key.getReadOnly ().serialize (true).equals (test.getString ("public"))); assertTrue (EncryptedHDRoot.encode (seed, birth).equals (test.getString ("clear"))); assertTrue (EncryptedHDRoot.encrypt (seed, birth, test.getString ("password"), ScryptDifficulty.LOW).equals (test.getString ("encryptedLow"))); - assertTrue (EncryptedHDRoot.encrypt (seed, birth, test.getString ("password"), ScryptDifficulty.MEDIUM).equals (test.getString - ("encryptedMedium"))); - assertTrue (EncryptedHDRoot.encrypt (seed, birth, test.getString ("password"), ScryptDifficulty.HIGH).equals (test.getString ("encryptedHigh"))); + // Travis can not run these as they need lots of memory. Uncomment if curious + // assertTrue (EncryptedHDRoot.encrypt (seed, birth, test.getString ("password"), ScryptDifficulty.MEDIUM).equals (test.getString + // ("encryptedMedium"))); + // assertTrue (EncryptedHDRoot.encrypt (seed, birth, test.getString ("password"), ScryptDifficulty.HIGH).equals (test.getString ("encryptedHigh"))); assertTrue (EncryptedHDRoot.decode (test.getString ("clear")).serialize (true).equals (key.serialize (true))); assertTrue (EncryptedHDRoot.decrypt (test.getString ("encryptedLow"), test.getString ("password")).serialize (true).equals (key.serialize (true))); - assertTrue (EncryptedHDRoot.decrypt (test.getString ("encryptedMedium"), test.getString ("password")).serialize (true) - .equals (key.serialize (true))); - assertTrue (EncryptedHDRoot.decrypt (test.getString ("encryptedHigh"), test.getString ("password")).serialize (true) - .equals (key.serialize (true))); + // Travis can not run these as they need lots of memory. Uncomment if curious + // assertTrue (EncryptedHDRoot.decrypt (test.getString ("encryptedMedium"), test.getString ("password")).serialize (true) + // .equals (key.serialize (true))); + // assertTrue (EncryptedHDRoot.decrypt (test.getString ("encryptedHigh"), test.getString ("password")).serialize (true) + // .equals (key.serialize (true))); } } } From b084800dd32f0ffe75ab997b52d392661562e0a2 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sun, 29 Dec 2013 19:26:53 +0100 Subject: [PATCH 274/299] add context otherwise preference between leveldb and memdb is operating system dependent. --- server/src/main/resources/context/memdb-profile.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/resources/context/memdb-profile.xml b/server/src/main/resources/context/memdb-profile.xml index 25f53145..e16b187f 100644 --- a/server/src/main/resources/context/memdb-profile.xml +++ b/server/src/main/resources/context/memdb-profile.xml @@ -1,5 +1,5 @@ - Date: Sat, 4 Jan 2014 16:18:52 +0100 Subject: [PATCH 275/299] upgraded poms. upgraded to leveldb 1.8 --- api/pom.xml | 2 +- pom.xml | 81 ++------------------------------------------------ server/pom.xml | 63 ++++++++++++++++++++++++++++++++------- 3 files changed, 56 insertions(+), 90 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 2d1d35fc..8b815ef4 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -17,7 +17,7 @@ com.google.protobuf.tools maven-protoc-plugin - 0.1.10 + 0.1.10 diff --git a/pom.xml b/pom.xml index 21f2d995..8c5ae67c 100644 --- a/pom.xml +++ b/pom.xml @@ -45,20 +45,12 @@ UTF-8 + UTF-8 3.1.2.RELEASE - 1.16-bop-patch - 1.0 1.6.6 1.7 1.48 - 1.1.2.RELEASE - 2.9.0 - 10.9.1.0 - 3.6.9.Final - 9.1-901.jdbc4 - 1.6.1 2.4.1 - 4.3.0.Final 20090211 1.2 @@ -96,37 +88,13 @@ maven-compiler-plugin - 2.3.2 + 3.1 ${java.compiler.version} ${java.compiler.version} + UTF-8 - - org.apache.maven.plugins - maven-dependency-plugin - 2.5.1 - - - copy-dependencies - package - - copy-dependencies - - - ${project.build.directory}/lib - false - false - true - runtime - - - - - - org.apache.maven.plugins - maven-source-plugin - org.apache.maven.plugins maven-release-plugin @@ -136,49 +104,6 @@ - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - org.apache.maven.plugins - maven-dependency-plugin - [2.5.1,) - - copy-dependencies - - - - - - - - - - - - org.apache.maven.plugins - maven-source-plugin - 2.2.1 - - - attach-sources - - jar - - - - - - org.apache.maven.wagon diff --git a/server/pom.xml b/server/pom.xml index bc356080..bff002f5 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -11,6 +11,11 @@ 1.3.7-SNAPSHOT ../ + + com.bitsofproof.supernode.main.Main + 5.8.0 + 1.8 + @@ -61,18 +66,11 @@ geronimo-jms_1.1_spec 1.1 - org.apache.activemq activemq-client - 5.8.0 + ${activemq.version} - - - org.fusesource.stompjms - stompjms-client - ${stomp.version} - org.json json @@ -98,14 +96,13 @@ + org.apache.maven.plugins maven-jar-plugin 2.4 - true - com.bitsofproof.supernode.main.Main - lib/ + true @@ -123,6 +120,50 @@ + + org.apache.maven.plugins + maven-shade-plugin + 2.1 + + + package + + shade + + + true + shaded + + + + + lib-signed/bcprov-jdk15on.jar + ${project.mainclass} + + + + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + org.bouncycastle:bcprov-jdk15on + + + + + + From 0cd6a5db3ef56beb80d2aba833587b81392e0cb9 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 4 Jan 2014 18:26:29 +0100 Subject: [PATCH 276/299] Move to bouncy castle 1.50 and use RFC6979 deterministic ECDSA signatures --- .../supernode/common/ECKeyPair.java | 51 +++++---------- .../supernode/api/RFC6979Test.java | 62 +++++++++++++++++++ api/src/test/resources/RFC6979.json | 22 +++++++ pom.xml | 2 +- .../com/bitsofproof/supernode/main/Main.java | 2 +- 5 files changed, 103 insertions(+), 36 deletions(-) create mode 100644 api/src/test/java/com/bitsofproof/supernode/api/RFC6979Test.java create mode 100644 api/src/test/resources/RFC6979.json diff --git a/api/src/main/java/com/bitsofproof/supernode/common/ECKeyPair.java b/api/src/main/java/com/bitsofproof/supernode/common/ECKeyPair.java index 58885bc4..ad2b59ca 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/ECKeyPair.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/ECKeyPair.java @@ -21,19 +21,20 @@ import java.security.SecureRandom; import org.bouncycastle.asn1.ASN1InputStream; -import org.bouncycastle.asn1.DERInteger; +import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.DERSequenceGenerator; import org.bouncycastle.asn1.DLSequence; import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.generators.ECKeyPairGenerator; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECKeyGenerationParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.crypto.signers.ECDSASigner; -import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.crypto.signers.HMacDSAKCalculator; import org.bouncycastle.util.Arrays; import com.bitsofproof.supernode.api.Address; @@ -79,15 +80,7 @@ public static ECKeyPair createNew (boolean compressed) ECKeyPair k = new ECKeyPair (); k.priv = privParams.getD (); k.compressed = compressed; - if ( compressed ) - { - ECPoint q = pubParams.getQ (); - k.pub = new ECPoint.Fp (domain.getCurve (), q.getX (), q.getY (), true).getEncoded (); - } - else - { - k.pub = pubParams.getQ ().getEncoded (); - } + k.pub = pubParams.getQ ().getEncoded (compressed); return k; } @@ -144,30 +137,14 @@ public ECKeyPair (byte[] p, boolean compressed) throws ValidationException } this.priv = new BigInteger (1, p).mod (curve.getN ()); this.compressed = compressed; - if ( compressed ) - { - ECPoint q = curve.getG ().multiply (priv); - pub = new ECPoint.Fp (domain.getCurve (), q.getX (), q.getY (), true).getEncoded (); - } - else - { - pub = curve.getG ().multiply (priv).getEncoded (); - } + pub = curve.getG ().multiply (priv).getEncoded (compressed); } public ECKeyPair (BigInteger priv, boolean compressed) { this.priv = priv; this.compressed = compressed; - if ( compressed ) - { - ECPoint q = curve.getG ().multiply (priv); - pub = new ECPoint.Fp (domain.getCurve (), q.getX (), q.getY (), true).getEncoded (); - } - else - { - pub = curve.getG ().multiply (priv).getEncoded (); - } + pub = curve.getG ().multiply (priv).getEncoded (compressed); } @Override @@ -177,15 +154,15 @@ public byte[] sign (byte[] hash) throws ValidationException { throw new ValidationException ("Need private key to sign"); } - ECDSASigner signer = new ECDSASigner (); + ECDSASigner signer = new ECDSASigner (new HMacDSAKCalculator (new SHA256Digest ())); signer.init (true, new ECPrivateKeyParameters (priv, domain)); BigInteger[] signature = signer.generateSignature (hash); ByteArrayOutputStream s = new ByteArrayOutputStream (); try { DERSequenceGenerator seq = new DERSequenceGenerator (s); - seq.addObject (new DERInteger (signature[0])); - seq.addObject (new DERInteger (signature[1])); + seq.addObject (new ASN1Integer (signature[0])); + seq.addObject (new ASN1Integer (signature[1])); seq.close (); return s.toByteArray (); } @@ -210,8 +187,8 @@ public static boolean verify (byte[] hash, byte[] signature, byte[] pub) signer.init (false, new ECPublicKeyParameters (curve.getCurve ().decodePoint (pub), domain)); DLSequence seq = (DLSequence) asn1.readObject (); - BigInteger r = ((DERInteger) seq.getObjectAt (0)).getPositiveValue (); - BigInteger s = ((DERInteger) seq.getObjectAt (1)).getPositiveValue (); + BigInteger r = ((ASN1Integer) seq.getObjectAt (0)).getPositiveValue (); + BigInteger s = ((ASN1Integer) seq.getObjectAt (1)).getPositiveValue (); return signer.verifySignature (hash, r, s); } catch ( Exception e ) @@ -231,6 +208,12 @@ public static boolean verify (byte[] hash, byte[] signature, byte[] pub) } } + @Override + public String toString () + { + return serializeWIF (this); + } + public static String serializeWIF (Key key) { return ByteUtils.toBase58 (bytesWIF (key)); diff --git a/api/src/test/java/com/bitsofproof/supernode/api/RFC6979Test.java b/api/src/test/java/com/bitsofproof/supernode/api/RFC6979Test.java new file mode 100644 index 00000000..22fe3c36 --- /dev/null +++ b/api/src/test/java/com/bitsofproof/supernode/api/RFC6979Test.java @@ -0,0 +1,62 @@ +package com.bitsofproof.supernode.api; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.io.InputStream; +import java.security.Security; +import java.util.Arrays; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.bitsofproof.supernode.common.ByteUtils; +import com.bitsofproof.supernode.common.ECKeyPair; +import com.bitsofproof.supernode.common.ValidationException; + +public class RFC6979Test +{ + static BouncyCastleProvider provider; + + @BeforeClass + public static void init () + { + Security.addProvider (provider = new BouncyCastleProvider ()); + } + + private static final String TESTS = "RFC6979.json"; + + private JSONArray readArray (String resource) throws IOException, JSONException + { + InputStream input = this.getClass ().getResource ("/" + resource).openStream (); + StringBuffer content = new StringBuffer (); + byte[] buffer = new byte[1024]; + int len; + while ( (len = input.read (buffer)) > 0 ) + { + byte[] s = new byte[len]; + System.arraycopy (buffer, 0, s, 0, len); + content.append (new String (buffer, "UTF-8")); + } + return new JSONArray (content.toString ()); + } + + @Test + public void rfc6979 () throws IOException, JSONException, ValidationException + { + JSONArray tests = readArray (TESTS); + for ( int i = 0; i < tests.length (); ++i ) + { + JSONObject test = tests.getJSONObject (i); + ECKeyPair key = ECKeyPair.parseWIF (test.getString ("key")); + byte[] message = test.getString ("message").getBytes (); + byte[] expectedSignature = ByteUtils.fromHex (test.getString ("expectedSignature")); + byte[] signature = key.sign (message); + assertTrue (Arrays.equals (expectedSignature, signature)); + } + } +} diff --git a/api/src/test/resources/RFC6979.json b/api/src/test/resources/RFC6979.json new file mode 100644 index 00000000..9aaf403d --- /dev/null +++ b/api/src/test/resources/RFC6979.json @@ -0,0 +1,22 @@ +[ + { + "key": "L24GFtCcK6E8dUNGkRK7iCUcfi3JJmVQq8NPG9LzcXS594YkuFoK", + "message":"Satoshi Nakamoto", + "expectedSignature":"304502202eb53dd34d8c0014371ddf3c57b60cbdde5c525abd6384b6453192fac9ec180a022100fe5b29c65d400d7e48a0f9e3e450069fa87400288e78e1e9932ad44f6c03f23f" + }, + { + "key": "L1MuyFhSGEJYXZWHRLt2Ggnou5BzCMy7165eFTB2qPPU93B4fRjV", + "message":"The Times 03/Jan/2009 Chancellor on brink of second bailout for banks", + "expectedSignature":"30460221009d4adeca74eaea3a81f0c55c54478f6d5c652196b4d7c02d69bbf30504d9e95b02210080bc27ba00a4c75919b477b2a61d56de851a521d2ff6782b631c542e76e7d346" + }, + { + "key": "KxiYEU4ti9G4pLb832rrjGnkvHpAt8Dqx7ZBq3NA66pQN3hpRFSx", + "message":"Satoshi Nakamoto", + "expectedSignature":"304402203db5f02f6d96090852a31d16e44077a4ee879402a01bb9019238ec5269b976010220558817ff1534dc47f21b766fc014369158e4a471ae42b986eaaf659f81abe3ee" + }, + { + "key": "L3wadShuDYpTR1sHVsbPSE695YX1Fh5wfDvE4ztZtyyYjidSUJmX", + "message":"The Times 03/Jan/2009 Chancellor on brink of second bailout for banks", + "expectedSignature":"30450220290a41120f7d06d86d4d6914ea718d91111e58700b28072a951e59a2a8b1f983022100dee6880df00d72bc847929219a85d4edad83bd2ac45907371af920f3cac20bd7" + } +] \ No newline at end of file diff --git a/pom.xml b/pom.xml index 8c5ae67c..994ad889 100644 --- a/pom.xml +++ b/pom.xml @@ -49,7 +49,7 @@ 3.1.2.RELEASE 1.6.6 1.7 - 1.48 + 1.50 2.4.1 20090211 1.2 diff --git a/server/src/main/java/com/bitsofproof/supernode/main/Main.java b/server/src/main/java/com/bitsofproof/supernode/main/Main.java index f41a610e..7b78354f 100644 --- a/server/src/main/java/com/bitsofproof/supernode/main/Main.java +++ b/server/src/main/java/com/bitsofproof/supernode/main/Main.java @@ -35,7 +35,7 @@ protected interface App public static void main (String[] args) throws Exception { - log.info ("bitsofproof supernode (c) 2013 bits of proof zrt."); + log.info ("bitsofproof supernode (c) 2013-2014 bits of proof zrt."); Security.addProvider (new BouncyCastleProvider ()); log.trace ("Spring context setup"); From 34caf26178c4ff16eb8e3fa58c162d43d51b7194 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 4 Jan 2014 21:00:53 +0100 Subject: [PATCH 277/299] upgrade activemq to 5.9.0 --- server/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/pom.xml b/server/pom.xml index bff002f5..1681d027 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -13,7 +13,7 @@ com.bitsofproof.supernode.main.Main - 5.8.0 + 5.9.0 1.8 From 3c31bc144b63e2162ecb9cb9345a377c650d5e8c Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 4 Jan 2014 21:28:09 +0100 Subject: [PATCH 278/299] add BOP Enterprise Bitcoin Server --- README.md | 38 ++++++++++--------- pom.xml | 2 +- .../com/bitsofproof/supernode/main/Main.java | 4 +- server/src/main/resources/context/server.xml | 10 +++++ 4 files changed, 33 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 01ce0dc0..6f732ffc 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,15 @@ BOP Community Bitcoin Server ============================ -This software enables your application to send/receive Bitcoin payments or to data mine the network's transaction history, with ease. +This software enables your application to send/receive Bitcoin payments. You may run it stand-alone utilizing its own validation engine or as a slave behind the Satoshi client so it accepts exactly what the 'reference client' does. In either case it builds a fully indexed block chain of transactions stored in LevelDB. -The server process handles peer-to-peer communication with the network and serves clients connected to it through a message bus. Wallet(s) are implemented by the client library and transactions are also signed at the client side, therefore it is safe to operate the Server in a remote environment as it does not store or receive private keys. +The server process handles peer-to-peer communication with the network and serves clients connected to it through a message bus. Wallet(s) are implemented by the client library and transactions are also signed at the client side. Private keys, that is the control of Bitcoins always remain at the client side. -Bits of Proof offers professionally hosted server instances for commercial use, that share and build on this code base: +The BOP Community Bitcoin Server is a fully functional demonstration of BOP technology, that we use to build commercial applications such as wallets and exchanges. -* BOP Enterprise Bitcoin Server - for very high volume multiple and hierarchical wallet support. Don't despair with the 'reference client's geeky RPC calls design and poor performance. Use our intuitive, business friendly, high performance API and spend rather to develop your own business case. - -* BopShop - Payment processor to accept Bitcoin on a website or in Person. Available both hosted by BOP or with white label to start your own Bitcoin payment processor business. - -* BeBop - Simple but powerful mobile wallet best integrated with BopShop. - -_Use our ready to go hosted BOP Server instance! Get an evaluation access, attractive pricing based on resources used or transactions processed from sales@bitsofproof.com_ +The BOP Enterprise Bitcoin Server supports the same API but has significantly improved performance e.g. in processing wallets using a large number of addresses. BOP offers professionally hosted Enterprise Bitcoin Servers, access to its latest Enterprise Server source code and support contracts. Please contact sales@bitsofproof.com for an offer. Build the Community Server -------------------------- @@ -27,20 +21,29 @@ Make sure you have Maven3, JDK 7 (with JCE Unlimited Strength Policy Jurisdictio mvn package +Let us assume the directory you would want to run the server is /home/bitsofproof/run. + + cp server/target/bitsofproof-server-version-shaded.jar /home/bitsofproof/run + cp server/src/main/resources/context/*.xml /home/bitsofproof/run + cp server/src/main/resources/log4j.properties /home/bitsofproof/run + mkdir /home/bitsofproof/run/signed-libs + +Download http://www.bouncycastle.org/download/bcprov-jdk15on-150.jar + cp bcprov-jdk15on-150.jar /home/bitsofproof/run/signed-libs/bcprov-jdk15on.jar + Run --- + cd /home/bitsofproof/run + java -server -Xmx4g -jar target/server/target/bitsofproof-server-version-shaded.jar testnet3 memdb -java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.2.0.jar testnet3 memdb - -The final two parameters of the above example command line identify configuration contexts stored under server/src/main/resources/context. You have to choose one of the networks by specifying either testnet3 or production or slave, and a database layer, that could be (examples): +The final two parameters of the above example command line identify configuration contexts you copied under *-profile.xml. You have to choose one of the networks by specifying either testnet3 or production or slave, and a database layer, that could be (examples): * memdb - in memory database for tests * leveldb - LevelDB -To use the API of your local server you need to run a message broker process providing the infrastructure. Since the message bus offers authentication and a wide selection of transports, your installation will likely be unique and need to be reflected in server/src/main/resources/context/BCSAPI-profile.xml. You find example configurations for the message broker Apollo and Active MQ there. The complete command line for a production environment might be: - -java -server -Xmx2g -jar target/server/target/bitsofproof-server-1.2.0.jar production leveldb BCSAPI apollo +To use the API of your local server you need to run a message broker process providing the infrastructure. Since the message bus offers authentication and a wide selection of transports, your installation will likely be unique and need to be reflected in BCSAPI-profile.xml. You find example configurations for the message Active MQ there. The complete command line for a production environment might be: + java -server -Xmx4g -jar target/server/target/bitsofproof-server-version-shaded.jar production leveldb BCSAPI activemq License ------- @@ -48,8 +51,7 @@ Apache License, Version 2.0. See LICENSE file. Donations --------- -In case you do not require professional services of Bits of Proof, but would like to honor its contribution to the Bitcoin community, please donate to: +Please honor Bits of Proof's contribution to the Bitcoin community, and donate to: 1EuamejAs2Lcz1ZPNrEhLsFTLnEY29BYKU -Donations will finance social events of Bits of Proof developer. diff --git a/pom.xml b/pom.xml index 994ad889..a9ae300f 100644 --- a/pom.xml +++ b/pom.xml @@ -46,7 +46,7 @@ UTF-8 UTF-8 - 3.1.2.RELEASE + 4.0.0.RELEASE 1.6.6 1.7 1.50 diff --git a/server/src/main/java/com/bitsofproof/supernode/main/Main.java b/server/src/main/java/com/bitsofproof/supernode/main/Main.java index 7b78354f..812db8c9 100644 --- a/server/src/main/java/com/bitsofproof/supernode/main/Main.java +++ b/server/src/main/java/com/bitsofproof/supernode/main/Main.java @@ -69,8 +69,8 @@ public static void main (String[] args) throws Exception } } } - ctx.load ("classpath:context/server.xml"); - ctx.load ("classpath:context/*-profile.xml"); + ctx.load ("file:server.xml"); + ctx.load ("file:*-profile.xml"); ctx.refresh (); ctx.getBean (App.class).start (a.toArray (new String[0])); } diff --git a/server/src/main/resources/context/server.xml b/server/src/main/resources/context/server.xml index 1f800021..84fa599a 100644 --- a/server/src/main/resources/context/server.xml +++ b/server/src/main/resources/context/server.xml @@ -9,6 +9,16 @@ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> + + + + + + log4j.properties + + + From 3ca7b07a68ee62896bc76c9a2037597ab45e16e7 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 4 Jan 2014 21:33:19 +0100 Subject: [PATCH 279/299] [maven-release-plugin] prepare release 2.0.0 --- api/pom.xml | 2 +- pom.xml | 4 ++-- server/pom.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 8b815ef4..86ece47a 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 1.3.7-SNAPSHOT + 2.0.0 ../ diff --git a/pom.xml b/pom.xml index a9ae300f..f2e7dc26 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.bitsofproof supernode - 1.3.7-SNAPSHOT + 2.0.0 supernode The bitsofproof supernode implementation of the Bitcoin protocol pom @@ -70,7 +70,7 @@ scm:git:git@github.com:bitsofproof/supernode.git scm:git:git@github.com:bitsofproof/supernode.git https://github.com/bitsofproof/supernode - HEAD + 2.0.0 diff --git a/server/pom.xml b/server/pom.xml index 1681d027..039233d3 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 1.3.7-SNAPSHOT + 2.0.0 ../ From a7109b15823e415b446b66dd6e630c46ec33a0c1 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 4 Jan 2014 21:33:25 +0100 Subject: [PATCH 280/299] [maven-release-plugin] prepare for next development iteration --- api/pom.xml | 2 +- pom.xml | 4 ++-- server/pom.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 86ece47a..3756f9d5 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 2.0.0 + 2.0.1-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index f2e7dc26..3bc4a4d9 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.bitsofproof supernode - 2.0.0 + 2.0.1-SNAPSHOT supernode The bitsofproof supernode implementation of the Bitcoin protocol pom @@ -70,7 +70,7 @@ scm:git:git@github.com:bitsofproof/supernode.git scm:git:git@github.com:bitsofproof/supernode.git https://github.com/bitsofproof/supernode - 2.0.0 + HEAD diff --git a/server/pom.xml b/server/pom.xml index 039233d3..2ea9ec64 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -8,7 +8,7 @@ com.bitsofproof supernode - 2.0.0 + 2.0.1-SNAPSHOT ../ From 1e1a7ea540bc9cdf9c639f130da82dd092bbf293 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sat, 4 Jan 2014 22:00:49 +0100 Subject: [PATCH 281/299] ident --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 6f732ffc..8fd89fca 100644 --- a/README.md +++ b/README.md @@ -24,16 +24,21 @@ Make sure you have Maven3, JDK 7 (with JCE Unlimited Strength Policy Jurisdictio Let us assume the directory you would want to run the server is /home/bitsofproof/run. cp server/target/bitsofproof-server-version-shaded.jar /home/bitsofproof/run + cp server/src/main/resources/context/*.xml /home/bitsofproof/run + cp server/src/main/resources/log4j.properties /home/bitsofproof/run + mkdir /home/bitsofproof/run/signed-libs Download http://www.bouncycastle.org/download/bcprov-jdk15on-150.jar + cp bcprov-jdk15on-150.jar /home/bitsofproof/run/signed-libs/bcprov-jdk15on.jar Run --- cd /home/bitsofproof/run + java -server -Xmx4g -jar target/server/target/bitsofproof-server-version-shaded.jar testnet3 memdb The final two parameters of the above example command line identify configuration contexts you copied under *-profile.xml. You have to choose one of the networks by specifying either testnet3 or production or slave, and a database layer, that could be (examples): From 8dbea615e1dd1603db89d69cd1e6a69dd8bfca15 Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Sun, 5 Jan 2014 17:40:28 +0100 Subject: [PATCH 282/299] remove unused raw scan function, simplify address extract from tx output --- .../com/bitsofproof/supernode/api/BCSAPI.java | 19 ------------ .../supernode/api/JMSServerConnector.java | 2 -- .../supernode/api/TransactionOutput.java | 30 +------------------ .../supernode/common/BloomFilter.java | 14 ++++----- .../supernode/common/ScriptFormat.java | 26 ++++++++++++++++ .../supernode/core/BlockStore.java | 3 -- .../bitsofproof/supernode/model/LvlStore.java | 29 ------------------ 7 files changed, 33 insertions(+), 90 deletions(-) diff --git a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java index 800c4aad..d54e5be3 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java @@ -15,7 +15,6 @@ */ package com.bitsofproof.supernode.api; -import java.util.Collection; import java.util.Set; import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; @@ -138,15 +137,6 @@ public interface BCSAPI public void scanTransactionsForAddresses (Set
addresses, UpdateMode mode, long after, TransactionListener listener) throws BCSAPIException; - /** - * Scan transactions using generic binary match. - * - * @param match - * @param listener - * @throws BCSAPIException - */ - public void scanTransactions (Collection match, UpdateMode mode, long after, TransactionListener listener) throws BCSAPIException; - /** * Scan transactions for an account * @@ -166,15 +156,6 @@ public void scanTransactionsForAddresses (Set
addresses, UpdateMode mod */ public void scanUTXOForAddresses (Set
addresses, UpdateMode mode, long after, TransactionListener listener) throws BCSAPIException; - /** - * Scan unspent transactions using and address or outpoint in match. - * - * @param match - * @param listener - * @throws BCSAPIException - */ - public void scanUTXO (Collection match, UpdateMode mode, long after, TransactionListener listener) throws BCSAPIException; - /** * Scan unspent transactions for an account * diff --git a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java index 8c0c4848..6f9c3411 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/JMSServerConnector.java @@ -335,7 +335,6 @@ public void scanUTXOForAddresses (Set
addresses, UpdateMode mode, long scanUTXO (al, mode, after, listener); } - @Override public void scanUTXO (Collection match, UpdateMode mode, long after, TransactionListener listener) throws BCSAPIException { scanRequest (match, mode, after, listener, "utxoMatchRequest"); @@ -347,7 +346,6 @@ public void scanUTXO (ExtendedKey master, int firstIndex, int lookAhead, long af scanRequest (master, firstIndex, lookAhead, after, listener, "utxoAccountRequest"); } - @Override public void scanTransactions (Collection match, UpdateMode mode, long after, final TransactionListener listener) throws BCSAPIException { scanRequest (match, mode, after, listener, "matchRequest"); diff --git a/api/src/main/java/com/bitsofproof/supernode/api/TransactionOutput.java b/api/src/main/java/com/bitsofproof/supernode/api/TransactionOutput.java index b4948f56..e4a9d5d3 100644 --- a/api/src/main/java/com/bitsofproof/supernode/api/TransactionOutput.java +++ b/api/src/main/java/com/bitsofproof/supernode/api/TransactionOutput.java @@ -16,13 +16,9 @@ package com.bitsofproof.supernode.api; import java.io.Serializable; -import java.util.List; -import com.bitsofproof.supernode.api.Address.Type; import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.ScriptFormat; -import com.bitsofproof.supernode.common.ScriptFormat.Token; -import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.common.WireFormat; import com.google.protobuf.ByteString; @@ -106,31 +102,7 @@ public static TransactionOutput fromWire (WireFormat.Reader reader) public Address getOutputAddress () { - if ( ScriptFormat.isPayToAddress (script) ) - { - List tokens; - try - { - tokens = ScriptFormat.parse (script); - return new Address (Type.COMMON, tokens.get (2).data); - } - catch ( ValidationException e ) - { - } - } - else if ( ScriptFormat.isPayToScriptHash (script) ) - { - List tokens; - try - { - tokens = ScriptFormat.parse (script); - return new Address (Type.P2SH, tokens.get (1).data); - } - catch ( ValidationException e ) - { - } - } - return null; + return ScriptFormat.getAddress (script); } @Override diff --git a/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java b/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java index f56b19f0..c20d3508 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/BloomFilter.java @@ -66,11 +66,6 @@ public BloomFilter (byte[] data, long hashFunctions, long tweak, UpdateMode upda this.update = update; } - public void addAddress (String address, int addressFlag) throws ValidationException - { - add (Address.fromSatoshiStyle (address, addressFlag)); - } - public static byte[] serializedOutpoint (String hash, long ix) { WireFormat.Writer writer = new WireFormat.Writer (); @@ -79,9 +74,11 @@ public static byte[] serializedOutpoint (String hash, long ix) return writer.toByteArray (); } - public void addOutpoint (String hash, long ix) + public int addOutpoint (String hash, long ix) { - add (serializedOutpoint (hash, ix)); + byte[] point = serializedOutpoint (hash, ix); + add (point); + return Arrays.hashCode (point); } public boolean containsOutpoint (String hash, long ix) @@ -104,12 +101,13 @@ private boolean testBit (int n) return (filter[n >>> 3] & 1 << (7 & n)) != 0; } - public void add (byte[] data) + public int add (byte[] data) { for ( int i = 0; i < hashFunctions; ++i ) { setBit ((int) ((murmurhash3bit (i, data, tweak) & 0xFFFFFFFFL) % (filter.length * 8))); } + return Arrays.hashCode (data); } public boolean contains (byte[] data) diff --git a/api/src/main/java/com/bitsofproof/supernode/common/ScriptFormat.java b/api/src/main/java/com/bitsofproof/supernode/common/ScriptFormat.java index 67e06c0d..d34c777a 100644 --- a/api/src/main/java/com/bitsofproof/supernode/common/ScriptFormat.java +++ b/api/src/main/java/com/bitsofproof/supernode/common/ScriptFormat.java @@ -22,6 +22,9 @@ import java.util.List; import java.util.StringTokenizer; +import com.bitsofproof.supernode.api.Address; +import com.bitsofproof.supernode.api.Address.Type; + public class ScriptFormat { // unfortunatelly unused: https://bitcointalk.org/index.php?topic=120836.0 @@ -718,6 +721,29 @@ public static boolean isPayToAddress (byte[] script) } } + public static Address getAddress (byte[] script) + { + try + { + List parsed = parse (script); + if ( parsed.size () == 5 && parsed.get (0).op == ScriptFormat.Opcode.OP_DUP && parsed.get (1).op == ScriptFormat.Opcode.OP_HASH160 + && parsed.get (2).data != null && parsed.get (2).data.length == 20 && parsed.get (3).op == ScriptFormat.Opcode.OP_EQUALVERIFY + && parsed.get (4).op == ScriptFormat.Opcode.OP_CHECKSIG ) + { + return new Address (Type.COMMON, parsed.get (2).data); + } + if ( parsed.size () == 3 && parsed.get (0).op == ScriptFormat.Opcode.OP_HASH160 && (parsed.get (1).data != null && parsed.get (1).op.o <= 75) + && parsed.get (1).data.length == 20 && parsed.get (2).op == ScriptFormat.Opcode.OP_EQUAL ) + { + return new Address (Type.P2SH, parsed.get (1).data); + } + } + catch ( Exception e ) + { + } + return null; + } + public static boolean isMultiSig (byte[] script) { try diff --git a/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java index 8c5a74c2..cfa9a79c 100644 --- a/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java @@ -18,7 +18,6 @@ import java.util.List; import java.util.Set; -import com.bitsofproof.supernode.common.BloomFilter; import com.bitsofproof.supernode.common.BloomFilter.UpdateMode; import com.bitsofproof.supernode.common.ByteVector; import com.bitsofproof.supernode.common.ExtendedKey; @@ -74,8 +73,6 @@ public interface TransactionProcessor public List getInventory (List locator, String last, int limit); - public void scan (BloomFilter filter, TransactionProcessor processor) throws ValidationException; - public void filterTransactions (boolean utxo, Set match, UpdateMode update, long after, TransactionProcessor processor) throws ValidationException; diff --git a/server/src/main/java/com/bitsofproof/supernode/model/LvlStore.java b/server/src/main/java/com/bitsofproof/supernode/model/LvlStore.java index 1d48b347..c92d8515 100644 --- a/server/src/main/java/com/bitsofproof/supernode/model/LvlStore.java +++ b/server/src/main/java/com/bitsofproof/supernode/model/LvlStore.java @@ -27,7 +27,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.bitsofproof.supernode.common.BloomFilter; import com.bitsofproof.supernode.common.Hash; import com.bitsofproof.supernode.common.ValidationException; import com.bitsofproof.supernode.common.WireFormat; @@ -420,34 +419,6 @@ protected Blk retrieveBlockHeader (CachedBlock cached) throws ValidationExceptio return readBlk (cached.getHash (), false); } - @Override - public void scan (final BloomFilter filter, final TransactionProcessor processor) - { - store.forAll (KeyType.TX, new DataProcessor () - { - @Override - public boolean process (byte[] key, byte[] data) - { - Tx t; - try - { - t = Tx.fromLevelDB (data); - if ( t.passesFilter (filter) && isOnTrunk (t.getBlockHash ()) ) - { - processor.process (t); - } - } - catch ( ValidationException e ) - { - log.error ("Can not read transaction ", e); - return false; - } - return true; - } - }); - processor.process (null); - } - @Override public boolean isEmpty () { From 2e112221148d3cacd4974f52df25ff2d1d8059aa Mon Sep 17 00:00:00 2001 From: Tamas Blummer Date: Mon, 6 Jan 2014 22:01:33 +0100 Subject: [PATCH 283/299] added assembly --- README.md | 29 ++++------ assembly/bcprov-jdk15on.jar | Bin 0 -> 2732684 bytes assembly/distribution.xml | 51 ++++++++++++++++++ assembly/log4j.properties | 12 +++++ pom.xml | 10 ++++ server/pom.xml | 2 + .../com/bitsofproof/supernode/main/Main.java | 39 +++++++++----- server/src/main/resources/context/server.xml | 10 ---- server/src/main/resources/log4j.properties | 16 ++---- 9 files changed, 116 insertions(+), 53 deletions(-) create mode 100644 assembly/bcprov-jdk15on.jar create mode 100644 assembly/distribution.xml create mode 100644 assembly/log4j.properties diff --git a/README.md b/README.md index 8fd89fca..435680aa 100644 --- a/README.md +++ b/README.md @@ -19,36 +19,25 @@ Make sure you have Maven3, JDK 7 (with JCE Unlimited Strength Policy Jurisdictio cd supernode - mvn package - -Let us assume the directory you would want to run the server is /home/bitsofproof/run. - - cp server/target/bitsofproof-server-version-shaded.jar /home/bitsofproof/run - - cp server/src/main/resources/context/*.xml /home/bitsofproof/run - - cp server/src/main/resources/log4j.properties /home/bitsofproof/run - - mkdir /home/bitsofproof/run/signed-libs - -Download http://www.bouncycastle.org/download/bcprov-jdk15on-150.jar - - cp bcprov-jdk15on-150.jar /home/bitsofproof/run/signed-libs/bcprov-jdk15on.jar + mvn assembly:assembly + +The build will create a zip file in the target directory. Run --- - cd /home/bitsofproof/run - - java -server -Xmx4g -jar target/server/target/bitsofproof-server-version-shaded.jar testnet3 memdb + + copy and unzip the distribution zip created in the previous step and cd to it. + + java -Xmx4g -jar bitsofproof-server-version-shaded.jar testnet3 memdb The final two parameters of the above example command line identify configuration contexts you copied under *-profile.xml. You have to choose one of the networks by specifying either testnet3 or production or slave, and a database layer, that could be (examples): * memdb - in memory database for tests * leveldb - LevelDB -To use the API of your local server you need to run a message broker process providing the infrastructure. Since the message bus offers authentication and a wide selection of transports, your installation will likely be unique and need to be reflected in BCSAPI-profile.xml. You find example configurations for the message Active MQ there. The complete command line for a production environment might be: +To use the API of your local server you need to run a message broker process providing the infrastructure. Since the message bus offers authentication and a wide selection of transports, your installation will likely be unique and need to be reflected in BCSAPI-profile.xml. You find example configurations for the message Active MQ there. The complete command line for a production environment might be (assuming you run it as a slave behind a satoshi): - java -server -Xmx4g -jar target/server/target/bitsofproof-server-version-shaded.jar production leveldb BCSAPI activemq + java -server -Xmx4g -jar bitsofproof-server-version-shaded.jar slave leveldb BCSAPI activemq License ------- diff --git a/assembly/bcprov-jdk15on.jar b/assembly/bcprov-jdk15on.jar new file mode 100644 index 0000000000000000000000000000000000000000..d4b510d7894afa3775fdfb7ab2b3a704baf82c99 GIT binary patch literal 2732684 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

e^KT?j&OK#grL>A#KMIHDSB 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

4f9&e#$WTSdy!h|E{@KHm1%RsGlp(=IN@%RxMC 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

Fv=~d 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%0DuVQ&UIz>24tq<`(^2}~QN@>q7* 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 z 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 z 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Α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 z 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